Append Pub file to other MS Publisher 2010 file with PowerShell - powershell

In MS Publisher 2010, you can add a PUB file (a catalog publication which merge a template with data coming from Excel file) to the end of an existing pub file.
Quite convenient to compose a Catalog book. I can do it by hand with Publisher user interface and I would like to automate this task (there is 26 chapters).
Unfortunately, I was not able to locate the automation function, neither a simple example.
Did someone could help me with some automation sample for this task?
Thanks,

Can you access the Publisher interop library with PowerShell?
This is how I'd go about it if so (untested C#, but it should give you an idea of how to go about it if you read up on it in MSDN's Publisher VBA reference):
Publisher.Application firstPubApp = new Publisher.Application(); //open a new publisher instance
Publisher.Document sourcePublication = firstPubApp.Open("sourcefile.pub"); //open your publisher document
Publisher.Application otherPubApp = new Publisher.Application();
Publisher.Document targetPublication = otherPubApp.Open("targetfile.pub");
targetPublication.Pages.Add(1, 1); //add one page after page 1
foreach (Publisher.Shape shape in sourcePublication.Pages[1].Shapes) //loop through all pages on page 1
{
shape.Copy(); //copy the shape
otherPubApp.ActiveDocument.Pages[2].Shapes.Paste(); //paste it in the other document
}
It is quite possible that a better way to do it than looping through all shapes on all pages is hidden somewhere in that documentation though. It's always hard to find samples for Publisher compared to Excel or Word.

Related

JSReport -- working with docxChild. I can't resolve the error : "docxChild helper requires asset parameter to be set"

I'm trying many functionality on jsReport and word and got stuck on this one.
I want to create an asset usable in different template, assuming that my asset got the right data.
Using this documentation and the UI jsReport Studio I create a working template called test-word, I upload a docx test_jsReport.docx with some ahnddlebar in it, create some data and it work.
Then I create an asset, in the same folder, with a second docx test_jsReport2.docx ; put some code and try to generate it alone with the same data, it worked again.
At this point my project look like this :
test project
Then, using this chapter I add in my docx test_jsReport.docx the following code : {{docxChild "test_jsReport2.docx"}}.
So I can no longer run my report, the error is the following.
Report "test-word" render failed.
Error while executing docx recipe
(because) error when evaluating engine handlebars for template anonymous
(because) "docxChild" helper call failed
(because) docxChild helper requires asset parameter to be set
(system-helpers.js line 706:11)
704 |
705 | if (assetNamePathOrObject == null) {
> 706 | throw new Error('docxChild helper requires asset parameter to be set')
| ^
707 | }
708 |
709 | const docxChildInfo = {}
What is that aset parameter that I need to set ? And how to do it ?
I'm pretty sure that I should omehow declare some kind of link beween my two reports, but I can't find how to do that.
First I tried to use the asset property in jsReport studio on my report, like this :
first try
But it doesn't worked and I never really believe it, I think it's an option to define a link to the physical docx I modify, so I don't have to upload after each save.
Second I read and try to mimic some other systems I can find in the doc like this and that and but it's for HTML report not word.
Lastly I tried to reference my report using a script like this :
const jsreport = require('jsreport-proxy')
// Use the "beforeRender" or "afterRender" hook
// to manipulate and control the report generation
async function beforeRender (req, res) {
await jsreport.assets.registerHelpers('test_jsReport2.js')
}
It doesn't work either.
I'm a little lost here, what is the right way to use docxChild ?
Here is a link to an example of what I do : https://playground.jsreport.net/w/anon/Pm_vIEwF
Here is how I setup the example :
I prepared my two word
Right click > new entity > template
Select handdlebars and docx > next > next
Click on my template > in properties, in docx I upload my main.docx
Right click > new entity > asset > upload, i put my subreport.docx
The docxChild feature works by searching a docx asset and take the content of such docx and insert it into the docx it was called from. You have follow the right steps, but the only issue is that you are using different characters for the quotes in your docxChild helper call. It was perhaps not your intention, for example on macos i noticed that whenever i try to use " it replace it for the characters present in your docx, so maybe if you are using macos this is the problem you are having.
Your original template
What should look like to have it working
I've prepared a playground example with your same code but fixing the docxChild call to use the right quote characters "",

Can you get access to a pages front matter (or other data) in a eleventy (11ty) plugin

