Question
SkillStorm
US
Last activity: 18 Jul 2017 10:30 EDT
Setting margins in PDF's.
How can I achieve setting margins in PDF's generated in Pega?
I have a requirement to generate PDF's that can be printed and used in windowed envelopes. I'm using the OOTB Activity HTMLToPDF. Neither this Activity or the Java API it uses (PDFUtils) have any settings for margins.
I have managed to get it to almost work by placing the content in a table and using CSS to apply margins for columns/table rows to approximate side and top margins. This only works if the content fits entirely on one page and isn't long enough to go past the desired bottom margin. Top and bottom margins are not applied to any subsequent pages (as is expected using an HTML table in this way).
***Updated by moderator: Lochan to close post***
This post has been archived for educational purposes. Contents and links will no longer be updated. If you have the same/similar question, please write a new post.
-
Like (0)
-
Share this page Facebook Twitter LinkedIn Email Copying... Copied!
Accepted Solution
Updated: 23 Jun 2015 15:56 EDT
SkillStorm
US
I thought I gleaned from PD4ML's user docs that the page size defaults to the standard 8.5 x 11 inches though that assumption likely only existed in my head. Evidently this is not the default as when I manually set the page size and printed at actual size things lined up properly. Here is the updated solution.
(code blocks are not working when I use them but here's the text)
===============================================================
try {
org.zefer.pd4ml.PD4ML PD4MLObject = new org.zefer.pd4ml.PD4ML();
//Set page size in points (8.5 x 11 inches)
PD4MLObject.setPageSize( new java.awt.Dimension(612, 792) );
//Set margins in milimeters (top, left, bottom, right)
PD4MLObject.setPageInsetsMM(new java.awt.Insets(24, 24, 12, 24));
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
PD4MLObject.render(new java.io.StringReader(HTMLStream), baos);
baos.close();
//Put the byte array in a parameter
tools.putParamValue("PDFDocument",baos.toByteArray());
}
catch (Exception e) {
e.printStackTrace();
}
===============================================================
I thought I gleaned from PD4ML's user docs that the page size defaults to the standard 8.5 x 11 inches though that assumption likely only existed in my head. Evidently this is not the default as when I manually set the page size and printed at actual size things lined up properly. Here is the updated solution.
(code blocks are not working when I use them but here's the text)
===============================================================
try {
org.zefer.pd4ml.PD4ML PD4MLObject = new org.zefer.pd4ml.PD4ML();
//Set page size in points (8.5 x 11 inches)
PD4MLObject.setPageSize( new java.awt.Dimension(612, 792) );
//Set margins in milimeters (top, left, bottom, right)
PD4MLObject.setPageInsetsMM(new java.awt.Insets(24, 24, 12, 24));
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
PD4MLObject.render(new java.io.StringReader(HTMLStream), baos);
baos.close();
//Put the byte array in a parameter
tools.putParamValue("PDFDocument",baos.toByteArray());
}
catch (Exception e) {
e.printStackTrace();
}
===============================================================
CAUTION: It's worth noting that PD4ML does not support setting font size via CSS tag (such as P {myStyles...}), class (such as P.myClass {myStyles...}), or custom tag styles (such as myTag {myStyles...}). Font sizing must be done inline (such as <P style="font-size:10pt">). Font family works as expected.
Pegasystems Inc.
US
PD4ML API (which is accessible through Java in Pega) may be help in achieving this. The following article provides the details of using pageInsets to define margins on pages:
http://pd4ml.com/cookbook/pdf_page_formatting.htm
http://pd4ml.com/api/org/zefer/pd4ml/PD4ML.html#setPageInsetsMM(java.awt.Insets)
Below is an example of using PD4ML api in a java step in an Activity to set page footer:
//Create the footer
org.zefer.pd4ml.PD4PageMark footer = new org.zefer.pd4ml.PD4PageMark();
java.awt.Font font = new java.awt.Font("Arial", java.awt.Font.PLAIN, 10);
footer.setFont(font);
footer.setFontSize(10);
footer.setPagesToSkip(0);
The API is available as long as fully qualified. Hope this helps !
SkillStorm
US
Thanks so much Nitin! For those interested here is how I solved this issue.
I specialized HTMLToPDF and replaced the Java in step 3 with this:
===============================================================
try {
org.zefer.pd4ml.PD4ML PD4MLObject = new org.zefer.pd4ml.PD4ML();
java.awt.Insets margins = new java.awt.Insets(25, 25, 12, 25);
PD4MLObject.setPageInsetsMM(margins);
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
PD4MLObject.render(new java.io.StringReader(HTMLStream), baos);
baos.close();
//Put the byte array in a parameter
tools.putParamValue("PDFDocument",baos.toByteArray());
}
catch (Exception e) {
e.printStackTrace();
}
===============================================================
See the support pages and API of pd4ml for more info.
Updated: 23 Jun 2015 4:10 EDT
Pegasystems Inc.
GB
Hi Hermy,
Glad you were able to put together a Local-Change for your specific requirement here !
I have logged a "Feedback Item" (Enhancement Request) on the back of this:
FDBK-11710: Allow PD4ML method "setPageInsetsMM" to be set from HTMLTOPDF
So that we can look into whether it would be feasable to include a way of doing this OOTB in a future release of PRPC.
Just an aside - you may already be aware of this, but in case not: PD4ML also has a limited set of "Proprietary tags"* (List of HTML tags supported by PD4ML - HTML-to-PDF converter for Java and .NET): these tags allow the input document itself to provide some configuration to the HTMLTOPDF process : unfortunately it appears that there is no tag available (that I could find) for "setPageInsetsMM" - but I thought I would mention this mechanism here - as this sometimes provides a path to a solution without having to create a custom version of HTMLTOPDF.
Thanks
John
*(To make use of the PD4ML Proprietary Tags from HTMLTOPDF, you will need to disable the 'HTMLTIDY' option within HTMLTOPDF - otherwise these (non-standard) TAGs get stripped out of the input document by default).
SkillStorm
US
I have logged a "Feedback Item" (Enhancement Request) on the back of this:
FDBK-11710: Allow PD4ML method "setPageInsetsMM" to be set from HTMLTOPDF
that's fantastic, John. saved me the trouble of submitting a feature request. thank you.
it seems none of the proprietary tags are for setting "insets" or margins, whether using the MM version or the standard version. it's good to know about these tags, though, for other requirements that might come up.
SkillStorm
US
Upon actually printing out and testing I've realized my problems are not solved. The margins are not accurate and the page doesn't seem to be scaled precisely as toggling "Fit to page" or "Actual size" causes a very noticable difference. Also, the printed margins are not the same when defined as such and the bottom margin seems to be entirely ignored. I've tried printing both "Fit to page" and "Actual size" from Chrome PDF viewer and from Adobe Reader but I cannot get it close to what I actually need.
I could continue to finagle with the defined margins to ensure the actual result is what I need and have "Use Adobe Ready and print at actual size" be a limitation but that's unpleasant for the end users who will have to do this extra work for each print.
Any suggestions or ideas? I'm not required to use PDF's so if there's something else that I can use to print reliably I'm open to it.
Updated: 23 Jun 2015 5:13 EDT
Pegasystems Inc.
GB
Hi Hermy,
I haven't tried this yet - but I took a look at the source code of the Engine Class used to call PD4ML - and it looks like we might actually over-ride any PageInsets within the engine with a call like this:
aConverter.setPageInsets(new Insets(20, 10, 10, 10));
aConverter.setHtmlWidth(950);
In fact: looking back at this - I'm not sure whether your Local Change can be having any effect on the resultant PDF here ? (maybe the other settings such as 'print actual size' etc are the parameters that are causing the end-effect here?).
So it looks like (unfortunately) that will not be able to achieve what you need to do with HTMLTOPDF at this point....
As an alternative - you could consider generating a WORD file ?
You could look into using PRPC correspondance rules here - or you could try the suggestion below.
I googled for a starting point and found this:Make address guesswork a thing of the past | VA Pro Magazine
Which just creates a Text Box in the WORD file - where you can position your address for the Windowed Envelope.
Hi Hermy,
I haven't tried this yet - but I took a look at the source code of the Engine Class used to call PD4ML - and it looks like we might actually over-ride any PageInsets within the engine with a call like this:
aConverter.setPageInsets(new Insets(20, 10, 10, 10));
aConverter.setHtmlWidth(950);
In fact: looking back at this - I'm not sure whether your Local Change can be having any effect on the resultant PDF here ? (maybe the other settings such as 'print actual size' etc are the parameters that are causing the end-effect here?).
So it looks like (unfortunately) that will not be able to achieve what you need to do with HTMLTOPDF at this point....
As an alternative - you could consider generating a WORD file ?
You could look into using PRPC correspondance rules here - or you could try the suggestion below.
I googled for a starting point and found this:Make address guesswork a thing of the past | VA Pro Magazine
Which just creates a Text Box in the WORD file - where you can position your address for the Windowed Envelope.
I then saved the document in 'Word 2003 XML Document' format (a single-page XML format :Microsoft Office XML formats - Wikipedia, the free encyclopedia -it's an older system now superceded by Open Office - but still useful sometimes).
The resulting XML contains quite a bit of stuff (some of which I was able to trim-out without affecting the resultant document - such as document author etc) - the important bit is this fragment:
<v:shape id="Text Box 1" o:spid="_x0000_s1026" type="#_x0000_t202" style="position:absolute; margin-left:-18.65pt; margin-top:19.75pt; width:282pt; height:102pt;z-index:1;visibility:visible; mso-wrap-style:square;mso-wrap-distance-left:9pt;mso-wrap-distance-top:0 ;mso-wrap-distance-right:9pt; mso-wrap-distance-bottom:0; mso-position-horizontal:absolute;mso-position-horizontal-relative:text;mso-position-vertical:absolute;mso-position-vertical-relative:text;v-text-anchor:top" strokeweight=".5pt"> <v:textbox> <w:txbxContent> <w:p wsp:rsidR="00A6337B" wsp:rsidRDefault="00A6337B" wsp:rsidP="00A6337B"> <w:pPr> <w:contextualSpacing /> </w:pPr> <w:r> <w:t>John Doe,</w:t> </w:r> </w:p> <w:p wsp:rsidR="00A6337B" wsp:rsidRDefault="00A6337B" wsp:rsidP="00A6337B"> <w:pPr> <w:contextualSpacing /> </w:pPr> <w:r> <w:t>Pegasystems,</w:t> </w:r> </w:p> <w:p wsp:rsidR="00A6337B" wsp:rsidRDefault="00A6337B" wsp:rsidP="00A6337B"> <w:pPr> <w:contextualSpacing /> </w:pPr> <w:r> <w:t>Corporate Headquarters</w:t> </w:r> </w:p> <w:p wsp:rsidR="00A6337B" wsp:rsidRDefault="00A6337B" wsp:rsidP="00A6337B"> <w:pPr> <w:contextualSpacing /> </w:pPr> <w:r> <w:t>One Rogers Street</w:t> </w:r> </w:p> <w:p wsp:rsidR="00A6337B" wsp:rsidRDefault="00A6337B" wsp:rsidP="00A6337B"> <w:pPr> <w:contextualSpacing /> </w:pPr> <w:r> <w:t>Cambridge, MA 02142-1209</w:t> </w:r> </w:p> <w:p wsp:rsidR="00A6337B" wsp:rsidRDefault="00A6337B" wsp:rsidP="00A6337B"> <w:pPr> <w:contextualSpacing /> </w:pPr> <w:r> <w:t>U.S.A</w:t> </w:r> </w:p> </w:txbxContent> </v:textbox> </v:shape>
And just to prove it (almost!) fits a real envelope:
It should be possible to dynamically replace the address components with the values of PRPC Properties here.
How to render this to WORD ? You could use XSLT (PRPC has an activity for this already: "XSLTranslate") to 'plugin' the values you need; or you could use a HTML Stream Rule - and use Pega JSP TAGS to do the value-replace.
If you need to deliver the File to the end-user (ie, they need to download it) - you will probably need to set the MIME type correctly in the HTTP headers.
There is an Activity "Rule-Obj-HTML|ViewExcelData|Pega-WB:07-10-01" (I'm looking at a PRPC 7.1.7 here) which does a similar thing for downloading EXCEL files, which does this:
ClipboardPage objHeadersPage = tools.findPage("pxRequestor").getProperty(".pyHTTPResponseHeaders").getPageValue();
objHeadersPage.putString("contentType" ,"application/vnd.ms-excel");
objHeadersPage.putString("ContentDisposition" ,"attachment;filename=ExportData.xls");
"
I think (from here for instance: Office 2007 File Format MIME Types for HTTP Content Streaming - VSOfficeDeveloper: Known Problems, Bugs, and Fixes - Sit…) that the contentType will need to change to "application/msword" or one of the others (2003 format is not listed here explicitly actually...)
Updated: 23 Jun 2015 6:50 EDT
Pegasystems Inc.
GB
One other idea that might be worth exploring here - using HTML5 and a CSS designed specifically for printing.... you might not even require conversion to PDF/WORD.....
I haven't tried this - but googling for 'labels html css' comes back with a few articles which may be off use here....
SkillStorm
US
One other idea that might be worth exploring here - using HTML5 and a CSS designed specifically for printing....
I considered this but it won't satisfy our needs because that cannot control for top and bottom margins for all pages.
SkillStorm
US
In fact: looking back at this - I'm not sure whether your Local Change can be having any effect on the resultant PDF here ?...
Here's what I observe with the local change:
I haven't tried this yet - but I took a look at the source code of the Engine Class used to call PD4ML - and it looks like we might actually over-ride any PageInsets within the engine with a call like this:
aConverter.setPageInsets(new Insets(20, 10, 10, 10));
aConverter.setHtmlWidth(950);
... (maybe the other settings such as 'print actual size' etc are the parameters that are causing the end-effect here?).
I tried using setPageInsets and setPageInsetsMM and the results were much the same - margins were there but not what was defined. The only differnce I see when using setHtmlWidth is it shrinks/enlarges the font - the margins are unaffected.
In fact: looking back at this - I'm not sure whether your Local Change can be having any effect on the resultant PDF here ?...
Here's what I observe with the local change:
I haven't tried this yet - but I took a look at the source code of the Engine Class used to call PD4ML - and it looks like we might actually over-ride any PageInsets within the engine with a call like this:
aConverter.setPageInsets(new Insets(20, 10, 10, 10));
aConverter.setHtmlWidth(950);
... (maybe the other settings such as 'print actual size' etc are the parameters that are causing the end-effect here?).
I tried using setPageInsets and setPageInsetsMM and the results were much the same - margins were there but not what was defined. The only differnce I see when using setHtmlWidth is it shrinks/enlarges the font - the margins are unaffected.
As an alternative - you could consider generating a WORD file ?
You could look into using PRPC correspondance rules here - or you could try the suggestion below.
I wanted to use correspondence rules as we are working with correspondence, both postal and email. However, our users need the ability to modify the corr before being sent/generated and we couldn't figure out how to do this. When I came in we were already using HTML rules for the content and exposing it via a Rich Text Editor before sending/generating.
I googled for a starting point and found this:Make address guesswork a thing of the past | VA Pro Magazine
Which just creates a Text Box in the WORD file - where you can position your address for the Windowed Envelope.
I then saved the document in 'Word 2003 XML Document' format (a single-page XML format :Microsoft Office XML formats - Wikipedia, the free encyclopedia -it's an older system now superceded by Open Office - but still useful sometimes).
The resulting XML contains quite a bit of stuff (some of which I was able to trim-out without affecting the resultant document - such as document author etc) - the important bit is this fragment:
...
It should be possible to dynamically replace the address components with the values of PRPC Properties here.
How to render this to WORD ? You could use XSLT (PRPC has an activity for this already: "XSLTranslate") to 'plugin' the values you need; or you could use a HTML Stream Rule - and use Pega JSP TAGS to do the value-replace.
It's ok to have the address be inline with the content- we're trying to control that by margins and blank lines. Would generating the Word XML be compatiable with what we have setup already where users can modify the HTML? Seems like it would be somewhat extensive work to translate our CSS to the Word XML tags and possibly not compatible with user defined content.
If you need to deliver the File to the end-user (ie, they need to download it) - you will probably need to set the MIME type correctly in the HTTP headers.
We won't be transmitting these files. They are only used to be printed (and stored for possible re-print).
Pegasystems Inc.
GB
Hi Hermy,
Thanks for the update: so to be clear - are you saying that if you could have a consistent set of 'print settings' (ie, prevent users making changes, and also prevent them from having to make changes) then you would have what you need here ? (ie, the PDF is printing out ok - so long as certain Print Options are used (or not used) )?
I'm just wondering whether you need (some hypothetical as yet - maybe using Powershell / Windows Host Commands?) mechanism to print the PDFs with a certain set options set up ?
Thanks again,
John
SkillStorm
US
Close. Let me try to explain. I have yet to be able to get PDF's to be generated accurately (thus printed) by any means. At this point, it would be ok if we must stipulate to users they must print using "Fit to page". It is not ok to stipulate they must manually set margins on the printer when printing.
What I'm trying to do at this time is find the pageInset values that results in the desired margins when printed using "Fit to page" and Adobe Reader. The setPageInsets and setPageInsetsMM methods don't seem to work on the basis the API says they do (points and milimeters) so I'm having to test every value until I land on the "right" one. Furthermore, the exact same values for each margin produce different results so I have to find the exact value for all sides separately dispite 3 of them needing to be the same.
The requirement is only that we have a reliable mechanism for printing (PDF or otherwise). A stipulation of that requirement is that the user be able to modify the content before printing.
Updated: 23 Jun 2015 12:30 EDT
Pegasystems Inc.
GB
Hi Hermy,
Mmmh - ok it might be worth testing this outside of PRPC - using the PD4ML's commandline tool (or a standalone Java class) as well as within PRPC.
http://pd4ml.com/html-to-pdf-command-line-tool.htm
We should test with the same version of PD4ML as PRPC is using - I think there is a way of downloading the PD4ML JAR from PRPC's PR_ENGINECLASSES table - but I will need to look into that (anybody know how to do this? #helpme)....
Then we could at least try and isolate the issue down to being something related to the Library itself or the way it is being used within PRPC.....
Thanks
John
Accepted Solution
Updated: 23 Jun 2015 15:56 EDT
SkillStorm
US
I thought I gleaned from PD4ML's user docs that the page size defaults to the standard 8.5 x 11 inches though that assumption likely only existed in my head. Evidently this is not the default as when I manually set the page size and printed at actual size things lined up properly. Here is the updated solution.
(code blocks are not working when I use them but here's the text)
===============================================================
try {
org.zefer.pd4ml.PD4ML PD4MLObject = new org.zefer.pd4ml.PD4ML();
//Set page size in points (8.5 x 11 inches)
PD4MLObject.setPageSize( new java.awt.Dimension(612, 792) );
//Set margins in milimeters (top, left, bottom, right)
PD4MLObject.setPageInsetsMM(new java.awt.Insets(24, 24, 12, 24));
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
PD4MLObject.render(new java.io.StringReader(HTMLStream), baos);
baos.close();
//Put the byte array in a parameter
tools.putParamValue("PDFDocument",baos.toByteArray());
}
catch (Exception e) {
e.printStackTrace();
}
===============================================================
I thought I gleaned from PD4ML's user docs that the page size defaults to the standard 8.5 x 11 inches though that assumption likely only existed in my head. Evidently this is not the default as when I manually set the page size and printed at actual size things lined up properly. Here is the updated solution.
(code blocks are not working when I use them but here's the text)
===============================================================
try {
org.zefer.pd4ml.PD4ML PD4MLObject = new org.zefer.pd4ml.PD4ML();
//Set page size in points (8.5 x 11 inches)
PD4MLObject.setPageSize( new java.awt.Dimension(612, 792) );
//Set margins in milimeters (top, left, bottom, right)
PD4MLObject.setPageInsetsMM(new java.awt.Insets(24, 24, 12, 24));
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
PD4MLObject.render(new java.io.StringReader(HTMLStream), baos);
baos.close();
//Put the byte array in a parameter
tools.putParamValue("PDFDocument",baos.toByteArray());
}
catch (Exception e) {
e.printStackTrace();
}
===============================================================
CAUTION: It's worth noting that PD4ML does not support setting font size via CSS tag (such as P {myStyles...}), class (such as P.myClass {myStyles...}), or custom tag styles (such as myTag {myStyles...}). Font sizing must be done inline (such as <P style="font-size:10pt">). Font family works as expected.
-
Naresh Chandra Anchuri BIBHASH KALITA
Pegasystems Inc.
GB
Nice one - glad you figured this out - I also realized that my previous suggestion (building a standalone Java class) was moot - since I had forgotten you were creating your own instance of the 'PD4ML' object rather than using the PDFUtilsImpl().
Aside:
With regard to code-formatting not working in forums - I'm using Firefox and I use the option 'Use advanced editor', then I paste the code, select it, click the '>>' button on the toolbar , navigate to 'Syntax Highlighting | Java' - this usually works ok for me ? I have repasted your code formatting here for reference.
Cheers !
John
try { org.zefer.pd4ml.PD4ML PD4MLObject = new org.zefer.pd4ml.PD4ML(); //Set page size in points (8.5 x 11 inches) PD4MLObject.setPageSize( new java.awt.Dimension(612, 792) ); //Set margins in milimeters (top, left, bottom, right) PD4MLObject.setPageInsetsMM(new java.awt.Insets(24, 24, 12, 24)); java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); PD4MLObject.render(new java.io.StringReader(HTMLStream), baos); baos.close(); //Put the byte array in a parameter tools.putParamValue("PDFDocument",baos.toByteArray()); } catch (Exception e) { e.printStackTrace(); }
Alter Domus
IE
Hi Hermy, John,
I tried to use the above code to set margins in the PDF, but above code is ignoring all the parameters like header, footer etc.
byte[] byteArray = pdfUtil.generatePDF(HTMLStream,tools.getParameterPage());
This actually considers all the parameters we pass to render the PDF content. Is there anyway that we can consider these params in the above code as well ?