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 <thaush...@t-online.de>
Sent: Thursday, November 7, 2019 12:13 PM
To: users@pdfbox.apache.org
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: users-unsubscr...@pdfbox.apache.org
For additional commands, e-mail: users-h...@pdfbox.apache.org

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: users-unsubscr...@pdfbox.apache.org
For additional commands, e-mail: users-h...@pdfbox.apache.org

Reply via email to