I haven't looked at this code in a few years, so I am not in a position to
expound on it. I offer it in the possibility it suggests something. Caveat: I
seem to recall they wanted their radio buttons optionally to do some things
that did not seem radio-button-like to me.
private static void addRadiobuttonAtRect(PDDocument document, PDPage page,
PDAcroForm acroForm, String name,
Map<String, String> optionMap, PDRectangle rect, GroupInfo groupInfo, String
dflt) {
PDAnnotationWidget radioButton = new PDAnnotationWidget();
String grp = Utils.unescapeChar(optionMap.get("grp"));
System.out.println("grp=" + grp + "; name=" + name + "; num=" +
optionMap.get("num"));
radioButton.setRectangle(rect);
radioButton.setPrinted(true);
int wd = Utils.getInt(optionMap.get("wd"));
if (wd == -1) {
System.out.println("Zero or invalid wd of radio button '" +
optionMap.get("wd") + "'");
wd = 8;
}
rect.setUpperRightX(rect.getLowerLeftX() + wd);
rect.setUpperRightY(rect.getLowerLeftY() + wd); // assume buttons are never
oblong/oval
boolean squareButtons = true;
String type = optionMap.get("btnshape");
if ((type != null) && type.equals("round")) {
squareButtons = false;
}
PDFormFieldAdditionalActions fieldActions = new
PDFormFieldAdditionalActions();
PDAnnotationAdditionalActions annotationActions = new
PDAnnotationAdditionalActions();
setWidgetActions(optionMap, fieldActions, annotationActions);
radioButton.setActions(annotationActions);
PDAppearanceCharacteristicsDictionary fieldAppearance
= new PDAppearanceCharacteristicsDictionary(new
COSDictionary());
if (Utils.getInt(optionMap.get("brdwt")) != 0) {
/*
* For borderWeight of zero to work reliably (and not exhibit a thin
rule),
* it is important not so set any border color.
*/
fieldAppearance.setBorderColour(Utils.hexToPDColor(optionMap.get("brdclr")));
}
fieldAppearance.setBackground(Utils.hexToPDColor(optionMap.get("bclr")));
radioButton.setAnnotationFlags(4);
radioButton.setPage(page);
PDRadioButton radioGroupWidget = (PDRadioButton)
groupInfo.getGroupButton();
radioButton.setParent(radioGroupWidget);
COSDictionary apNDict = new COSDictionary();
COSDictionary apDDict = new COSDictionary();
PDAppearanceDictionary appearance = new PDAppearanceDictionary();
PDAppearanceEntry appearanceNEntry;
PDAppearanceEntry appearanceDEntry;
COSStream offNStream = new COSStream();
offNStream.setItem(COSName.BBOX, rect);
offNStream.setItem(COSName.FORMTYPE, COSInteger.ONE);
offNStream.setItem(COSName.TYPE, COSName.XOBJECT);
offNStream.setItem(COSName.SUBTYPE, COSName.FORM);
apNDict.setItem(COSName.Off, offNStream);
COSStream onNStream = new COSStream();
onNStream.setItem(COSName.BBOX, rect);
onNStream.setItem(COSName.FORMTYPE, COSInteger.ONE);
onNStream.setItem(COSName.TYPE, COSName.XOBJECT);
onNStream.setItem(COSName.SUBTYPE, COSName.FORM);
apNDict.setItem(name, onNStream);
COSStream offDStream = new COSStream();
offDStream.setItem(COSName.BBOX, rect);
offDStream.setItem(COSName.FORMTYPE, COSInteger.ONE);
offDStream.setItem(COSName.TYPE, COSName.XOBJECT);
offDStream.setItem(COSName.SUBTYPE, COSName.FORM);
apDDict.setItem(COSName.Off, offDStream);
COSStream onDStream = new COSStream();
onDStream.setItem(COSName.BBOX, rect);
onDStream.setItem(COSName.FORMTYPE, COSInteger.ONE);
onDStream.setItem(COSName.TYPE, COSName.XOBJECT);
onDStream.setItem(COSName.SUBTYPE, COSName.FORM);
apDDict.setItem(name, onDStream);
if ((dflt != null) && dflt.equals("Yes")) {
//radioButton.setAppearanceState(name);
} else {
radioButton.setAppearanceState("Off");
}
radioButton.setAppearance(appearance);
radioButton.setAppearanceCharacteristics(fieldAppearance);
appearanceNEntry = new PDAppearanceEntry(apNDict);
appearanceDEntry = new PDAppearanceEntry(apDDict);
appearance.setNormalAppearance(appearanceNEntry);
appearance.setDownAppearance(appearanceDEntry);
if (squareButtons) {
/* The following controls what appears within a selected square.
* If applied to round buttons, they become square. */
// 8 = cross; 4 = checkmark; H = star; u = diamond; n = square, l = dot
fieldAppearance.setNormalCaption("4");
try {
apNDict.setItem(COSName.Off, createRadioButtonAppearanceStream(document,
radioButton, false));
apNDict.setItem(COSName.YES, createRadioButtonAppearanceStream(document,
radioButton, true));
} catch (IOException exc) {
System.out.println("IOException calling createRadioButtonAppearanceStream: " +
exc.getMessage());
}
}
// Add the annotation to the group.
groupInfo.addItem(radioButton);
try {
page.getAnnotations().add(radioButton);
} catch (IOException exc) {
System.out.println("IOException adding readioButton to page: " +
exc.getMessage());
}
}
private static PDAppearanceStream createRadioButtonAppearanceStream (final
PDDocument document, PDAnnotationWidget widget, boolean on) throws IOException {
PDRectangle rect = widget.getRectangle();
PDAppearanceCharacteristicsDictionary appearanceCharacteristics;
PDAppearanceStream yesAP = new PDAppearanceStream(document);
yesAP.setBBox(new PDRectangle(rect.getWidth(), rect.getHeight()));
yesAP.setResources(new PDResources());
try (PDAppearanceContentStream yesAPCS = new
PDAppearanceContentStream(yesAP)) {
appearanceCharacteristics = widget.getAppearanceCharacteristics();
PDColor backgroundColor = appearanceCharacteristics.getBackground();
PDColor borderColor = appearanceCharacteristics.getBorderColour();
float lineWidth = 1.0F;
yesAPCS.setBorderLine(lineWidth, widget.getBorderStyle(),
widget.getBorder());
yesAPCS.setNonStrokingColor(backgroundColor);
yesAPCS.addRect(0, 0, rect.getWidth(), rect.getHeight());
yesAPCS.fill();
if (borderColor != null) {
yesAPCS.setStrokingColor(borderColor);
}
yesAPCS.addRect(lineWidth / 2, lineWidth / 2, rect.getWidth() -
lineWidth, rect.getHeight() - lineWidth);
yesAPCS.stroke();
if (!on) {
return yesAP;
}
yesAPCS.addRect(lineWidth, lineWidth, rect.getWidth() - lineWidth *
2, rect.getHeight() - lineWidth * 2);
yesAPCS.clip();
String normalCaption = appearanceCharacteristics.getNormalCaption();
if (normalCaption == null) {
normalCaption = "4"; // Adobe behaviour
}
if ("8".equals(normalCaption)) {
// Adobe paints a cross instead of using the Zapf Dingbats
cross symbol
yesAPCS.setStrokingColor(0f);
yesAPCS.moveTo(lineWidth * 2, rect.getHeight() - lineWidth * 2);
yesAPCS.lineTo(rect.getWidth() - lineWidth * 2, lineWidth * 2);
yesAPCS.moveTo(rect.getWidth() - lineWidth * 2,
rect.getHeight() - lineWidth * 2);
yesAPCS.lineTo(lineWidth * 2, lineWidth * 2);
yesAPCS.stroke();
} else {
// The caption is not unicode, but the Zapf Dingbats code in
the PDF
// Thus convert it back to unicode
// Assume that only the first character is used.
String name =
PDType1Font.ZAPF_DINGBATS.codeToName(normalCaption.codePointAt(0));
String unicode =
PDType1Font.ZAPF_DINGBATS.getGlyphList().toUnicode(name);
Rectangle2D bounds =
PDType1Font.ZAPF_DINGBATS.getPath(name).getBounds2D();
float size = (float) Math.min(bounds.getWidth(),
bounds.getHeight()) / 1000;
// assume that checkmark has square size
// the calculations approximate what Adobe is doing, i.e. put
the glyph in the middle
float fontSize = (rect.getWidth() - lineWidth * 2) / size *
0.6666f;
float xOffset = (float) (rect.getWidth() - (bounds.getWidth())
/ 1000 * fontSize) / 2;
xOffset -= bounds.getX() / 1000 * fontSize;
float yOffset = (float) (rect.getHeight() -
(bounds.getHeight()) / 1000 * fontSize) / 2;
yOffset -= bounds.getY() / 1000 * fontSize;
yesAPCS.setNonStrokingColor(0);
yesAPCS.beginText();
yesAPCS.setFont(PDType1Font.ZAPF_DINGBATS, fontSize);
yesAPCS.newLineAtOffset(xOffset, yOffset);
yesAPCS.showText(unicode);
yesAPCS.endText();
}
}
return yesAP;
}
-----Original Message-----
From: Tilman Hausherr <[email protected]>
Sent: Thursday, November 7, 2019 12:13 PM
To: [email protected]
Subject: Re: Unable to find documentation on using PDRadioButton.setValue with
regards to PDDocument.saveIncremental (selected appearance not show in PDF
viewer)
Am 07.11.2019 um 14:07 schrieb Jason Pyeron:
> Most of the form is saving and displaying just fine, since we
> carefully call setNeedToBeUpdated(true) after setting fields.
>
>
>
> Radio buttons seem to require a bit more. The actual value is there,
> but the display appearance is blank when going from Off to value, and
> the old value display is still shown when from going from old value to new
> value.
>
>
>
> Where is this properly documented? I have reviewed the examples,
> pdfbox website, stackoverflow, google, etc.
It's not documented, the whole incremental thing is hard to understand even for
me... I suspect you need to add "setNeedToBeUpdated(true)" on the appearance
state / dictionary too. Do a normal save and then look at your PDF with
PDFDebugger to see what's in the field / the annotation.
Then do an incrementalSave and look whether something is missing.
It may also be possible that you can profit from the experimental branch from
PDFBOX-45 (pdfbox/branches/issue45). See the comments around April 2019.
Tilman
>
>
>
> v/r,
>
>
>
> Jason
>
>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
The content of this email and any attached files are intended for the recipient
specified in this message only. It may contain information that is
confidential, proprietary, privileged, and/or exempt from disclosure under
applicable law. It is strictly forbidden to share any part of this message with
any third party or rely on any of its contents, without the written consent of
the sender. If you received this message by mistake, please reply to this
message and follow with deletion of the original message, any copies and all
attachments, so that Oberon Technologies can ensure such a mistake does not
occur in the future.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]