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