I'm creating (would like to create) an eleventy (11ty) plugin that can automatically generate Open Graph images based on a pages data. So in the template:
---
generate_og_image: true
image_text: "text which will be baked into the image"
og_image_filename: some_file_name.jpg
---
#some markdown
...
I can process each file in my .eleventy.js file via plugin using:
module.exports = function (eleventyConfig) {
eleventyConfig.addLinter("og-image-generator", function(content, inputPath, outputPath) {
title = HOW_TO_ACCESS_TEMPLATE_FRONT_MATTER
createImage(title)
});
}
But only have access to the content, inputPath and outputPath of the template.
How can I access the front matter data associated with the Template? Or is there a better way to go about this?
Answering my own question. As #moritzlost mentioned it is not possible directly. I found this workaround.
eleventyComputed allows you to dynamically assign values to keys. It also allows you to call a custom shortcode.
You can pass whatever properties you like from the template into the shortcode. In this case ogImageName the image name, ogImageTemplate a template or background image and text which is the text to be written on that background.
You can even pass in other keys from your front matter and process them as you go.
---
layout: _main.njk
title: "Some title here"
eleventyComputed:
ogImageName: "{% ogCreateImage { ogImageName: title | slug, ogImageTemplate: 'page-blank.png', text: title } %}"
---
Then in .eleventy.js or a plugin:
eleventyConfig.addShortcode("ogCreateImage", function(props) {
const imageName = props.ogImageName
const imageTemplate = props.ogImageTemplate
const imageText = props.text
console.log('-----------------ogCreateImage-----------------');
console.log(`filename: ${imageName}`);
console.log(`using template: ${imageTemplate}`);
console.log(`with the text : ${imageText}`);
// call the image creation code — return filename with extension
const imageNameWithExtension = createOGImage(imageName, imageTemplate, imageText)
return imageNameWithExtension
});
Returning the final filename which you can use in your template.
I've also come across this problem. I don't think what you're trying to do is possible at the moment. There are not many ways for a plugin to hook into the build step directly:
Transforms
Linters
Events
I think events would be the best solution. However, events also don't receive enough information to process a template in a structured way. I've opened an issue regarding this on Github. For your use-case, you'd need to get structured page data in this hook as well. Or eleventy would need to provide a build hook for each page. I suggest opening a new feature-request issue or adding a comment with your use-case to my issue above so those hooks can be implemented.
Other solutions
Another solution that requires a bit more setup for the users of your plugin would be to add your functionality as a filter instead of an automatic script that's applied to every template. This means that the users of your plugin would need to add their own template which passes the relevant data to your filter. Of course this also gives more fine-control to the user, which may be beneficial.
I use a similar approach for my site processwire.dev:
A special template loops over all posts and generates an HTML file which is used as a template for the generated preview images. This template is processed by eleventy. (source)
After the build step: I start a local server in the directory with the generated HTML files, open them using puppeteer and programmatically take a screenshot which is saved alongside the HTML templates. The HTML templates are then deleted.
This is integrated into the build step with a custom script that is executed after the eleventy build.
I've published the script used to take screenshots with Puppeteer as an NPM package (generate-preview-images), though it's very much still in alpha. But you can check the source code on Github to see how it works, maybe it helps with your plugin.

VSTO Load On Demand

