Setting MS Word Paper Size to PostScript Custom Page Size programmatically - vba

The Scene
What I am trying to do is programmatically convert an MS word (.docx) file to a PostScript file (.ps). I am doing this by creating a PostScript printer with one of the default PostScript printer drivers bundled with MS Windows and then printing the Word doc using this printer from Word. The catch is I am trying to do this with a custom page size, i.e. the height and width do not match any of the standard paper sizes ie. A4, A3, Letter etc
If I do this manually in MS Word everything works as expected BUT only if I set the Page Setup paper size to PostScript Custom Page Size. If its not set to this value the output page size is one of the pre-defined page sizes i.e. B5 (default).
But if I set the Paper size to PostScript Custom Page Size and then print with the same printer the output file is the correct height and width as set on the document, in this case, 181mm x 260mm
The Problem
I cant find a way to programmatically set the Page Setup Paper size value to the value "PostScript Custom Page Size", and if I dont set this value then the custom height and width are ignored.
What have I already tried
I have tried doing the following:
Using the Word COM objects in PowerShell
...
#create com object
$word = New-Object -com Word.Application
#dont open word UI
$word.visible = $false
#open input file
$doc = $word.Documents.Open($inputfile)
$width = [double]$word.MillimetersToPoints($widthInMM)
$height = [double]$word.MillimetersToPoints($heightInMM)
#set page setup width and height
$doc.PageSetup.PageWidth = $width
$doc.PageSetup.PageHeight = $height
#save the changes
$doc.Save()
$pBackGround = 0
$pAppend = 0
$pRange = 0
#print the file to default printer (i.e. ps printer)
$doc.printout([ref]$pBackGround,[ref]$pAppend,[ref]$pRange,[ref]$outputfile)
...
Looking at the MS docs, the PageSetup object has a PageSize property, which says the following on the page
Setting the PageHeight or PageWidth property changes the PaperSize property to wdPaperCustom.
And looking at the PaperSize property its an enum, WdPaperSize, which has the following values
But as you can see by the quote above, if you set the height and width the paper size will be set to the wdPaperCustom value. BUT this is not the same as PostScript Custom Page Size, which from what I have read this is not one of the valid enum values.
Pure PowerShell
The only way to print a word (docx) file is using the Start-Process command with verb Print. If you dont want to use the default printer you can pipe it to the out-printer command
Start-Process $file -verb Print | out-printer -name "PrinterName"
This prints the document, but actually opens up Word to print which has 2 problems
a. You have to manually specify the output file name
b. It still uses the MS Word default page settings
Recording a VBA Macro: Recording setting the correct paper size doesn't record setting it to PostScript Custom Page Size. This is what the macro looks like
With Selection.PageSetup
.LineNumbering.Active = False
.Orientation = wdOrientPortrait
.TopMargin = MillimetersToPoints(13)
.BottomMargin = MillimetersToPoints(13)
.LeftMargin = MillimetersToPoints(13)
.RightMargin = MillimetersToPoints(13)
.Gutter = MillimetersToPoints(3)
.HeaderDistance = MillimetersToPoints(12.5)
.FooterDistance = MillimetersToPoints(12.5)
.PageWidth = MillimetersToPoints(181)
.PageHeight = MillimetersToPoints(260)
.FirstPageTray = wdPrinterDefaultBin
.OtherPagesTray = wdPrinterDefaultBin
.SectionStart = wdSectionNewPage
.OddAndEvenPagesHeaderFooter = True
.DifferentFirstPageHeaderFooter = True
.VerticalAlignment = wdAlignVerticalTop
.SuppressEndnotes = False
.MirrorMargins = True
.TwoPagesOnOne = False
.BookFoldPrinting = False
.BookFoldRevPrinting = False
.BookFoldPrintingSheets = 1
.GutterPos = wdGutterPosLeft
End With
As you can see above there is no mention of the paper size being set to any value.
I haven't tried this in c# or .NET because they all seem to use the COM Object API which reverts to my issues in 1.
I think the issue is that Word seems to ignore the printer settings, even Microsoft seems to admit this
With the printer I am creating a PostScript printer defining the specific paper size, height and width, but MS Word when printing ignores these settings and uses its own default settings. Even though the height and width of the pages in Word are set correctly its the paper size property that seems to be messing things up.
So the only logical thing I can think of is remove Word from the mix. The issue there is I cant find anything that handles Word properly. You can just send the file to the printer say in PowerShell, but it seems to still open Word and use the Word settings again.
Does anyone know of a way around this or a way to programmatically set the paper size to PostScript Custom Page Size

