Difficulty in setting values of header from top and footer from bottom using Apache POI - apache

I can not find any direct Apache POI code for setting the values of header from top and footer from bottom in a word file.Please give me some suggestion for setting these values using java code.

I too have been looking for the answer to this question for quite some time and came across this answer but sadly forget where.
I hope this helps someone too.
import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar;
import java.math.BigInteger;
public class CreateWordHeaderFooterSpacing {
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
// create header-footer
XWPFHeaderFooterPolicy headerFooterPolicy = document.getHeaderFooterPolicy();
if (headerFooterPolicy == null) headerFooterPolicy = document.createHeaderFooterPolicy();
// create footer start
XWPFFooter footer = headerFooterPolicy.createFooter(XWPFHeaderFooterPolicy.DEFAULT);
XWPFParagraph paragraph = footer.getParagraphArray(0);
paragraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = paragraph.createRun();
run.setText("Footer");
CTSectPr sectPr = document.getDocument().getBody().getSectPr();
if (sectPr == null) sectPr = document.getDocument().getBody().addNewSectPr();
CTPageMar pageMar = sectPr.getPgMar();
if (pageMar == null) pageMar = sectPr.addNewPgMar();
pageMar.setLeft(BigInteger.valueOf(720)); //720 TWentieths of an Inch Point (Twips) = 720/20 = 36 pt = 36/72 = 0.5"
pageMar.setRight(BigInteger.valueOf(720));
pageMar.setTop(BigInteger.valueOf(1440)); //1440 Twips = 1440/20 = 72 pt = 72/72 = 1"
pageMar.setFooter(BigInteger.valueOf(720)); //0.5" footer margin
long notPrintableBottomPageRange = (long)(0.038888*72*20); //0.038888" gap for non printable bottom page range
pageMar.setBottom(BigInteger.valueOf(1152+720+notPrintableBottomPageRange)); //1152 Twips = 1152/20/72 = 0.8"
//bottom margin = 0.8" footer spacing + 0.5" footer margin + 0.038888" gap for non printable bottom page range
document.write(new FileOutputStream("CreateWordHeaderFooterSpacing.docx"));
document.close();
}
}

Related

Using pdfbox - how to get the font from a COSName?

