/*
* 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.interactive.annotation.handlers;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationSquareCircle;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceContentStream;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceEntry;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
/**
* Generic handler to generate the fields appearance.
*
* Individual handler will provide specific implementations for different field
* types.
*
*/
public abstract class PDAbstractAppearanceHandler implements PDAppearanceHandler
{
private PDAnnotation annotation;
private PDAppearanceEntry appearanceEntry;
private PDAppearanceContentStream contentStream;
public PDAbstractAppearanceHandler(PDAnnotation annotation)
{
this.annotation = annotation;
}
public abstract void generateNormalAppearance();
public abstract void generateRolloverAppearance();
public abstract void generateDownAppearance();
PDAnnotation getAnnotation()
{
return annotation;
}
PDColor getColor()
{
return annotation.getColor();
}
PDRectangle getRectangle()
{
return annotation.getRectangle();
}
/**
* Get the annotations appearance dictionary.
*
* <p>
* This will get the annotations appearance dictionary. If this is not
* existent an empty appearance dictionary will be created.
*
* @return the annotations appearance dictionary
*/
PDAppearanceDictionary getAppearance()
{
PDAppearanceDictionary appearanceDictionary = annotation.getAppearance();
if (appearanceDictionary == null)
{
appearanceDictionary = new PDAppearanceDictionary();
annotation.setAppearance(appearanceDictionary);
}
return appearanceDictionary;
}
/**
* Get the annotations normal appearance content stream.
*
* <p>
* This will get the annotations normal appearance content stream,
* to 'draw' to.
*
* @return the appearance entry representing the normal appearance.
* @throws IOException
*/
PDAppearanceContentStream getNormalAppearanceAsContentStream() throws IOException
{
appearanceEntry = getNormalAppearance();
contentStream = getAppearanceEntryAsContentStream(appearanceEntry);
return contentStream;
}
/**
* Get the annotations down appearance.
*
* <p>
* This will get the annotations down appearance. If this is not existent an
* empty appearance entry will be created.
*
* @return the appearance entry representing the down appearance.
*/
PDAppearanceEntry getDownAppearance()
{
PDAppearanceDictionary appearanceDictionary = getAppearance();
PDAppearanceEntry appearanceEntry = appearanceDictionary.getDownAppearance();
if (appearanceEntry.isSubDictionary())
{
appearanceEntry = new PDAppearanceEntry(new COSStream());
appearanceDictionary.setDownAppearance(appearanceEntry);
}
return appearanceEntry;
}
/**
* Get the annotations rollover appearance.
*
* <p>
* This will get the annotations rollover appearance. If this is not
* existent an empty appearance entry will be created.
*
* @return the appearance entry representing the rollover appearance.
*/
PDAppearanceEntry getRolloverAppearance()
{
PDAppearanceDictionary appearanceDictionary = getAppearance();
PDAppearanceEntry appearanceEntry = appearanceDictionary.getRolloverAppearance();
if (appearanceEntry.isSubDictionary())
{
appearanceEntry = new PDAppearanceEntry(new COSStream());
appearanceDictionary.setRolloverAppearance(appearanceEntry);
}
return appearanceEntry;
}
/**
* Set the differences rectangle.
*/
void setRectDifference(float lineWidth)
{
if (annotation instanceof PDAnnotationSquareCircle && lineWidth > 0)
{
PDRectangle differences = new PDRectangle(lineWidth/2, lineWidth/2,0,0);
((PDAnnotationSquareCircle) annotation).setRectDifference(differences);
}
}
/**
* Get a padded rectangle.
*
* <p>Creates a new rectangle with padding applied to each side.
* .
* @param rectangle the rectangle.
* @param padding the padding to apply.
* @return the padded rectangle.
*/
PDRectangle getPaddedRectangle(PDRectangle rectangle, float padding)
{
return new PDRectangle(rectangle.getLowerLeftX() + padding, rectangle.getLowerLeftY() + padding,
rectangle.getWidth() - 2 * padding, rectangle.getHeight() - 2 * padding);
}
void handleOpacity(float opacity) throws IOException
{
if (opacity < 1)
{
PDExtendedGraphicsState gs = new PDExtendedGraphicsState();
gs.setStrokingAlphaConstant(opacity);
gs.setNonStrokingAlphaConstant(opacity);
PDAppearanceStream appearanceStream = appearanceEntry.getAppearanceStream();
PDResources resources = appearanceStream.getResources();
if (resources == null)
{
resources = new PDResources();
appearanceStream.setResources(resources);
contentStream.setResources(resources);
}
contentStream.setGraphicsStateParameters(gs);
}
}
/**
* Get the annotations normal appearance.
*
* <p>
* This will get the annotations normal appearance. If this is not existent
* an empty appearance entry will be created.
*
* @return the appearance entry representing the normal appearance.
*/
private PDAppearanceEntry getNormalAppearance()
{
PDAppearanceDictionary appearanceDictionary = getAppearance();
PDAppearanceEntry appearanceEntry = appearanceDictionary.getNormalAppearance();
if (appearanceEntry.isSubDictionary())
{
appearanceEntry = new PDAppearanceEntry(new COSStream());
appearanceDictionary.setNormalAppearance(appearanceEntry);
}
return appearanceEntry;
}
private PDAppearanceContentStream getAppearanceEntryAsContentStream(PDAppearanceEntry appearanceEntry) throws IOException
{
PDAppearanceStream appearanceStream = appearanceEntry.getAppearanceStream();
setTransformationMatrix(appearanceStream);
return new PDAppearanceContentStream(appearanceStream);
}
private void setTransformationMatrix(PDAppearanceStream appearanceStream)
{
PDRectangle bbox = getRectangle();
appearanceStream.setBBox(bbox);
AffineTransform transform = AffineTransform.getTranslateInstance(-bbox.getLowerLeftX(),
-bbox.getLowerLeftY());
appearanceStream.setMatrix(transform);
}
}