001/* 002 * $Id: RadioCheckField.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.IOException; 047 048import com.itextpdf.text.DocumentException; 049import com.itextpdf.text.ExceptionConverter; 050import com.itextpdf.text.Rectangle; 051 052/** 053 * Creates a radio or a check field. 054 * <p> 055 * Example usage: 056 * <p> 057 * <PRE> 058 * Document document = new Document(PageSize.A4, 50, 50, 50, 50); 059 * PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("output.pdf")); 060 * document.open(); 061 * PdfContentByte cb = writer.getDirectContent(); 062 * RadioCheckField bt = new RadioCheckField(writer, new Rectangle(100, 100, 200, 200), "radio", "v1"); 063 * bt.setCheckType(RadioCheckField.TYPE_CIRCLE); 064 * bt.setBackgroundColor(Color.cyan); 065 * bt.setBorderStyle(PdfBorderDictionary.STYLE_SOLID); 066 * bt.setBorderColor(Color.red); 067 * bt.setTextColor(Color.yellow); 068 * bt.setBorderWidth(BaseField.BORDER_WIDTH_THICK); 069 * bt.setChecked(false); 070 * PdfFormField f1 = bt.getRadioField(); 071 * bt.setOnValue("v2"); 072 * bt.setChecked(true); 073 * bt.setBox(new Rectangle(100, 300, 200, 400)); 074 * PdfFormField f2 = bt.getRadioField(); 075 * bt.setChecked(false); 076 * PdfFormField top = bt.getRadioGroup(true, false); 077 * bt.setOnValue("v3"); 078 * bt.setBox(new Rectangle(100, 500, 200, 600)); 079 * PdfFormField f3 = bt.getRadioField(); 080 * top.addKid(f1); 081 * top.addKid(f2); 082 * top.addKid(f3); 083 * writer.addAnnotation(top); 084 * bt = new RadioCheckField(writer, new Rectangle(300, 300, 400, 400), "check1", "Yes"); 085 * bt.setCheckType(RadioCheckField.TYPE_CHECK); 086 * bt.setBorderWidth(BaseField.BORDER_WIDTH_THIN); 087 * bt.setBorderColor(Color.black); 088 * bt.setBackgroundColor(Color.white); 089 * PdfFormField ck = bt.getCheckField(); 090 * writer.addAnnotation(ck); 091 * document.close(); 092 * </PRE> 093 * @author Paulo Soares 094 */ 095public class RadioCheckField extends BaseField { 096 097 /** A field with the symbol check */ 098 public static final int TYPE_CHECK = 1; 099 /** A field with the symbol circle */ 100 public static final int TYPE_CIRCLE = 2; 101 /** A field with the symbol cross */ 102 public static final int TYPE_CROSS = 3; 103 /** A field with the symbol diamond */ 104 public static final int TYPE_DIAMOND = 4; 105 /** A field with the symbol square */ 106 public static final int TYPE_SQUARE = 5; 107 /** A field with the symbol star */ 108 public static final int TYPE_STAR = 6; 109 110 private static String typeChars[] = {"4", "l", "8", "u", "n", "H"}; 111 112 /** 113 * Holds value of property checkType. 114 */ 115 private int checkType; 116 117 /** 118 * Holds value of property onValue. 119 */ 120 private String onValue; 121 122 /** 123 * Holds value of property checked. 124 */ 125 private boolean checked; 126 127 /** 128 * Creates a new instance of RadioCheckField 129 * @param writer the document <CODE>PdfWriter</CODE> 130 * @param box the field location and dimensions 131 * @param fieldName the field name. It must not be <CODE>null</CODE> 132 * @param onValue the value when the field is checked 133 */ 134 public RadioCheckField(PdfWriter writer, Rectangle box, String fieldName, String onValue) { 135 super(writer, box, fieldName); 136 setOnValue(onValue); 137 setCheckType(TYPE_CIRCLE); 138 } 139 140 /** 141 * Getter for property checkType. 142 * @return Value of property checkType. 143 */ 144 public int getCheckType() { 145 return this.checkType; 146 } 147 148 /** 149 * Sets the checked symbol. It can be 150 * <CODE>TYPE_CHECK</CODE>, 151 * <CODE>TYPE_CIRCLE</CODE>, 152 * <CODE>TYPE_CROSS</CODE>, 153 * <CODE>TYPE_DIAMOND</CODE>, 154 * <CODE>TYPE_SQUARE</CODE> and 155 * <CODE>TYPE_STAR</CODE>. 156 * @param checkType the checked symbol 157 */ 158 public void setCheckType(int checkType) { 159 if (checkType < TYPE_CHECK || checkType > TYPE_STAR) 160 checkType = TYPE_CIRCLE; 161 this.checkType = checkType; 162 setText(typeChars[checkType - 1]); 163 try { 164 setFont(BaseFont.createFont(BaseFont.ZAPFDINGBATS, BaseFont.WINANSI, false)); 165 } 166 catch (Exception e) { 167 throw new ExceptionConverter(e); 168 } 169 } 170 171 /** 172 * Getter for property onValue. 173 * @return Value of property onValue. 174 */ 175 public String getOnValue() { 176 return this.onValue; 177 } 178 179 /** 180 * Sets the value when the field is checked. 181 * @param onValue the value when the field is checked 182 */ 183 public void setOnValue(String onValue) { 184 this.onValue = onValue; 185 } 186 187 /** 188 * Getter for property checked. 189 * @return Value of property checked. 190 */ 191 public boolean isChecked() { 192 return this.checked; 193 } 194 195 /** 196 * Sets the state of the field to checked or unchecked. 197 * @param checked the state of the field, <CODE>true</CODE> for checked 198 * and <CODE>false</CODE> for unchecked 199 */ 200 public void setChecked(boolean checked) { 201 this.checked = checked; 202 } 203 204 /** 205 * Gets the field appearance. 206 * @param isRadio <CODE>true</CODE> for a radio field and <CODE>false</CODE> 207 * for a check field 208 * @param on <CODE>true</CODE> for the checked state, <CODE>false</CODE> 209 * otherwise 210 * @throws IOException on error 211 * @throws DocumentException on error 212 * @return the appearance 213 */ 214 public PdfAppearance getAppearance(boolean isRadio, boolean on) throws IOException, DocumentException { 215 if (isRadio && checkType == TYPE_CIRCLE) 216 return getAppearanceRadioCircle(on); 217 PdfAppearance app = getBorderAppearance(); 218 if (!on) 219 return app; 220 BaseFont ufont = getRealFont(); 221 boolean borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET; 222 float h = box.getHeight() - borderWidth * 2; 223 float bw2 = borderWidth; 224 if (borderExtra) { 225 h -= borderWidth * 2; 226 bw2 *= 2; 227 } 228 float offsetX = (borderExtra ? 2 * borderWidth : borderWidth); 229 offsetX = Math.max(offsetX, 1); 230 float offX = Math.min(bw2, offsetX); 231 float wt = box.getWidth() - 2 * offX; 232 float ht = box.getHeight() - 2 * offX; 233 float fsize = fontSize; 234 if (fsize == 0) { 235 float bw = ufont.getWidthPoint(text, 1); 236 if (bw == 0) 237 fsize = 12; 238 else 239 fsize = wt / bw; 240 float nfsize = h / (ufont.getFontDescriptor(BaseFont.ASCENT, 1)); 241 fsize = Math.min(fsize, nfsize); 242 } 243 app.saveState(); 244 app.rectangle(offX, offX, wt, ht); 245 app.clip(); 246 app.newPath(); 247 if (textColor == null) 248 app.resetGrayFill(); 249 else 250 app.setColorFill(textColor); 251 app.beginText(); 252 app.setFontAndSize(ufont, fsize); 253 app.setTextMatrix((box.getWidth() - ufont.getWidthPoint(text, fsize)) / 2, 254 (box.getHeight() - ufont.getAscentPoint(text, fsize)) / 2); 255 app.showText(text); 256 app.endText(); 257 app.restoreState(); 258 return app; 259 } 260 261 /** 262 * Gets the special field appearance for the radio circle. 263 * @param on <CODE>true</CODE> for the checked state, <CODE>false</CODE> 264 * otherwise 265 * @return the appearance 266 */ 267 public PdfAppearance getAppearanceRadioCircle(boolean on) { 268 PdfAppearance app = PdfAppearance.createAppearance(writer, box.getWidth(), box.getHeight()); 269 switch (rotation) { 270 case 90: 271 app.setMatrix(0, 1, -1, 0, box.getHeight(), 0); 272 break; 273 case 180: 274 app.setMatrix(-1, 0, 0, -1, box.getWidth(), box.getHeight()); 275 break; 276 case 270: 277 app.setMatrix(0, -1, 1, 0, 0, box.getWidth()); 278 break; 279 } 280 Rectangle box = new Rectangle(app.getBoundingBox()); 281 float cx = box.getWidth() / 2; 282 float cy = box.getHeight() / 2; 283 float r = (Math.min(box.getWidth(), box.getHeight()) - borderWidth) / 2; 284 if (r <= 0) 285 return app; 286 if (backgroundColor != null) { 287 app.setColorFill(backgroundColor); 288 app.circle(cx, cy, r + borderWidth / 2); 289 app.fill(); 290 } 291 if (borderWidth > 0 && borderColor != null) { 292 app.setLineWidth(borderWidth); 293 app.setColorStroke(borderColor); 294 app.circle(cx, cy, r); 295 app.stroke(); 296 } 297 if (on) { 298 if (textColor == null) 299 app.resetGrayFill(); 300 else 301 app.setColorFill(textColor); 302 app.circle(cx, cy, r / 2); 303 app.fill(); 304 } 305 return app; 306 } 307 308 /** 309 * Gets a radio group. It's composed of the field specific keys, without the widget 310 * ones. This field is to be used as a field aggregator with {@link PdfFormField#addKid(PdfFormField) addKid()}. 311 * @param noToggleToOff if <CODE>true</CODE>, exactly one radio button must be selected at all 312 * times; clicking the currently selected button has no effect. 313 * If <CODE>false</CODE>, clicking 314 * the selected button deselects it, leaving no button selected. 315 * @param radiosInUnison if <CODE>true</CODE>, a group of radio buttons within a radio button field that 316 * use the same value for the on state will turn on and off in unison; that is if 317 * one is checked, they are all checked. If <CODE>false</CODE>, the buttons are mutually exclusive 318 * (the same behavior as HTML radio buttons) 319 * @return the radio group 320 */ 321 public PdfFormField getRadioGroup(boolean noToggleToOff, boolean radiosInUnison) { 322 PdfFormField field = PdfFormField.createRadioButton(writer, noToggleToOff); 323 if (radiosInUnison) 324 field.setFieldFlags(PdfFormField.FF_RADIOSINUNISON); 325 field.setFieldName(fieldName); 326 if ((options & READ_ONLY) != 0) 327 field.setFieldFlags(PdfFormField.FF_READ_ONLY); 328 if ((options & REQUIRED) != 0) 329 field.setFieldFlags(PdfFormField.FF_REQUIRED); 330 field.setValueAsName(checked ? onValue : "Off"); 331 return field; 332 } 333 334 /** 335 * Gets the radio field. It's only composed of the widget keys and must be used 336 * with {@link #getRadioGroup(boolean,boolean)}. 337 * @return the radio field 338 * @throws IOException on error 339 * @throws DocumentException on error 340 */ 341 public PdfFormField getRadioField() throws IOException, DocumentException { 342 return getField(true); 343 } 344 345 /** 346 * Gets the check field. 347 * @return the check field 348 * @throws IOException on error 349 * @throws DocumentException on error 350 */ 351 public PdfFormField getCheckField() throws IOException, DocumentException { 352 return getField(false); 353 } 354 355 /** 356 * Gets a radio or check field. 357 * @param isRadio <CODE>true</CODE> to get a radio field, <CODE>false</CODE> to get 358 * a check field 359 * @throws IOException on error 360 * @throws DocumentException on error 361 * @return the field 362 */ 363 protected PdfFormField getField(boolean isRadio) throws IOException, DocumentException { 364 PdfFormField field = null; 365 if (isRadio) 366 field = PdfFormField.createEmpty(writer); 367 else 368 field = PdfFormField.createCheckBox(writer); 369 field.setWidget(box, PdfAnnotation.HIGHLIGHT_INVERT); 370 if (!isRadio) { 371 field.setFieldName(fieldName); 372 if ((options & READ_ONLY) != 0) 373 field.setFieldFlags(PdfFormField.FF_READ_ONLY); 374 if ((options & REQUIRED) != 0) 375 field.setFieldFlags(PdfFormField.FF_REQUIRED); 376 field.setValueAsName(checked ? onValue : "Off"); 377 setCheckType(TYPE_CHECK); 378 } 379 if (text != null) 380 field.setMKNormalCaption(text); 381 if (rotation != 0) 382 field.setMKRotation(rotation); 383 field.setBorderStyle(new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3))); 384 PdfAppearance tpon = getAppearance(isRadio, true); 385 PdfAppearance tpoff = getAppearance(isRadio, false); 386 field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, onValue, tpon); 387 field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, "Off", tpoff); 388 field.setAppearanceState(checked ? onValue : "Off"); 389 PdfAppearance da = (PdfAppearance)tpon.getDuplicate(); 390 da.setFontAndSize(getRealFont(), fontSize); 391 if (textColor == null) 392 da.setGrayFill(0); 393 else 394 da.setColorFill(textColor); 395 field.setDefaultAppearanceString(da); 396 if (borderColor != null) 397 field.setMKBorderColor(borderColor); 398 if (backgroundColor != null) 399 field.setMKBackgroundColor(backgroundColor); 400 switch (visibility) { 401 case HIDDEN: 402 field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN); 403 break; 404 case VISIBLE_BUT_DOES_NOT_PRINT: 405 break; 406 case HIDDEN_BUT_PRINTABLE: 407 field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW); 408 break; 409 default: 410 field.setFlags(PdfAnnotation.FLAGS_PRINT); 411 break; 412 } 413 return field; 414 } 415}