Thank Gary and Tilman, digesting the code right now. I am about 20+ hours in decomposing the differences of my output vs acrobat.
If I have anything re-usable, will make a pull request for examples. -JAson > -----Original Message----- > From: Gary Grosso <gary.gro...@oberontech.com> > Sent: Thursday, November 7, 2019 12:24 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) > > 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 > --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@pdfbox.apache.org For additional commands, e-mail: users-h...@pdfbox.apache.org