I have a Word 2016 VSTO that I developed using VS2015 and deployed to a Windows 10 machine using InstallShield. If I set the LoadBehavior to 3 it loads and works fine.
What I need is for it load on demand. I've tried just setting LoadBehavior to 9 but no dice. The web application that is creating the Word document (that ultimately loads the VSTO) was originally written for Word 2010 and is embedding the VSTO path in the Word document with an added GUID (C:\myAddIn.vsto|GUID|vstolocal). Opening the document created by the web application in Word 2016 throws this error when the VSTO tries to load:
Microsoft.VisualStudio.Tools.Applications.Runtime.CannotCreateStartupObjectException:
Could not create an instance of startup object myAddin.ThisAddIn
Any tutorials I've seen for VS2013/2015 do not reference embedding a GUID in the VSTO path; just set the LoadBehavior to 9.
There is no Publisher listed in File -> Options ->AddIns. The VSTO is listed under Inactive AddIns which I would expect until it is loaded on demand.
Any help is greatly appreciated.
If you want code to only execute with certain documents, such as you describe (from Comments)
I only want the AddIn to load for Word documents created by the web
application. Opening Word from the Start menu should not load the
AddIn.
then it is probably better to use a document-level customization, rather than an add-in.
The code attached to a document-level customization will load with the document, and unload when the document is closed. It's possible to create a document-level customization and distribute the document, or to attach the code at a later time using VSTO's ServerDocument class.
Since in your case the documents are being generated by a web app, using ServerDocument is indicated.
Here's the main content of the MSDN article:
=============================================
To attach managed code extensions to a document
In a project that does not require Microsoft Office, such as a console application or Windows Forms project, add a reference to the
Microsoft.VisualStudio.Tools.Applications.ServerDocument.dll and
Microsoft.VisualStudio.Tools.Applications.Runtime.dll assemblies.
Add the following Imports or using statements to the top of your code file.
using Microsoft.VisualStudio.Tools.Applications;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
Call the static AddCustomization method.
The following code example uses the AddCustomization overload. This overload takes the full path of the document and a Uri that specifies the location of the deployment manifest for the customization you want to attach to the document. This example assumes that a Word document named WordDocument1.docx is on the desktop, and that the deployment manifest is located in a folder that is named Publish that is also on the desktop.
string documentPath = System.Environment.GetFolderPath(
Environment.SpecialFolder.Desktop) + #"\WordDocument1.docx";
int runtimeVersion = 0;
try
{
runtimeVersion = ServerDocument.GetCustomizationVersion(documentPath);
// Make sure that this document does not yet have any Visual Studio Tools
// for Office customizations.
if (runtimeVersion == 0)
{
string deployManifestPath = System.Environment.GetFolderPath(
Environment.SpecialFolder.Desktop) + #"\Publish\WordDocument1.vsto";
Uri deploymentManifestUri = new Uri(deployManifestPath);
ServerDocument.AddCustomization(documentPath, deploymentManifestUri);
System.Windows.Forms.MessageBox.Show("The document was successfully customized.");
}
else
{
System.Windows.Forms.MessageBox.Show("The document is already customized.");
}
}
catch (FileNotFoundException)
{
System.Windows.Forms.MessageBox.Show("The specified document does not exist.");
}
catch (DocumentNotCustomizedException ex)
{
System.Windows.Forms.MessageBox.Show("The document could not be customized.\n" +
ex.Message);
}
Build the project and run the application on the computer where you want to attach the customization. The computer must have the Visual Studio 2010 Tools for Office Runtime installed.
Did you try to set LoadBehaviour to 0x10 => Load first time, then load on demand? This should make Office load your addin the first time you execute it and internally cache it. It will then change the value to 0x9... The subsequent times, the app will load on demand.

Novacode LineChart type

