How to create/enable bookmarks when converting word document to PDF using office js - ms-word

I used getFileAsync() method to create pdf of the word document:
Office.context.document.getFileAsync(Office.FileType.Pdf,
function(result) {
if (result.status == "succeeded") {
var myFile = result.value;
var sliceCount = myFile.sliceCount;
app.showNotification("File size:" + myFile.size + " #Slices: " + sliceCount);
// Now, you can call getSliceAsync to download the files, as described in the previous code segment (compressed format).
myFile.closeAsync();
}
else {
app.showNotification("Error:", result.error.message);
}
}
);
For more details: https://dev.office.com/reference/add-ins/shared/document.getfileasync
But once the pdf gets created, it doesn't show up the bookmarks. (Need to enable it like we can in ms word when we convert word to pdf through it's options setting)
Do we have any way in office js methods for enabling bookmarks of word document for pdf ?

Related

Word found unreadable content in xxx.docx after split a docx using openxml

I have a full.docx which includes two math questions, the docx embeds some pictures and MathType equation (oleobject), I split the doc according to this, get two files (first.docx, second.docx) , first.docx works fine, the second.docx, however, pops up a warning dialog when I try to open it:
"Word found unreadable content in second.docx. Do you want to recover the contents of this document? If you trust the source of this document, click Yes."
After click "Yes", the doc can be opened, the content is also correct, I want to know what is wrong with the second.docx? I have checked it with "Open xml sdk 2.5 productivity tool", but found no reason. Very appreciated for any help. Thanks.
The three files have been uploaded to here.
Show some code:
byte[] templateBytes = System.IO.File.ReadAllBytes(TEMPLATE_YANG_FILE);
using (MemoryStream templateStream = new MemoryStream())
{
templateStream.Write(templateBytes, 0, (int)templateBytes.Length);
string guidStr = Guid.NewGuid().ToString();
using (WordprocessingDocument document = WordprocessingDocument.Open(templateStream, true))
{
document.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
MainDocumentPart mainPart = document.MainDocumentPart;
mainPart.Document = new Document();
Body bd = new Body();
foreach (DocumentFormat.OpenXml.Wordprocessing.Paragraph clonedParagrph in lst)
{
bd.AppendChild<DocumentFormat.OpenXml.Wordprocessing.Paragraph>(clonedParagrph);
clonedParagrph.Descendants<Blip>().ToList().ForEach(blip =>
{
var newRelation = document.CopyImage(blip.Embed, this.wordDocument);
blip.Embed = newRelation;
});
clonedParagrph.Descendants<DocumentFormat.OpenXml.Vml.ImageData>().ToList().ForEach(imageData =>
{
var newRelation = document.CopyImage(imageData.RelationshipId, this.wordDocument);
imageData.RelationshipId = newRelation;
});
}
mainPart.Document.Body = bd;
mainPart.Document.Save();
}
string subDocFile = System.IO.Path.Combine(this.outDir, guidStr + ".docx");
this.subWordFileLst.Add(subDocFile);
File.WriteAllBytes(subDocFile, templateStream.ToArray());
}
the lst contains Paragraph cloned from original docx using:
(DocumentFormat.OpenXml.Wordprocessing.Paragraph)p.Clone();
Using productivity tool, found oleobjectx.bin not copied, so I add below code after copy Blip and ImageData:
clonedParagrph.Descendants<OleObject>().ToList().ForEach(ole =>
{
var newRelation = document.CopyOleObject(ole.Id, this.wordDocument);
ole.Id = newRelation;
});
Solved the issue.

Blueimp jquery file upload alternative file names cause the preview missing

