/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pdfbox.pdmodel.graphics.optionalcontent;
import java.util.Collection;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.pdmodel.common.COSObjectable;
/**
* This class represents the optional content properties dictionary.
*
* @since PDF 1.5
* @version $Revision$
*/
public class PDOptionalContentProperties implements COSObjectable
{
/**
* Enumeration for the BaseState dictionary entry on the "D" dictionary.
*/
public static enum BaseState
{
/** The "ON" value. */
ON(COSName.ON),
/** The "OFF" value. */
OFF(COSName.OFF),
/** The "Unchanged" value. */
UNCHANGED(COSName.UNCHANGED);
private COSName name;
private BaseState(COSName value)
{
this.name = value;
}
/**
* Returns the PDF name for the state.
* @return the name of the state
*/
public COSName getName()
{
return this.name;
}
/**
* Returns the base state represented by the given {@link COSName}.
* @param state the state name
* @return the state enum value
*/
public static BaseState valueOf(COSName state)
{
if (state == null)
{
return BaseState.ON;
}
return BaseState.valueOf(state.getName().toUpperCase());
}
}
private COSDictionary dict;
/**
* Creates a new optional content properties dictionary.
*/
public PDOptionalContentProperties()
{
this.dict = new COSDictionary();
this.dict.setItem(COSName.OCGS, new COSArray());
this.dict.setItem(COSName.D, new COSDictionary());
}
/**
* Creates a new instance based on a given {@link COSDictionary}.
* @param props the dictionary
*/
public PDOptionalContentProperties(COSDictionary props)
{
this.dict = props;
}
/** {@inheritDoc} */
public COSBase getCOSObject()
{
return this.dict;
}
private COSArray getOCGs()
{
COSArray ocgs = (COSArray)this.dict.getItem(COSName.OCGS);
if (ocgs == null)
{
ocgs = new COSArray();
this.dict.setItem(COSName.OCGS, ocgs); //OCGs is required
}
return ocgs;
}
private COSDictionary getD()
{
COSDictionary d = (COSDictionary)this.dict.getDictionaryObject(COSName.D);
if (d == null)
{
d = new COSDictionary();
this.dict.setItem(COSName.D, d); //D is required
}
return d;
}
/**
* Returns the optional content group of the given name.
* @param name the group name
* @return the optional content group or null, if there is no such group
*/
public PDOptionalContentGroup getGroup(String name)
{
COSArray ocgs = getOCGs();
for (COSBase o : ocgs)
{
COSDictionary ocg = toDictionary(o);
String groupName = ocg.getString(COSName.NAME);
if (groupName.equals(name))
{
return new PDOptionalContentGroup(ocg);
}
}
return null;
}
/**
* Adds an optional content group (OCG).
* @param ocg the optional content group
*/
public void addGroup(PDOptionalContentGroup ocg)
{
COSArray ocgs = getOCGs();
ocgs.add(ocg.getCOSObject());
//By default, add new group to the "Order" entry so it appears in the user interface
COSArray order = (COSArray)getD().getDictionaryObject(COSName.ORDER);
if (order == null)
{
order = new COSArray();
getD().setItem(COSName.ORDER, order);
}
order.add(ocg);
}
/**
* Returns the collection of all optional content groups.
* @return the optional content groups
*/
public Collection<PDOptionalContentGroup> getOptionalContentGroups()
{
Collection<PDOptionalContentGroup> coll = new java.util.ArrayList<PDOptionalContentGroup>();
COSArray ocgs = getOCGs();
for (COSBase base : ocgs)
{
COSObject obj = (COSObject)base; //Children must be indirect references
coll.add(new PDOptionalContentGroup((COSDictionary)obj.getObject()));
}
return coll;
}
/**
* Returns the base state for optional content groups.
* @return the base state
*/
public BaseState getBaseState()
{
COSDictionary d = getD();
COSName name = (COSName)d.getItem(COSName.BASE_STATE);
return BaseState.valueOf(name);
}
/**
* Sets the base state for optional content groups.
* @param state the base state
*/
public void setBaseState(BaseState state)
{
COSDictionary d = getD();
d.setItem(COSName.BASE_STATE, state.getName());
}
/**
* Lists all optional content group names.
* @return an array of all names
*/
public String[] getGroupNames()
{
COSArray ocgs = (COSArray)dict.getDictionaryObject(COSName.OCGS);
int size = ocgs.size();
String[] groups = new String[size];
for (int i = 0; i < size; i++)
{
COSBase obj = (COSBase)ocgs.get(i);
COSDictionary ocg = toDictionary(obj);
groups[i] = ocg.getString(COSName.NAME);
}
return groups;
}
/**
* Indicates whether a particular optional content group is found in the PDF file.
* @param groupName the group name
* @return true if the group exists, false otherwise
*/
public boolean hasGroup(String groupName)
{
String[] layers = getGroupNames();
for (String layer : layers)
{
if (layer.equals(groupName))
{
return true;
}
}
return false;
}
/**
* Indicates whether an optional content group is enabled.
* @param groupName the group name
* @return true if the group is enabled
*/
public boolean isGroupEnabled(String groupName)
{
//TODO handle Optional Content Configuration Dictionaries,
//i.e. OCProperties/Configs
COSDictionary d = getD();
COSArray on = (COSArray)d.getDictionaryObject(COSName.ON);
if (on != null)
{
for (COSBase o : on)
{
COSDictionary group = toDictionary(o);
String name = group.getString(COSName.NAME);
if (name.equals(groupName))
{
return true;
}
}
}
COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF);
if (off != null)
{
for (COSBase o : off)
{
COSDictionary group = toDictionary(o);
String name = group.getString(COSName.NAME);
if (name.equals(groupName))
{
return false;
}
}
}
BaseState baseState = getBaseState();
boolean enabled = !baseState.equals(BaseState.OFF);
//TODO What to do with BaseState.Unchanged?
return enabled;
}
private COSDictionary toDictionary(COSBase o)
{
if (o instanceof COSObject)
{
return (COSDictionary)((COSObject)o).getObject();
}
else
{
return (COSDictionary)o;
}
}
/**
* Enables or disables an optional content group.
* @param groupName the group name
* @param enable true to enable, false to disable
* @return true if the group already had an on or off setting, false otherwise
*/
public boolean setGroupEnabled(String groupName, boolean enable)
{
COSDictionary d = getD();
COSArray on = (COSArray)d.getDictionaryObject(COSName.ON);
if (on == null)
{
on = new COSArray();
d.setItem(COSName.ON, on);
}
COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF);
if (off == null)
{
off = new COSArray();
d.setItem(COSName.OFF, off);
}
boolean found = false;
for (COSBase o : on)
{
COSDictionary group = toDictionary(o);
String name = group.getString(COSName.NAME);
if (!enable && name.equals(groupName))
{
//disable group
on.remove(group);
off.add(group);
found = true;
break;
}
}
for (COSBase o : off)
{
COSDictionary group = toDictionary(o);
String name = group.getString(COSName.NAME);
if (enable && name.equals(groupName))
{
//enable group
off.remove(group);
on.add(group);
found = true;
break;
}
}
if (!found)
{
PDOptionalContentGroup ocg = getGroup(groupName);
if (enable)
{
on.add(ocg.getCOSObject());
}
else
{
off.add(ocg.getCOSObject());
}
}
return found;
}
}