I have a code that implements a Novacode.LineChart. And the LineChart type which is shown by default is this one:
But I dont want this type of chart, I want it without points, like this:
This is the code where I create the chart:
LineChart c = new LineChart();
c.AddLegend(ChartLegendPosition.Bottom, false);
c.Grouping = Grouping.Stacked;
Anyone knows how can I hide thoose points and show only the lines? Thanks to everyone!!
Your question is shown up while I was searching for the exact same feature. It's probably a bit late but I hope it would be useful for other people in need of this feature.
My so called answer is not more than a few lines of dirty and unmanageable hack so unless you are not in dire need, I do not recommend to follow this way.
I also do not know if is it an approved approach here but I prefer to write the solution step by step so it may help you to grasp the concept and use better methods.
After I have realized that I was unable to use DocX to create a line chart without markers, using currently provided API, I wanted to know what were the differences between actual and desired output. So I saved a copy of .docx file with line chart after I manually edited the chart to expected result.
Before and after the edit
As you may already know, a .docx is a container format and essentially comprised of a few different folders and files. You can open it up with a .zip archive extractor. I used 7-Zip for this task and found chart file at location of /word/charts/chart1.xml but this may differ depending on the file, but you can easily figure it out.
Compared both of chart1.xml files and the difference was, the file without the markers had and extra XML tag with an additional attribute;
<c:marker>
<c:symbol val="none" />
</c:marker>
I had to somehow add this segment of code to chart. I added these up to example code provided by DocX. You can follow up from: DocX/ChartSample.cs at master
This is where the fun begins. Easy part first.
using System.Xml;
using System.Xml.Linq;
using Xceed.Words.NET;
// Create a line chart.
var line_chart = new LineChart();
// Create the data.
var PlaceholderData = ChartData.GenerateRandomDataForLinechart();
// Create and add series
var Series_1 = new Series("Your random chart with placeholder data");
Series_1.Bind(PlaceholderData, "X-Axis", "Y-Axis");
line_chart.AddSeries(Series_1);
// Create a new XmlDocument object and clone the actual chart XML
XmlDocument XMLWithNewTags = new XmlDocument();
XMLWithNewTags.LoadXml(line_chart.Xml.ToString());
I've used XPath Visualizer Tool to determine the XPath query, which is important to know because you can't just add the marker tag to somewhere and expect it to work. Why do I tell this? Because I appended marker tag on a random line and expected it to work. Naive.
// Set a namespace manager with the proper XPath location and alias
XmlNamespaceManager NSMngr = new XmlNamespaceManager(XMLWithNewTags.NameTable);
string XPathQuery = "/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser";
string xmlns = "http://schemas.openxmlformats.org/drawingml/2006/chart";
NSMngr.AddNamespace("c", xmlns);
XmlNode NewNode = XMLWithNewTags.SelectSingleNode(XPathQuery, NSMngr);
Now create necessary tags on newly created XML Document object with specified namespace
XmlElement Symbol = XMLWithNewTags.CreateElement("c", "symbol", xmlns);
Symbol.SetAttribute("val", "none");
XmlElement Marker = XMLWithNewTags.CreateElement("c", "marker", xmlns);
Marker.AppendChild(Symbol);
NewNode.AppendChild(Marker);
And we should copy the contents of latest changes to actual XML object. But oops, understandably it is defined as private so it is a read-only object. This is where I thought like "Okay, I've fiddled enough with this. I better find another library" but then decided to go on because reasons.
Downloaded DocX repo, changed this line to
get; set;
recompiled, copied Xceed.Words.NET.dll to both projectfolder/packages and projectfolder/projectname/bin/Debug folder and finally last a few lines were
// Copy the contents of latest changes to actual XML object
line_chart.Xml = XDocument.Parse(XMLWithNewTags.InnerXml);
// Insert chart into document
document.InsertChart(line_chart);
// Save this document to disk.
document.Save();
Is it worth it? I'm not sure but I have learned a few things while working on it. There're probably lots of bad programming practises in this answer so please tell me if you see one. Sorry for meh English.

how to stop macros running when opening a Word document using OLE Interop?

As the title suggests, I have a .Net application which uses interop to open documents in Word. I have set
app.AutomationSecurity = Microsoft.Office.Core.MsoAutomationSecurity.msoAutomationSecurityForceDisable
before opening the document. According to the documentation, thhis "Disables all macros in all files opened programmatically, without showing any security alerts"
However, when I attempt to open one specific document I get a dialog box on the screen that says "could not load an object because it is not available on this machine". It's a customer document but I believe it contains a macro with references to a COM object which I don't have installed.
Am I doing something stupid? is there any way to actually disable macros when opening a Word document?
Try:
WordBasic.DisableAutoMacros 1
Bizarrely, this relies on a throwback to pre-VBA days, but still seems to be the most-reliable way to ensure that no auto macros are triggered (in any document - you may want to turn it back using the parameter "0").
I recently had a project where I had to process 6,000 Word templates (yes, templates, not documents) many of which had oddball stuff like macros, etc. I was able to process all but 6 using this technique. (I never did figure out what the problem was with those 6).
EDIT: for a discussion of how to call this from C#, see: http://www.dotnet247.com/247reference/msgs/56/281785.aspx
For c# you can use
(_wordApp.WordBasic as dynamic).DisableAutoMacros();
The whole code I'm using is:
using Word = Microsoft.Office.Interop.Word;
private Word.Application _wordApp;
...
_wordApp = new Word.Application
{
Visible = false,
ScreenUpdating = false,
DisplayAlerts = Word.WdAlertLevel.wdAlertsNone,
FileValidation = MsoFileValidationMode.msoFileValidationSkip
};
_wordApp.Application.AutomationSecurity = MsoAutomationSecurity.msoAutomationSecurityForceDisable;
(_wordApp.WordBasic as dynamic).DisableAutoMacros();