For anyone interested in how I solved this, well I say solved but its more of a different solution to achieve the same outcome.
I still have not found a way to change the paper size to PostScript Custom Page Size but instead I was able to get the paper size to change based on the width and height set on the document (as per the documentation), which feels like a better solution to me. So these are the steps I took to solve it:
Chose a PostScript driver that I want to use. I decided to use the Xerox PS Class Driver, which is a PostScript driver bundled with Windows.
Find where the driver is located. Printer drivers on Windows are located in following directory
C:\Windows\System32\DriverStore\FileRepository\
You can located the driver you are after using the following grep like command
findstr /S /I /M /C:"Xerox PS Class Driver" C:\Windows\System32\DriverStore\FileRepository\*.*
Edited the PPD file and added the paper size that I am looking for and set it as the default paper size. The most import part to update is PageSize, it provides an invocation value to invoke supported page sizes. I removed all other page sizes and just added the one I was after, calling it Custom
*% Page Size
*OpenUI *PageSize: PickOne
*OrderDependency: 40 AnySetup *PageSize
*DefaultPageSize: Custom
*PageSize Custom/Custom: "featurebegin{<< /PageSize [369 522] >> setpagedevice}featurecleanup"
*CloseUI: *PageSize
The values in this snippet are in points so you need to convert them. Above I am using 130 mm x 184 mm ~ 369 points x 522 points
More information about PPD files in its spec document
Added a printer using this adjusted printer driver
Add-Printer -Name "PrinterName" -DriverName "Xerox PS Class Driver" -PortName "file:"
To keep things simple I named my printer the size of the page i.e. 130x184 so its easy to use programmatically
Created a new form in the print server properties which matches my new paper size. To do this open Devices and Printers > Click your printer > Click Printer Server Properties in top menu > Check "Create a new form" checkbox > Add a name and set your dimenions > Save Form
Using my PowerShell code above, when the page dimensions of my document are set correctly and my new printer is set as default, the new form I just created above is found because our printer now handles the new page dimensions. In my script above I am actually setting the printer as default printer on Windows I just left that part out, so either add it to the script or manually set the printer as default printer.
Printed a PostScript file using the new printer
Hope this helps someone else too

Related

pyQt5: setting font & size in Qtextbrower

I have been using Qt designer to develop a GUI. So far so good except for setting the font and size for Qtextbrowser object.
I redirect print outputs (mainly tables & numbers) toward this textbrowser. It doesn't look like so nice to read s I want to change the font in fixed width but I have been unsuccesful so far.
I tried to change the font and size in Qt Designer --> No changes.
Code looks like this:
font = QtGui.QFont()
print(font)
font.setFamily("Courier")
font.setPointSize(10)
self.textBrowser.setFont(font)
self.textBrowser.setObjectName("textBrowser")
Thanks for any help.
S/

Grapecity Active reports - Print with Letter paper , contents gets cut off

I am using active reports with vb.net to generate my report.
When the paper size is "Letter" and scale is "default", the contents on the right side of my page are getting cut while printing.
Other paper sizes like "Legal" are working fine. How to overcome this issue ? Is there any property can I set to make this work in "Letter" format during print ?
Me.PageSettings.PaperHeight = 11.0!
Me.PageSettings.PaperWidth = 8.5!
Me.PrintWidth = 14.0!
In Reports property dialog, Set (Top,Bottom,Right,left) padding to Zero.it will print all the data without cutting any field.

iText7 - create PDF with exact dimensions when printed - how?

I'm creating a simple PDF using iText7 (C#) but I need it to be printed at exactly the right size. Here's my code:
PdfWriter writer = new PdfWriter("output.pdf");
PdfDocument pdf = new PdfDocument(writer);
pdf.SetDefaultPageSize(iText.Kernel.Geom.PageSize.LETTER);
var page = pdf.AddNewPage();
page.SetCropBox(new iText.Kernel.Geom.Rectangle(36, 36, 7.5f * 72, 10 * 72));
PdfCanvas canvas = new PdfCanvas(page);
canvas.SetStrokeColor(ColorConstants.BLACK).SetLineWidth(3);
canvas.MoveTo(36, 36);
canvas.LineTo(36, 36 + 72); // Draw a line 1 inch long
canvas.LineTo(36 + 72, 36 + 72); // Draw a second line, perpendicular to the first, also 1 inch long
canvas.ClosePathStroke();
pdf.Close();
If I right-click the resulting PDF and select "Print", my triangle is off the bottom of the page.
When I open the resulting PDF in the PDF program I'm using (PDF Architect), it gives me a few options:
If I just click "Print", it gives me lines that are 1 1/16" long and start about 1/8" from the edge of the page, so by default PDF Architect seems to be taking the contents of my crop box and expanding it to the maximum page availability.
If I click on "Fit" before clicking "Print", that results in the desired output - lines 1" long, starting 1/2" from each side of the page. That works but is error-prone - too easy to forget to click "Fit" every time.
Is there a way to generate a PDF that contains information that says "I'm targeting this document at letter size, but I'm staying 1/2" away from all the edges, so when you print, if the printer has margins <= 1/2 inch you should be fine, and just print it exactly how I've described without any shrinking or enlarging"?
You will not be able to completely control this from the PDF document. The PDF processor (e.g. viewing application) or the printer (driver) will always be able to scale the content up or down.
Apparently, PDF Architect has the "Fit" option enabled by default, so it scales the page to the selected paper size.
You are setting a crop box of 7.5x10 in. I assume you're printing to Letter sized (8.5x11 in) paper. So the 7.5x10 page will indeed be scaled up, and your content will become slightly larger.
Is there a way to generate a PDF that contains information that says "I'm targeting this document at letter size, but I'm staying 1/2" away from all the edges, so when you print, if the printer has margins <= 1/2 inch you should be fine, and just print it exactly how I've described without any shrinking or enlarging"?
I would not set the crop box. When the pages in the PDF document are Letter size and the output paper is also Letter size, it should not matter whether the "Fit" option is enabled or not, as not scaling needs to happen. It's definitely not a fool proof solution, but at least it's less error prone.