The client can preview before uploading but cannot see after uploding as the local language file name.
So I want to rename the filename before upload the image for it can preview before and after uploading.
reference the https://github.com/blueimp/jQuery-File-Upload/wiki/API
How to provide alternative file names:
The name property of File objects is read only, but an alternative name can be provided as uploadName property for each individual file:
$('#fileupload').fileupload({
add: function (e, data) {
var count = data.files.length;
var i;
for (i = 0; i < count; i++) {
data.files[i].uploadName =
Math.floor(Math.random() * 1000) + '_' + data.files[i].name;
}
data.submit();
}
});
When I use above js function the preview is good function when the image add and submit to server immediately~
But when I delete
data.submit();
as I want client to decide if upload or not before uploading.
the preview image function is missing!
and cannot upload any file.
Does any code can rename for the unicode filename (as the local language encoing filename cannot show on the preview function when it uploaded) before submit to the server?
jquery.fileupload.js
....
} else {
$.each(options.files, function (index, file) {
// This check allows the tests to run with
// dummy objects:
if (that._isInstanceOf('File', file) ||
that._isInstanceOf('Blob', file)) {
formData.append(
($.type(options.paramName) === 'array' &&
options.paramName[index]) || paramName,
file,
file.uploadName || file.name
);
}
});
}
}
change the last
file.uploadName || file.name
to
encodeURI(file.uploadName || file.name)
then can see the write decode in the back
fileName = URLDecoder.decode(fileDetail.getFileName(), "UTF-8");

Insert HTML in docx file

I have made an application that fills wordfiles with customxmlparts now I am trying to put text into a textfield, but it has HTML in it and I want it to show the styling of it. I tried converting it to rich text format but that just gets pasted in the word file. Here is an example of the code:
var taskId = Guid.NewGuid();
var tempFilePath = $"{Path.GetTempPath()}/{taskId}";
using (var templateStream = new FileStream($"{tempFilePath}.docx", FileMode.CreateNew))
{
templateStream.Write(template, 0, template.Length);
// 1. Fill template.
using (WordprocessingDocument doc = WordprocessingDocument.Open(templateStream, true))
{
MainDocumentPart mainDocument = doc.MainDocumentPart;
if (mainDocument.CustomXmlParts != null)
{
mainDocument.DeleteParts<CustomXmlPart>(mainDocument.CustomXmlParts);
}
CustomXmlPart cxp = mainDocument.AddCustomXmlPart(CustomXmlPartType.CustomXml);
foreach (var line in data.Lines)
{
if (line.MoreInfo != null && line.MoreInfo != " ") {
}
}
var xmlData = ObjectToXml(data);
using (var stream = GenerateStreamFromString(tempFilePath, xmlData))
{
cxp.FeedData(stream);
}
mainDocument.Document.Save();
}
}
You can't just write the HTML formatted text into a DOCX field, you would need to convert it into a WordprocessingML format.
However, there is another way that you could try and that is to insert an "AltChunk" element. That element represents a sort of like a placeholder which can reference a HTML file and then when the DOCX file is opened in MS Word, it will make that HTML to WordprocessingML conversion for you. For details see: How to Use altChunk for Document Assembly
Alternatively you could use some third party, like GemBox.Document, which can make that HTML to WordprocessingML conversion for you.
For example check this Set Content example:
// Set content using HTML tags
document.Sections[0].Blocks[4].Content.LoadText(
"Paragraph 5 <b>(part of this paragraph is bold)</b>", LoadOptions.HtmlDefault);

Using Word.SearchOptions in an Office Add-In

