Does anybody know if it is possible to insert a cross reference (I want to reference a bookmark, but I could make anything else work as well), using Aspose Words, in C#?
If you want to insert a footnote or endnote, you can use the following code.
DocumentBuilder builder = new DocumentBuilder(doc);
builder.Write("Some text is added.");
Footnote endNote = new Footnote(doc, FootnoteType.Endnote);
builder.CurrentParagraph.AppendChild(endNote);
endNote.Paragraphs.Add(new Paragraph(doc));
endNote.FirstParagraph.Runs.Add(new Run(doc, "Endnote text."));
doc.Save(MyDir + #"FootNote.docx");
If you want to insert a bookmark, you can use the following code.
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
builder.StartBookmark("MyBookmark");
builder.Writeln("Text inside a bookmark.");
builder.EndBookmark("MyBookmark")
If you want to update a bookmark, you can use the following code.
Document doc = new Document(MyDir + "Bookmark.doc");
// Use the indexer of the Bookmarks collection to obtain the desired bookmark.
Bookmark bookmark = doc.Range.Bookmarks["MyBookmark"];
// Get the name and text of the bookmark.
string name = bookmark.Name;
string text = bookmark.Text;
// Set the name and text of the bookmark.
bookmark.Name = "RenamedBookmark";
bookmark.Text = "This is a new bookmarked text.";
I work as developer evangelist at Aspose.
You can use the following code to get the page number of a bookmark.
Document doc = new Document("Bookmark.docx");
Aspose.Words.Layout.LayoutCollector layoutCollector = new Aspose.Words.Layout.LayoutCollector(doc);
// Use the indexer of the Bookmarks collection to obtain the desired bookmark.
Bookmark bookmark = doc.Range.Bookmarks["MyBookmark"];
// Get the name and text of the bookmark.
string name = bookmark.Name;
string text = bookmark.Text;
int pageNumber = layoutCollector.GetStartPageIndex(bookmark.BookmarkStart);
Console.Write("Bookmark name is {0}, it is placed on page number {1} and following is the text inside it: {2}", name, pageNumber, text);
Related
I am new to Google Apps Script and coding in general and wasn't entirely sure how to do this. I want to create code that allows me to create a new set of Google Slides based on a Slides template using the relevant rows from a Google Sheets document.
function generateNewSlides() {
var wsID = "would insert worksheet URL ID here";
var ws = SpreadsheetApp.openById(wsID).getSheetByName("Data");
var data = ws.getRange(2, 1, ws.getLastRow()-1, 5).getValues();
>the above should get the relevant table from the sheet
data.forEach(function(info){
if(info[0]){
var firstname = info[0];
var surname = info[1];
var email = info[2];
var phone = info[3];
var image = info[4];
var presName = info[5];
>the above are columns where the different pieces of data would be taken from for the placeholders in the Slides template
var slidesTemplateID = "would insert slides template URL ID here";
var slidesTemplate = SlidesApp.openById(slidesTemplateID);
var template = slidesTemplate.getSlides();
var folderID = "would insert desired folder ID for saving in here";
>the above should get me the Slides template
template.makeCopy(presName,DriveApp.getFolderById(folderID)); **>line where error occurred**
var newPresentation = DriveApp.getFilesByName(presName).next().getUrl();
var Presentation = SlidesApp.openByUrl(newPresentation);
>the above should create a copy and then open it
var shapes = (Presentation.getShapes());
shapes.forEach(function(shape){
shape.getText().replaceAllText('{{firstname}}',firstname);
shape.getText().replaceAllText('{{surname}}',surname);
shape.getText().replaceAllText('{{email}}',email);
shape.getText().replaceAllText('{{phone}}',phone);
shape.getText().replaceAllText('{{presname}}', presName)
});
>the above should replace all the placeholder tags in the template with the row data
}
});
}
Above is the code I have so far. The worksheet I am extracting data from has columns: first name, surname, email address, phone number, image (URL), and presentation name. When I try to run it I encounter an error on line 37 where it says template.makeCopy is not a function, however I am certain .makeCopy should be able to create a copy for it, no?
My main questions are:
1) What should I change to make it work, generating a new set slides for each row in the worksheet?
2) How can I add images to it replacing placeholder tags I've added in squares (not textboxes) in the template?
Thanks in advance!
Issue 1. makeCopy:
makeCopy(name, destination) is a method of the class File, which belongs to the Drive Service, not to the Slides Service. In your code, template is a list of Slides (you retrieve it by calling the method getSlides() from a Presentation). makeCopy cannot work here.
In order to make a copy of a Presentation, you should be using the Drive Service instead. You should replace these lines:
var slidesTemplate = SlidesApp.openById(slidesTemplateID);
var template = slidesTemplate.getSlides();
With this one:
var template = DriveApp.getFileById(slidesTemplateID);
Issue 2. Iterating through all shapes:
Next, you want to iterate through all shapes in your Presentation, and replace all placeholder tags with your desired text. In order to do that, you are using Presentation.getShapes(), which cannot work, since getShapes() is not a method of Presentation, but of Slide.
You should first iterate through all Slides in the Presentation, and for each Slide, iterate through all Shapes. You should replace these lines:
var shapes = (Presentation.getShapes());
shapes.forEach(function(shape){
// Replacing text lines
});
With these ones:
Presentation.getSlides().forEach(function(slide) {
slide.getShapes().forEach(function(shape) {
// Replacing text lines
})
});
Note:
In order to retrieve the copied presentation, you are currently doing this:
template.makeCopy(presName,DriveApp.getFolderById(folderID));
var newPresentation = DriveApp.getFilesByName(presName).next().getUrl();
var Presentation = SlidesApp.openByUrl(newPresentation);
There is no need to do this, you can just retrieve the ID of the created template, and open by ID, like this:
var copiedTemplate = template.makeCopy(presName,DriveApp.getFolderById(folderID));
var Presentation = SlidesApp.openById(copiedTemplate.getId());
Reference:
Slides Service
Drive Service
I've spent two days on this and I'm still not able to figure it out 8-)
I have a LibreOffice Writer document with some Placeholders (Insert -> Fields -> More Fields -> Functions -> Placeholder -> Image) and Input fields (Insert -> Fields -> More Fieds -> Functions -> Input field) and I need to retrieve the value of an Input field and use it to replace a specified Placeholder in the same document.
To be more precise. I have an Input field where I enter for example 123
and somewhere in the document is a button, which triggers a macro, and this macro should:
retrieve the current value of the specified (named?) Input field ("123"),
"replace" a specified (named?) Placeholder with an image loaded from http://domain.tld/image/123.png
Is this somehow possible? Would be great, because I'm trying to to insert externally generated barcodes into my document...
These are both "Text fields", and some information and macro examples are in Andrew Pitonyak's book OpenOffice Macros Explained (available as a free pdf download from http://www.pitonyak.org/oo.php). The wiki page also has some good background.
Form controls (from the toolbar "Form controls") are named, so they have an advantage when working with macros. Text fields, however - the kind you have in your document - are not named, so you have to cycle through all the fields in a document, or highlight a particular run of text and cycle through the field within the highlighted area to find the one you are after. The Pitonyak document has examples of both methods.
Assuming the document has only one input field, this StarBasic code will print its current value:
Sub DisplayFields
Dim oEnum As Object
Dim oField As Object
oEnum = ThisComponent.getTextFields().createEnumeration()
Do While oEnum.hasMoreElements()
oField = oEnum.nextElement()
If oField.getPresentation(True) = "Input field" Then
Print "Input field contents: " & oField.getPresentation(False)
Exit Do
End If
Loop
End Sub
As far as I can tell, there is no API to replace a placeholder with its designated content. There might be a way with the dispatcher - the list of dispatch commands tantalizingly includes "FieldDialog" - but I wasn't able to find any documentation or examples.
I think what you'd have to do is find the field, put your cursor there, insert the image, then delete the placeholder field. Some more StarBasic code (again, assuming there's only a single placeholder field in the document):
Sub InsertImage
Dim oEnum As Object
Dim oField As Object
Dim oAnchor As Object
Dim oText As Object
Dim oCursor As Object
Dim FileName As String
Dim FileURL As String
Dim objTextGraphicObject As Object
oEnum = ThisComponent.getTextFields().createEnumeration()
Do While oEnum.hasMoreElements()
oField = oEnum.nextElement()
If oField.getPresentation(True) = "Placeholder" Then
oAnchor = oField.Anchor
oText = oAnchor.getText()
oCursor = oText.createTextCursorByRange(oAnchor.getEnd)
FileName = "C:\after zoo.JPG"
FileURL = convertToURL(FileName)
objTextGraphicObject = ThisComponent.createInstance("com.sun.star.text.TextGraphicObject")
REM Optional to set the size
' Dim objSize as New com.sun.star.awt.Size
' objSize.Width = 3530
' objSize.Height = 1550
' objTextGraphicObject.setSize(objSize)
objTextGraphicObject.GraphicURL = FileURL
oText.insertTextContent(oCursor.Start, objTextGraphicObject, false)
oField.dispose()
Exit Do
End If
Loop
End Sub
I have a dynamically generated docx file.
Need write the text strictly to end of page.
With Microsoft.Interop i insert Paragraphs before text:
int kk = objDoc.ComputeStatistics(WdStatistic.wdStatisticPages, ref wMissing);
while (objDoc.ComputeStatistics(WdStatistic.wdStatisticPages, ref wMissing) != kk + 1)
{
objWord.Selection.TypeParagraph();
}
objWord.Selection.TypeBackspace();
But i can't use same code with Open XML, because pages.count calculated only by word.
Using interop impossible, because it so slowwwww.
There are 2 options of doing this in Open XML.
create Content Place holder from Microsoft Office Developer Tab at the end of your document and now you can access this Content Place Holder programatically and can place any text in it.
you can append text driectly to your word document where it will be inserted at the end of your text. In this approach you got to write all the stuff to your document first and once you are done than you can append your document the following way
//
public void WriteTextToWordDocument()
{
using(WordprocessingDocument doc = WordprocessingDocument.Open(documentPath, true))
{
MainDocumentPart mainPart = doc.MainDocumentPart;
Body body = mainPart.Document.Body;
Paragraph paragraph = new Paragraph();
Run run = new Run();
Text myText = new Text("Append this text at the end of the word document");
run.Append(myText);
paragraph.Append(run);
body.Append(paragraph);
// dont forget to save and close your document as in the following two lines
mainPart.Document.Save();
doc.Close();
}
}
I haven't tested the above code but hope it will give you an idea of dealing with word document in OpenXML.
Regards,
I have multiple tables in a word template and need to change each of it in an other way. Also it is possible that one or the other will be deleted or inserted so I can't say I take the 5th and that's always the same one.
The identification has to be saved so I can't use the .ID value.
What way is there to identify a specific table with VSTO? Preferable one which can also be set in the document without VSTO.
I found a way to do it:
Mark the table in word and add a bookmark to it. You have to choose a unique name so you can also identify the table. The identification can be done with a method like the following:
public Word.Bookmark GetBookmark(String bookmarkName)
{
// Find bookmark
Word.Bookmark bookmark = null;
foreach (Word.Bookmark curBookmark in Globals.ThisDocument.Bookmarks)
{
if (curBookmark.Name.Equals(bookmarkName))
{
bookmark = curBookmark;
break;
}
}
return bookmark;
}
I ran into similar problem. To solve the problem I set title of the table in word tamplate(Right click table->Table Properties...->Alt text->Title) and search all the table in word document for the title. Below is the code I used to search the table.
public static Table getTable(Document doc, String title){
int totalTables = doc.Tables.Count;
Microsoft.Office.Interop.Word.Table ret = null;
for (int i = 1; i <= totalTables; i++){
if (title.Equals(doc.Tables[i].Title, StringComparison.OrdinalIgnoreCase)){
ret = doc.Tables[i];
break;
}
}
return ret;
}
Is it possible to use alternative text for the tables as identifier?
Use the below code to retrieve them (code copied from here)
Word.Application wdApp = Application;
Word.Document wdDoc = wdApp.ActiveDocument;
Word.Table wdTable = wdDoc.Tables[1];
MessageBox.Show(wdTable.Title + "\n" + wdTable.Descr);
I used the code in the link mentioned below to merge word files into a single file
http://devpinoy.org/blogs/keithrull/archive/2007/06/09/updated-how-to-merge-multiple-microsoft-word-documents.aspx
However, seeing the output file i realized that it was unable to copy header image in the first document. How do we merge documents preserving format and content.
I will suggest to use GroupDocs.Merger Cloud for merging multiple word document to a single word document, it keeps the formatting and contents of the source documents. It is a platform independent REST API solution without depending on any third-party tool or software.
Sample C# code:
var configuration = new GroupDocs.Merger.Cloud.Sdk.Client.Configuration(MyAppSid, MyAppKey);
var apiInstance_Document = new GroupDocs.Merger.Cloud.Sdk.Api.DocumentApi(configuration);
var apiInstance_File = new GroupDocs.Merger.Cloud.Sdk.Api.FileApi(configuration);
var pathToSourceFiles = #"C:/Temp/input/";
var remoteFolder = "Temp/";
var joinItem_list = new List<JoinItem>();
try
{
DirectoryInfo dir = new DirectoryInfo(pathToSourceFiles);
System.IO.FileInfo[] files = dir.GetFiles();
foreach (System.IO.FileInfo file in files)
{
var request_upload = new GroupDocs.Merger.Cloud.Sdk.Model.Requests.UploadFileRequest(remoteFolder + file.Name, File.Open(file.FullName, FileMode.Open));
var response_upload = apiInstance_File.UploadFile(request_upload);
var item = new JoinItem
{
FileInfo = new GroupDocs.Merger.Cloud.Sdk.Model.FileInfo
{ FilePath = remoteFolder + file.Name }
};
joinItem_list.Add(item);
}
var options = new JoinOptions
{
JoinItems = joinItem_list,
OutputPath = remoteFolder + "Merged_Document.docx"
};
var request = new JoinRequest(options);
var response = apiInstance_Document.Join(request);
Console.WriteLine("Output file path: " + response.Path);
}
catch (Exception e)
{
Console.WriteLine("Exception while Merging Documents: " + e.Message);
}
That code is inserting a page break after each file.
Since sections control headers, if a second or subsequent document has a header, you'll probably be wanting to keep the original section properties, and insert those after your first document.
If you look at your original document as a docx, you'll probably see that your section is a document level section properties element.
The easiest way around your problem may be to create a second section properties element inside the last paragraph (which contains the header information). Then this should just stay there when the documents are merged (ie other paragraphs added after it).
That's the theory. See also http://www.pcreview.co.uk/forums/thread-898133.php
But I haven't tried it; it assumes InsertFile behaves as I expect it should.