Set PPT Font to 'Body' Type

I am not able to paste the entirety of my code, but the crux is that I have a textbox in PPT 2013, myTb, that I have (programmatically) pasted some text into. I now want to perform the following two actions:
See if the original text was the PPT 'body default' font (e.g. 'Calibri (body)' vs. 'Calibri' in the MS Ribbon)
If it was the body default, set the new text to also be the body default.
I can't seem to figure either part out, even though I have experimented with reading/writing from/to most of the Shape.TextFrame[n].TextRange.Font.Name... fields. I also had two confounding points to ask about, regarding the Shape.TextFrame.TextRange.Font.NameComplexScript field:
This field does not seem to be a 'complete' indicator of body-default vs non-body-default font. The reason is that if this textbox is originally the body-default ('Calibri (body)'), it will read '+mn-cs', but then I can change the font to the non-default variant ('Calibri'), and it still reads '+mn-cs'.
I then proceed to change the textbox to an entirely different font, which changes this field to the font's name as expected. However, if I then change back to the body-default font (or any other font, for that matter), this field remains on the previous font's name.
To set the font to the body or heading font, you need to use a weird bit of syntax:
{object}.Font.Name = "+" + FontType + "-" + FontLang
Where:
FontType is "mj" or "mn" for Major (headings) or Minor (body) respectively.
FontLang is "lt", "cs" or "ea" for Latin, Complex Scripts or East Asian
For example to set the font to the theme's body text font for Latin text:
ActivePresentation.Slides(1).Shapes(1).TextFrame.TextRange.Font.Name = "+mn-lt"
A bit late to the party, but I found this in case anyone needs it.
Edit: This is C# code, using the Interop PowerPoint namespace.
string headingsThemeFont = Globals.ThisAddIn.Application.ActivePresentation.SlideMaster.Theme.ThemeFontScheme.MajorFont.Item(MsoFontLanguageIndex.msoThemeLatin).Name;
string bodyThemeFont = Globals.ThisAddIn.Application.ActivePresentation.SlideMaster.Theme.ThemeFontScheme.MinorFont.Item(MsoFontLanguageIndex.msoThemeLatin).Name;

How to choose a different header image in MS Word 2013 using a macro/button

I'd like to create a Word stationary template with ability to cycle through different colored logos in its header. My company uses a logo in five different colors and I would like to create a single template with a button that would allow me to cycle through the different colored logos every time I create a new document from this template. Can this be done, perhaps with a little VBA?
Edit:
After working with an answer from Olle Sjögren I've come up with the following working script:
Option Explicit
Public imgCounter As Integer
Sub cycle_logos()
Dim I As Variant
Dim logoColors(4) As String
logoColors(0) = "logo_magenta.png"
logoColors(1) = "logo_teal.png"
logoColors(2) = "logo_orange.png"
logoColors(3) = "logo_red.png"
logoColors(4) = "logo_grayscale.png"
For Each I In logoColors
ActiveDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange.Item(I).Visible = msoFalse
Next I
imgCounter = imgCounter + 1
If imgCounter = 5 Then imgCounter = 0
ActiveDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange.Item(logoColors(imgCounter)).Visible = msoTrue
End Sub
It is worth mentioning how I came up with the image names, since I didn't find a way to do this from inside Word. I renamed the template extension to zip and unzipped it. In the extracted files I opened word\header2.xml (this may vary, depending on the position in the document) and edited the nodes containing the names of pictures, i.e. <wp:docPr/>, e.g.:
<wp:docPr name="Picture 1" id="1"/>
became:
<wp:docPr name="logo_magenta.png" id="1"/>
etc. I then replaced the XML file in the ZIP with my edited version and changed the extension back to dotm.
As mentioned, there are several ways to do this. I would suggest storing the images outside of the template, otherwise all documents based on the templates would include all logo images, even if they are not shown, making the documents bigger than they need to be. That approach makes installing the template a bit harder, since you would have to copy the images to the clients as well as the template file.
To answer your question regarding addressing the images in the header - you can address then through the correct story range object. In my example I assume that they are in the primary header. To tell the different images apart, you can use the Name property or the index in the Item collection:
ThisDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange.Item("Picture 1").Visible = msoTrue
ThisDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange.Item(1).Visible = msoFalse