I am developing an Office Add-In that processes the text of each paragraph of a Word document against data in JSON format and writes the result to a within the index.html file that is rendered in the Task Pane. This works fine. I am now trying to format the strings within the Word document that correspond to the hits for the keys in the JSON data.
I have a JS block in the head of the index.html file in which I call "Office.initialize" and define a variable based on the JSON data, and have utility functions related to the above functionality. After that comes a function in which I get the Word context and process the Word file paragraphs against the JSON data, and then try to search the Word paragraph itself in order to format the hits. In this last task I am trying to reproduce a snippet from Michael Mainer 1. But no formatting happens when I activate this function. Unfortunately I don't have access to a console since I am on a Mac, which makes it harder to debug.
I would be very appreciative of someone showing me where I'm going wrong.
`function tester() {
Word.run(function (context) {
// Create a proxy object for the document's paragraphs
var paragraphs = context.document.body.paragraphs;
// Load the paragraphs' text, which I run regexes on
context.load(paragraphs, 'text');
return context.sync().then(function () {
for (var i = 0; i < paragraphs.items.length; i++) {
var text = paragraphs.items[i].text;
// jquery to iterate over the "notes" objects from the JSON
$.each(notes, function(key, value) {
var regex = new RegExp("\\b" + key + "\\b", "g");
var res = regex.test(text);
// if the regex hits...
if (res == true) {
// This part works fine, using the JSON data to append to the <DIV> with ID = "notes"
document.getElementById('notes').innerHTML += "<button onclick=hide('" + value.seqNo + "')><b>" + key + "</b></button><p class='" + value.seqNo + "' id='" + i + "'>" + value.notes[0].note + "</p>";
// I now go on to searching for these hits within the current paragraph in the Word file
var thisPara = paragraphs.items[i];
// Set up the search options.
var options = Word.SearchOptions.newObject(context);
options.matchCase = false
// Queue the commmand to search the current paragraph for occurrences of the string "key" (coming from the JSON data)
var searchResults = paragraphs.items[i].search(key, options);
// Load 'text' and 'font' for searchResults.
context.load(searchResults, 'text, font');
// Synchronize the document state by executing the queued-up commands, and return a promise to indicate task completion.
return context.sync().then(function () {
// Queue a command to change the font for each found item.
for (var j = 0; j < searchResults.items.length; j++) {
searchResults.items[j].font.color = '#FF0000'
searchResults.items[j].font.highlightColor = '#FFFF00';
searchResults.items[j].font.bold = true;
}
// Synchronize the document state by executing the queued-up commands,
// and return a promise to indicate task completion.
return context.sync();
});
}
});
}
});
})
.catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
}
`
It looks like you just need access to the font property, have you tried just:
context.load(searchResults, 'font');
That was working for me?

Generate and download file from Word dialog

Is there a way to generate a file with some JSON content and prompt the user with a saveAs dialog.
This is from an open dialog in word.
The object could be like (will be quite a lot bigger in practice)
var obj = {a: 1, b: 2, c: 'qwerty'}
I tried to uri encode and using window.open without any luck.
content = JSON.stringify(obj);
uriContent = "data:application/octet-stream," + encodeURIComponent(content);
newWindow = window.open(uriContent, 'filename');
I did this for XML, but it requires some back-end and front-end work. On the backend, you need some ASPX like this:
string filename = "export.xml";
byte[] data = Convert.FromBase64String(Request.QueryString[0].Replace("\"", ""));
string decodedString = System.Text.Encoding.UTF8.GetString(data);
// set the http content type to "APPLICATION/OCTET-STREAM
Response.ContentType = "APPLICATION/OCTET-STREAM";
System.String disHeader = "Attachment; Filename=\"" + filename + "\"";
Response.AppendHeader("Content-Disposition", disHeader);
Response.Flush();
Response.Write(decodedString);
I called mine "download.aspx." Then on the front-end, you have to use AJAX. This created a form region on the page and the forces a submit of the form to start the download:
// Helper function to load a form and then send the post results to a
// new window so that we can get the download button
function ajax_download(url, data, input_name) {
try {
$('#form-div').append("<form method='GET' action='" +
url + "' target='_blank'>" +
"<input type=hidden name='" + input_name + "' value='" +
JSON.stringify(data) + "'/></form>");
$('#form-div').find('form').submit();
} catch (err) {
showNotification("error", err.description);
}
}
To call it, you simply make this this call from your JavaScript code where you want it:
xml = "<xml><data>12345</data></xml>";
ajax_download('./Download.aspx', btoa(xml), 'xml');
In this case, mine is targeting XML and always creates a file called "export.xml", but you can adjust it as needed.