/**
* Copyright (c) 2005, www.pdfbox.org
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of pdfbox; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://www.pdfbox.org
*
*/
package org.pdfbox.pdmodel.interactive.documentnavigation.outline;
import java.awt.Color;
import java.io.IOException;
import java.util.List;
import org.pdfbox.cos.COSArray;
import org.pdfbox.cos.COSDictionary;
import org.pdfbox.cos.COSFloat;
import org.pdfbox.exceptions.OutlineNotLocalException;
import org.pdfbox.pdmodel.PDDestinationNameTreeNode;
import org.pdfbox.pdmodel.PDDocument;
import org.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.pdfbox.pdmodel.PDPage;
import org.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
import org.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.pdfbox.pdmodel.interactive.action.type.PDAction;
import org.pdfbox.pdmodel.interactive.action.type.PDActionGoTo;
import org.pdfbox.pdmodel.interactive.action.PDActionFactory;
import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination;
import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
import org.pdfbox.util.BitFlagHelper;
/**
* This represents an outline in a pdf document.
*
* @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
* @version $Revision: 1.7 $
*/
public class PDOutlineItem extends PDOutlineNode
{
private static final int ITALIC_FLAG = 1;
private static final int BOLD_FLAG = 2;
/**
* Default Constructor.
*/
public PDOutlineItem()
{
super();
}
/**
* Constructor for an existing outline item.
*
* @param dic The storage dictionary.
*/
public PDOutlineItem( COSDictionary dic )
{
super( dic );
}
/**
* Insert a sibling after this node.
*
* @param item The item to insert.
*/
public void insertSiblingAfter( PDOutlineItem item )
{
item.setParent( getParent() );
PDOutlineItem next = getNextSibling();
setNextSibling( item );
item.setPreviousSibling( this );
if( next != null )
{
item.setNextSibling( next );
next.setPreviousSibling( item );
}
updateParentOpenCount( 1 );
}
/**
* Return the previous sibling or null if there is no sibling.
*
* @return The previous sibling.
*/
public PDOutlineItem getPreviousSibling()
{
PDOutlineItem last = null;
COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Prev" );
if( lastDic != null )
{
last = new PDOutlineItem( lastDic );
}
return last;
}
/**
* Set the previous sibling, this will be maintained by this class.
*
* @param outlineNode The new previous sibling.
*/
protected void setPreviousSibling( PDOutlineNode outlineNode )
{
node.setItem( "Prev", outlineNode );
}
/**
* Return the next sibling or null if there is no next sibling.
*
* @return The next sibling.
*/
public PDOutlineItem getNextSibling()
{
PDOutlineItem last = null;
COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Next" );
if( lastDic != null )
{
last = new PDOutlineItem( lastDic );
}
return last;
}
/**
* Set the next sibling, this will be maintained by this class.
*
* @param outlineNode The new next sibling.
*/
protected void setNextSibling( PDOutlineNode outlineNode )
{
node.setItem( "Next", outlineNode );
}
/**
* Get the title of this node.
*
* @return The title of this node.
*/
public String getTitle()
{
return node.getString( "Title" );
}
/**
* Set the title for this node.
*
* @param title The new title for this node.
*/
public void setTitle( String title )
{
node.setString( "Title", title );
}
/**
* Get the page destination of this node.
*
* @return The page destination of this node.
* @throws IOException If there is an error creating the destination.
*/
public PDDestination getDestination() throws IOException
{
return PDDestination.create( node.getDictionaryObject( "Dest" ) );
}
/**
* Set the page destination for this node.
*
* @param dest The new page destination for this node.
*/
public void setDestination( PDDestination dest )
{
node.setItem( "Dest", dest );
}
/**
* A convenience method that will create an XYZ destination using only the defaults.
*
* @param page The page to refer to.
*/
public void setDestination( PDPage page )
{
PDPageXYZDestination dest = null;
if( page != null )
{
dest = new PDPageXYZDestination();
dest.setPage( page );
}
setDestination( dest );
}
/**
* This method will attempt to find the page in this PDF document that this outline points to.
* If the outline does not point to anything then this method will return null. If the outline
* is an action that is not a GoTo action then this methods will throw the OutlineNotLocationException
*
* @param doc The document to get the page from.
*
* @return The page that this outline will go to when activated or null if it does not point to anything.
* @throws IOException If there is an error when trying to find the page.
*/
public PDPage findDestinationPage( PDDocument doc ) throws IOException
{
PDPage page = null;
PDDestination rawDest = getDestination();
if( rawDest == null )
{
PDAction outlineAction = getAction();
if( outlineAction instanceof PDActionGoTo )
{
rawDest = ((PDActionGoTo)outlineAction).getDestination();
}
else if( outlineAction == null )
{
//if the outline action is null then this outline does not refer
//to anything and we will just return null.
}
else
{
throw new OutlineNotLocalException( "Error: Outline does not reference a local page." );
}
}
PDPageDestination pageDest = null;
if( rawDest instanceof PDNamedDestination )
{
//if we have a named destination we need to lookup the PDPageDestination
PDNamedDestination namedDest = (PDNamedDestination)rawDest;
PDDocumentNameDictionary namesDict = doc.getDocumentCatalog().getNames();
if( namesDict != null )
{
PDDestinationNameTreeNode destsTree = namesDict.getDests();
if( destsTree != null )
{
pageDest = (PDPageDestination)destsTree.getValue( namedDest.getNamedDestination() );
}
}
}
else if( rawDest instanceof PDPageDestination)
{
pageDest = (PDPageDestination) rawDest;
}
else if( rawDest == null )
{
//if the destination is null then we will simply return a null page.
}
else
{
throw new IOException( "Error: Unknown destination type " + rawDest );
}
if( pageDest != null )
{
page = pageDest.getPage();
if( page == null )
{
int pageNumber = pageDest.getPageNumber();
if( pageNumber != -1 )
{
List allPages = doc.getDocumentCatalog().getAllPages();
page = (PDPage)allPages.get( pageNumber );
}
}
}
return page;
}
/**
* Get the action of this node.
*
* @return The action of this node.
*/
public PDAction getAction()
{
return PDActionFactory.createAction( (COSDictionary)node.getDictionaryObject( "A" ) );
}
/**
* Set the action for this node.
*
* @param action The new action for this node.
*/
public void setAction( PDAction action )
{
node.setItem( "A", action );
}
/**
* Get the structure element of this node.
*
* @return The structure element of this node.
*/
public PDStructureElement getStructureElement()
{
PDStructureElement se = null;
COSDictionary dic = (COSDictionary)node.getDictionaryObject( "SE" );
if( dic != null )
{
se = new PDStructureElement( dic );
}
return se;
}
/**
* Set the structure element for this node.
*
* @param structureElement The new structure element for this node.
*/
public void setStructuredElement( PDStructureElement structureElement )
{
node.setItem( "SE", structureElement );
}
/**
* Get the text color of this node. Default is black and this method
* will never return null.
*
* @return The structure element of this node.
*/
public PDColorSpaceInstance getTextColor()
{
PDColorSpaceInstance retval = null;
COSArray csValues = (COSArray)node.getDictionaryObject( "C" );
if( csValues == null )
{
csValues = new COSArray();
csValues.growToSize( 3, new COSFloat( 0 ) );
node.setItem( "C", csValues );
}
retval = new PDColorSpaceInstance(csValues);
retval.setColorSpace( PDDeviceRGB.INSTANCE );
return retval;
}
/**
* Set the text color for this node. The colorspace must be a PDDeviceRGB.
*
* @param textColor The text color for this node.
*/
public void setTextColor( PDColorSpaceInstance textColor )
{
node.setItem( "C", textColor.getCOSColorSpaceValue() );
}
/**
* Set the text color for this node. The colorspace must be a PDDeviceRGB.
*
* @param textColor The text color for this node.
*/
public void setTextColor( Color textColor )
{
COSArray array = new COSArray();
array.add( new COSFloat( textColor.getRed()/255f));
array.add( new COSFloat( textColor.getGreen()/255f));
array.add( new COSFloat( textColor.getBlue()/255f));
node.setItem( "C", array );
}
/**
* A flag telling if the text should be italic.
*
* @return The italic flag.
*/
public boolean isItalic()
{
return BitFlagHelper.getFlag( node, "F", ITALIC_FLAG );
}
/**
* Set the italic property of the text.
*
* @param italic The new italic flag.
*/
public void setItalic( boolean italic )
{
BitFlagHelper.setFlag( node, "F", ITALIC_FLAG, italic );
}
/**
* A flag telling if the text should be bold.
*
* @return The bold flag.
*/
public boolean isBold()
{
return BitFlagHelper.getFlag( node, "F", BOLD_FLAG );
}
/**
* Set the bold property of the text.
*
* @param bold The new bold flag.
*/
public void setBold( boolean bold )
{
BitFlagHelper.setFlag( node, "F", BOLD_FLAG, bold );
}
}