How to get the font from a COSName?
The solution I'm looking for looks somehow like this:
COSDictionary dict = new COSDictionary();
dict.add(fontname, something); // fontname COSName from below code
PDFontFactory.createFont(dict);
If you need more background, I added the whole story below:
I try to replace some string in a pdf. This succeeds (as long as all text is stored in one token). In order to keep the format I like to re-center the text. As far as I understood I can do this by getting the width of the old string and the new one, do some trivial calculation and setting the new position.
I found some inspiration on stackoverflow for replacing https://stackoverflow.com/a/36404377 (yes it has some issues, but works for my simple pdf's. And How to center a text using PDFBox. Unfortunatly this example uses a font constant.
So using the first link's code I get a handling for operator 'TJ' and one for 'Tj'.
PDFStreamParser parser = new PDFStreamParser(page);
parser.parse();
java.util.List<Object> tokens = parser.getTokens();
for (int j = 0; j < tokens.size(); j++)
{
Object next = tokens.get(j);
if (next instanceof Operator)
{
Operator op = (Operator) next;
// Tj and TJ are the two operators that display strings in a PDF
if (op.getName().equals("Tj"))
{
// Tj takes one operator and that is the string to display so lets
// update that operator
COSString previous = (COSString) tokens.get(j - 1);
String string = previous.getString();
String replaced = prh.getReplacement(string);
if (!string.equals(replaced))
{ // if changes are there, replace the content
previous.setValue(replaced.getBytes());
float xpos = getPosX(tokens, j);
//if (true) // center the text
if (6 * xpos > page.getMediaBox().getWidth()) // check if text starts right from 1/xth page width
{
float fontsize = getFontSize(tokens, j);
COSName fontname = getFontName(tokens, j);
// TODO
PDFont font = ?getFont?(fontname);
// TODO
float widthnew = getStringWidth(replaced, font, fontsize);
setPosX(tokens, j, page.getMediaBox().getWidth() / 2F - (widthnew / 2F));
}
replaceCount++;
}
}
Considering the code between the TODO tags, I will get the required values from the token list. (yes this code is awful, but for now it let's me concentrate on the main issue)
Having the string, the size and the font I should be able to call the getWidth(..) method from the sample code.
Unfortunatly I run into trouble to create a font from the COSName variable.
PDFont doesn't provide a method to create a font by name.
PDFontFactory looks fine, but requests a COSDictionary. This is the point I gave up and request help from you.
The names are associated with font objects in the page resources.
Assuming you use PDFBox 2.0.x and that page is a PDPage instance, you can resolve the name fontname using:
PDFont font = page.getResources().getFont(fontname);
But the warning from the comments to the questions you reference remain: This approach will work only for very simple PDFs and might even damage other ones.
try {
//Loading an existing document
File file = new File("UKRSICH_Mo6i-Spikyer_z1560-FAV.pdf");
PDDocument document = PDDocument.load(file);
PDPage page = document.getPage(0);
PDResources pageResources = page.getResources();
System.out.println(pageResources.getFontNames() );
for (COSName key : pageResources.getFontNames())
{
PDFont font = pageResources.getFont(key);
System.out.println("Font: " + font.getName());
}
document.close();
}

PDFBox inserting data into multi-page doc

I'm trying to create a simple pdf multi-page document with fields in it. To do that I have a template pdf which in the code I clone this template as many times as needed in order to create the document itself.
The problem comes with inserting some data in it. The type of data I try to insert to the document is not supposed to change across the pages. Rather than that, it stays static in all pages, Like the "Pages" digit that represents the number of the pages that this document contains.
Now, Inside my template pdf I have some text fields like, for instance, "Shipper1" and "Pages". I want to be able to insert my data into this text fields so that all the pages in the document will have this values in their "Shipper1" and "Pages" fields.
My code currently does that only on the first page. It shows the data perfectly. On the other hand, when I go to another page, the data isn't shown there. It's just displays an empty field.
Here is the code where I initiate the pdf document:
static void initiatePdf() {
// Initiate a new PDF Box object and get the acro form from it
File file = new File(Constants.Paths.EMPTY_DOC)
PDDocument tempDoc
Evaluator evaluator = new Evaluator(metaHolder)
int numPages = evaluator.getNumOfPagesRequired(objects)
FieldRenamer renamer = new FieldRenamer()
PDResources res = new PDResources()
COSDictionary acroFormDict = new COSDictionary()
List<PDField> fields = []
Closure isFieldExist = {List<PDField> elements, String fieldName ->
elements.findAll{it.getFullyQualifiedName() == fieldName}.size() > 0
}
for(int i = 0; i < numPages; i++) {
tempDoc = new PDDocument().load(file)
PDDocumentCatalog docCatalog = tempDoc.getDocumentCatalog()
PDAcroForm acroForm = docCatalog.acroForm
PDPage page = (PDPage) docCatalog.getPages().get(0)
renamer.setCurrentForm(acroForm)
if(i == 0) {
res = acroForm.getDefaultResources()
acroFormDict.mergeInto(acroForm.getCOSObject())
renamer.renameFields(1)
} else
renamer.renameFields(i*10+1)
List<PDField> newFields = acroForm.fields.findAll { PDField newField ->
isFieldExist(fields, newField.getFullyQualifiedName()) == false
}
fields.addAll(newFields)
document.addPage(page)
}
PDAcroForm acroForm = new PDAcroForm(document, acroFormDict);
acroForm.setFields(fields)
acroForm.setDefaultResources(res);
document.documentCatalog.setAcroForm(acroForm)
}
A couple of things first:
metaHolder instance holds the information about all
the fields that reside inside the acro form. the info is: Field Name, Field Widget Width, Field Font and Font size
evaluator is just and instance of the Evaluator class. Its purpose is to analyze the dynamic data and decide how many pages will take to contain all that text data.
Here is where I try to populate the fields with text:
static void populateData() {
def properties = ["$Constants.Fields.SHIPPER" : "David"]
FieldPopulater populater = new FieldPopulater(document, metaHolder)
populater.populateStaticFields(properties)
}
FieldPopulater class:
package app.components
import app.StringUtils
import app.components.entities.DGObject
import app.components.entities.FieldMeta
import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm
import org.apache.pdfbox.pdmodel.interactive.form.PDField
/**
* Created by David on 18/10/2016.
*/
class FieldPopulater {
PDAcroForm acroForm
FormMetaHolder metaHolder
FieldPopulater(PDDocument document, FormMetaHolder metaHolder) {
this.acroForm = document.getDocumentCatalog().acroForm
this.metaHolder = metaHolder
}
void populateStaticFields(properties) {
List<PDField> fields = []
properties.each {fieldName, data ->
FieldMeta fieldMeta = metaHolder.getMetaData(fieldName)
fields = acroForm.fields.findAll { PDField field ->
String currentName = field.getFullyQualifiedName()
char lastChar = currentName[-1]
if(Character.isDigit(lastChar)) {
currentName = currentName.substring(0,currentName.size()-1)
}
currentName == fieldName
}
if(fields.size() > 1) {
int counter = 1
String tempData = data
String currentFitData
while(tempData.isEmpty() != true) {
int maxWords = Utils.getMaxWords(tempData, fieldMeta)
currentFitData = StringUtils.getTextByWords(tempData, maxWords)
tempData = StringUtils.chopTextByWords(tempData, maxWords)
PDField field = fields.find{it.getFullyQualifiedName()[-1] == "$counter"}
field?.setValue(currentFitData)
counter++
}
} else {
PDField tempField = fields[0]
tempField.setValue(data)
}
}
}
}
The result is, in the first page, the field "Shipper" has a value of "David"
In the second page, the field "Shipper" is empty.
Here is an image. First page:
Second page:
What is the problem here?
UPDATE: I tried to add the widgets of every new acro form to the current page so that every field will a few kids widgets that will represent the field, but it still doesn't work.
// All the widgets that are associated with the fields
List<PDAnnotationWidget> widgets = acroForm.fields.collect {PDField field -> field.getWidgets().get(0)}
page.annotations.addAll(widgets)
UPDATE: I also tried to add the current widget of a field to the parent field's collection of widgets. Here is the code:
List<PDAnnotationWidget> widgets = []
// All the widgets that are associated with the fields
acroForm.fields.each {PDField field ->
PDAnnotationWidget widget = field.widgets.get(0)
// Adding the following widget to the page and to the field's list of annotation widgets
widgets.add(widget)
fields.find {it.getFullyQualifiedName() == field.getFullyQualifiedName()}?.widgets.add(widget)
}
page.annotations.addAll(widgets)
What you want is to have sereval visual representations of the same field. This is done by having several annotation widgets for such a field.
In PDF, when a field has only one annotation widget, they share a common dictionary. When it has several, the annotation widgets are in a child list of the field.
When you want several annotation widgets for one field, you need to create the annotation widgets with new PDAnnotationWidget() instead of calling field.getWidgets().get(0) and using that one. These widgets must be added to a list, and this list must be assigned to the field with setWidgets(). And for each widget you must call setRectangle() and setPage() and setParent().
An example for this is in the new CreateMultiWidgetsForm.java example. The setParent() method is not yet available in 2.0.3 (but will be in 2.0.4). In this answer, it is replaced with a call that does the same thing in a less elegant way.
public final class CreateMultiWidgetsForm
{
private CreateMultiWidgetsForm()
{
}
public static void main(String[] args) throws IOException
{
// Create a new document with 2 empty pages.
PDDocument document = new PDDocument();
PDPage page1 = new PDPage(PDRectangle.A4);
document.addPage(page1);
PDPage page2 = new PDPage(PDRectangle.A4);
document.addPage(page2);
// Adobe Acrobat uses Helvetica as a default font and
// stores that under the name '/Helv' in the resources dictionary
PDFont font = PDType1Font.HELVETICA;
PDResources resources = new PDResources();
resources.put(COSName.getPDFName("Helv"), font);
// Add a new AcroForm and add that to the document
PDAcroForm acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
// Add and set the resources and default appearance at the form level
acroForm.setDefaultResources(resources);
// Acrobat sets the font size on the form level to be
// auto sized as default. This is done by setting the font size to '0'
String defaultAppearanceString = "/Helv 0 Tf 0 g";
acroForm.setDefaultAppearance(defaultAppearanceString);
// Add a form field to the form.
PDTextField textBox = new PDTextField(acroForm);
textBox.setPartialName("SampleField");
// Acrobat sets the font size to 12 as default
// This is done by setting the font size to '12' on the
// field level.
// The text color is set to blue in this example.
// To use black, replace "0 0 1 rg" with "0 0 0 rg" or "0 g".
defaultAppearanceString = "/Helv 12 Tf 0 0 1 rg";
textBox.setDefaultAppearance(defaultAppearanceString);
// add the field to the AcroForm
acroForm.getFields().add(textBox);
// Specify 1st annotation associated with the field
PDAnnotationWidget widget1 = new PDAnnotationWidget();
PDRectangle rect = new PDRectangle(50, 750, 250, 50);
widget1.setRectangle(rect);
widget1.setPage(page1);
widget1.getCOSObject().setItem(COSName.PARENT, textBox);
// Specify 2nd annotation associated with the field
PDAnnotationWidget widget2 = new PDAnnotationWidget();
PDRectangle rect2 = new PDRectangle(200, 650, 100, 50);
widget2.setRectangle(rect2);
widget2.setPage(page2);
widget2.getCOSObject().setItem(COSName.PARENT, textBox);
// set green border and yellow background for 1st widget
// if you prefer defaults, just delete this code block
PDAppearanceCharacteristicsDictionary fieldAppearance1
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fieldAppearance1.setBorderColour(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
fieldAppearance1.setBackground(new PDColor(new float[]{1,1,0}, PDDeviceRGB.INSTANCE));
widget1.setAppearanceCharacteristics(fieldAppearance1);
// set red border and green background for 2nd widget
// if you prefer defaults, just delete this code block
PDAppearanceCharacteristicsDictionary fieldAppearance2
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fieldAppearance2.setBorderColour(new PDColor(new float[]{1,0,0}, PDDeviceRGB.INSTANCE));
fieldAppearance2.setBackground(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
widget2.setAppearanceCharacteristics(fieldAppearance2);
List <PDAnnotationWidget> widgets = new ArrayList<PDAnnotationWidget>();
widgets.add(widget1);
widgets.add(widget2);
textBox.setWidgets(widgets);
// make sure the annotations are visible on screen and paper
widget1.setPrinted(true);
widget2.setPrinted(true);
// Add the annotations to the pages
page1.getAnnotations().add(widget1);
page2.getAnnotations().add(widget2);
// set the field value
textBox.setValue("Sample field");
document.save("MultiWidgetsForm.pdf");
document.close();
}
}

QGroupBox sizing with my QT5 custom widget

I am trying to make a custom widget: for displaying a processor register which has a name, a value and can be displayed in octal/decimal hexa. The code is shown at the bottom. I receive better result when I use the code as shown (i.e I insert QRadioButtons):
If I use
mainLayout->addWidget(DisplayMode);
instead (I guess this is the correct method) then the resulting picture is
Do I misunderstand something? What is wrong?
RegisterWidget::RegisterWidget(QWidget *parent)
:QFrame (parent)
{
mValue = 0;
mName = "";
setFrameStyle(QFrame::Panel | QFrame::Sunken);
QHBoxLayout *mainLayout = new QHBoxLayout(this);
label = new QLabel(tr("mName"),this);
label->setText(mName);
label->setLineWidth(2);
QGroupBox *DisplayMode = new QGroupBox("");
QRadioButton *OctalR = new QRadioButton(this);
QRadioButton *DecimalR = new QRadioButton(this);
DecimalR->setChecked(true); DecimalR->setDown(true);
QRadioButton *HexaR = new QRadioButton(this);
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(OctalR);
hbox->addWidget(DecimalR);
hbox->addWidget(HexaR);
hbox->addStretch(1);
DisplayMode->setLayout(hbox);
mainLayout->addWidget(label);
Value = new QLCDNumber(this);
Value->setDigitCount(8);
Value->setSegmentStyle(QLCDNumber::Flat);
Value->display(mValue);
mainLayout->addWidget(Value);
/* mainLayout->addWidget(DisplayMode);*/
mainLayout->addWidget(OctalR);
mainLayout->addWidget(DecimalR);
mainLayout->addWidget(HexaR);
setLineWidth(3);
setLayout(mainLayout);
connect(OctalR, SIGNAL(clicked()), this, SLOT(setOctal()));
connect(DecimalR, SIGNAL(clicked()), this, SLOT(setDecimal()));
connect(HexaR, SIGNAL(clicked()), this, SLOT(setHexa()));
}
Call QLayout::setContentsMargins() for both mainLayout and hbox. Try (3, 3, 3, 3) as parameters for a starting point and tweak. Layouts have default margins of 11 pixels on most platforms, according to the docs.

Expert Pdf - add footer on all pages except on the first page

I am using Expert PDF to generate PDF from HTML.
I have to generate footer on all pages except on the first page.
I tried with:
PdfConverter pdfConverter = new PdfConverter();
AddFooter(pdfConverter);
private void AddFooter(PdfConverter pdfConverter)
{
string thisPageURL = HttpContext.Current.Request.Url.AbsoluteUri;
string headerAndFooterHtmlUrl = thisPageURL.Substring(0, thisPageURL.LastIndexOf('/')) + "/HeaderAndFooterHtml.htm";
//enable footer
pdfConverter.PdfDocumentOptions.ShowFooter = true;
// set the footer height in points
pdfConverter.PdfFooterOptions.FooterHeight = 60;
//write the page number
pdfConverter.PdfFooterOptions.TextArea = new TextArea(0, 30, "This is page &p; of &P; ",
new System.Drawing.Font(new System.Drawing.FontFamily("Times New Roman"), 10, System.Drawing.GraphicsUnit.Point));
pdfConverter.PdfFooterOptions.TextArea.EmbedTextFont = true;
pdfConverter.PdfFooterOptions.TextArea.TextAlign = HorizontalTextAlign.Right;
// set the footer HTML area
pdfConverter.PdfFooterOptions.HtmlToPdfArea = new HtmlToPdfArea(0, 0, -1, pdfConverter.PdfFooterOptions.FooterHeight,
headerAndFooterHtmlUrl, 1024, -1);
pdfConverter.PdfFooterOptions.HtmlToPdfArea.FitHeight = true;
}
but this code generate footer on all pages.
Can someone give me idea or solution for this problem?
Thanks in advance!
Add below line in your code:
pdfConverter.PdfFooterOptions.ShowOnFirstPage = false;

Flex 3 - Enable context menu for text object under a transparent PNG

Here is the situation. In my app I have an overlay layer that is composed of a transparent PNG. I have replaced the hitarea for the png with a 1x1 image using the following code:
[Bindable]
[Embed(source = "/assets/1x1image.png")]
private var onexonebitmapClass:Class;
private function loadCompleteHandler(event:Event):void
{
// Create the bitmap
var onexonebitmap:BitmapData = new onexonebitmapClass().bitmapData;
var bitmap:Bitmap;
bitmap = event.target.content as Bitmap;
bitmap.smoothing = true;
var _hitarea:Sprite = createHitArea(onexonebitmap, 1);
var rect:flash.geom.Rectangle = _box.toFlexRectangle(sprite.width, sprite.height);
var drawnBox:Sprite = new FlexSprite();
bitmap.width = rect.width;
bitmap.height = rect.height;
bitmap.x = -loader.width / 2;
bitmap.y = -loader.height / 2;
bitmap.alpha = _alpha;
_hitarea.alpha = 0;
drawnBox.x = rect.x + rect.width / 2;
drawnBox.y = rect.y + rect.height / 2;
// Add the bitmap as a child to the drawnBox
drawnBox.addChild(bitmap);
// Rotate the object.
drawnBox.rotation = _rotation;
// Add the drawnBox to the sprite
sprite.addChild(drawnBox);
// Set the hitarea to drawnBox
drawnBox.hitArea = _hitarea;
}
private function createHitArea(bitmapData:BitmapData, grainSize:uint = 1):Sprite
{
var _hitarea:Sprite = new Sprite();
_hitarea.graphics.beginFill(0x900000, 1.0);
for (var x:uint = 0; x < bitmapData.width; x += grainSize)
{
for (var y:uint = grainSize; y < bitmapData.height; y += grainSize)
{
if (x <= bitmapData.width && y <= bitmapData.height && bitmapData.getPixel(x, y) != 0)
{
_hitarea.graphics.drawRect(x, y, grainSize, grainSize);
}
}
}
_hitarea.graphics.endFill();
return _hitarea;
}
This is based off the work done here: Creating a hitarea for PNG Image with transparent (alpha) regions in Flex
Using the above code I am able to basically ignore the overlay layer for all mouse events (click, double click, move, etc.) However, I am unable to capture the right click (context menu) event for items that are beneath the overlay.
For instance I have a spell check component that checks the spelling on any textitem and like most other spell checkers if the word is incorrect or not in the dictionary underlines the word in red and if you right click on it would give you a list of suggestions in the contextmenu. This is working great when the text box is not under the overlay, but if the text box is under the overlay I get nothing back.
If anyone can give me some pointers on how to capture the right click event on a textItem that is under a transparent png that would be great.