2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package org.apache.pdfbox.pdmodel.graphics.optionalcontent;
19 import java.util.Collection;
21 import org.apache.pdfbox.cos.COSArray;
22 import org.apache.pdfbox.cos.COSBase;
23 import org.apache.pdfbox.cos.COSDictionary;
24 import org.apache.pdfbox.cos.COSName;
25 import org.apache.pdfbox.cos.COSObject;
26 import org.apache.pdfbox.pdmodel.common.COSObjectable;
29 * This class represents the optional content properties dictionary.
34 public class PDOptionalContentProperties implements COSObjectable
38 * Enumeration for the BaseState dictionary entry on the "D" dictionary.
40 public static enum BaseState
43 /** The "ON" value. */
45 /** The "OFF" value. */
47 /** The "Unchanged" value. */
48 UNCHANGED(COSName.UNCHANGED);
52 private BaseState(COSName value)
58 * Returns the PDF name for the state.
59 * @return the name of the state
61 public COSName getName()
67 * Returns the base state represented by the given {@link COSName}.
68 * @param state the state name
69 * @return the state enum value
71 public static BaseState valueOf(COSName state)
77 return BaseState.valueOf(state.getName().toUpperCase());
82 private COSDictionary dict;
85 * Creates a new optional content properties dictionary.
87 public PDOptionalContentProperties()
89 this.dict = new COSDictionary();
90 this.dict.setItem(COSName.OCGS, new COSArray());
91 this.dict.setItem(COSName.D, new COSDictionary());
95 * Creates a new instance based on a given {@link COSDictionary}.
96 * @param props the dictionary
98 public PDOptionalContentProperties(COSDictionary props)
104 public COSBase getCOSObject()
109 private COSArray getOCGs()
111 COSArray ocgs = (COSArray)this.dict.getItem(COSName.OCGS);
114 ocgs = new COSArray();
115 this.dict.setItem(COSName.OCGS, ocgs); //OCGs is required
120 private COSDictionary getD()
122 COSDictionary d = (COSDictionary)this.dict.getDictionaryObject(COSName.D);
125 d = new COSDictionary();
126 this.dict.setItem(COSName.D, d); //D is required
132 * Returns the optional content group of the given name.
133 * @param name the group name
134 * @return the optional content group or null, if there is no such group
136 public PDOptionalContentGroup getGroup(String name)
138 COSArray ocgs = getOCGs();
139 for (COSBase o : ocgs)
141 COSDictionary ocg = toDictionary(o);
142 String groupName = ocg.getString(COSName.NAME);
143 if (groupName.equals(name))
145 return new PDOptionalContentGroup(ocg);
152 * Adds an optional content group (OCG).
153 * @param ocg the optional content group
155 public void addGroup(PDOptionalContentGroup ocg)
157 COSArray ocgs = getOCGs();
158 ocgs.add(ocg.getCOSObject());
160 //By default, add new group to the "Order" entry so it appears in the user interface
161 COSArray order = (COSArray)getD().getDictionaryObject(COSName.ORDER);
164 order = new COSArray();
165 getD().setItem(COSName.ORDER, order);
171 * Returns the collection of all optional content groups.
172 * @return the optional content groups
174 public Collection<PDOptionalContentGroup> getOptionalContentGroups()
176 Collection<PDOptionalContentGroup> coll = new java.util.ArrayList<PDOptionalContentGroup>();
177 COSArray ocgs = getOCGs();
178 for (COSBase base : ocgs)
180 COSObject obj = (COSObject)base; //Children must be indirect references
181 coll.add(new PDOptionalContentGroup((COSDictionary)obj.getObject()));
187 * Returns the base state for optional content groups.
188 * @return the base state
190 public BaseState getBaseState()
192 COSDictionary d = getD();
193 COSName name = (COSName)d.getItem(COSName.BASE_STATE);
194 return BaseState.valueOf(name);
198 * Sets the base state for optional content groups.
199 * @param state the base state
201 public void setBaseState(BaseState state)
203 COSDictionary d = getD();
204 d.setItem(COSName.BASE_STATE, state.getName());
208 * Lists all optional content group names.
209 * @return an array of all names
211 public String[] getGroupNames()
213 COSArray ocgs = (COSArray)dict.getDictionaryObject(COSName.OCGS);
214 int size = ocgs.size();
215 String[] groups = new String[size];
216 for (int i = 0; i < size; i++)
218 COSBase obj = (COSBase)ocgs.get(i);
219 COSDictionary ocg = toDictionary(obj);
220 groups[i] = ocg.getString(COSName.NAME);
226 * Indicates whether a particular optional content group is found in the PDF file.
227 * @param groupName the group name
228 * @return true if the group exists, false otherwise
230 public boolean hasGroup(String groupName)
232 String[] layers = getGroupNames();
233 for (String layer : layers)
235 if (layer.equals(groupName))
244 * Indicates whether an optional content group is enabled.
245 * @param groupName the group name
246 * @return true if the group is enabled
248 public boolean isGroupEnabled(String groupName)
250 //TODO handle Optional Content Configuration Dictionaries,
251 //i.e. OCProperties/Configs
253 COSDictionary d = getD();
254 COSArray on = (COSArray)d.getDictionaryObject(COSName.ON);
259 COSDictionary group = toDictionary(o);
260 String name = group.getString(COSName.NAME);
261 if (name.equals(groupName))
268 COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF);
271 for (COSBase o : off)
273 COSDictionary group = toDictionary(o);
274 String name = group.getString(COSName.NAME);
275 if (name.equals(groupName))
282 BaseState baseState = getBaseState();
283 boolean enabled = !baseState.equals(BaseState.OFF);
284 //TODO What to do with BaseState.Unchanged?
288 private COSDictionary toDictionary(COSBase o)
290 if (o instanceof COSObject)
292 return (COSDictionary)((COSObject)o).getObject();
296 return (COSDictionary)o;
301 * Enables or disables an optional content group.
302 * @param groupName the group name
303 * @param enable true to enable, false to disable
304 * @return true if the group already had an on or off setting, false otherwise
306 public boolean setGroupEnabled(String groupName, boolean enable)
308 COSDictionary d = getD();
309 COSArray on = (COSArray)d.getDictionaryObject(COSName.ON);
313 d.setItem(COSName.ON, on);
315 COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF);
318 off = new COSArray();
319 d.setItem(COSName.OFF, off);
322 boolean found = false;
325 COSDictionary group = toDictionary(o);
326 String name = group.getString(COSName.NAME);
327 if (!enable && name.equals(groupName))
336 for (COSBase o : off)
338 COSDictionary group = toDictionary(o);
339 String name = group.getString(COSName.NAME);
340 if (enable && name.equals(groupName))
352 PDOptionalContentGroup ocg = getGroup(groupName);
355 on.add(ocg.getCOSObject());
359 off.add(ocg.getCOSObject());