/*
* 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 com.tom_roush.pdfbox.pdmodel.interactive.form;
import com.tom_roush.pdfbox.contentstream.operator.Operator;
import com.tom_roush.pdfbox.cos.COSName;
import com.tom_roush.pdfbox.cos.COSNumber;
import com.tom_roush.pdfbox.cos.COSString;
import com.tom_roush.pdfbox.pdfparser.PDFStreamParser;
import com.tom_roush.pdfbox.pdmodel.PDPageContentStream;
import com.tom_roush.pdfbox.pdmodel.PDResources;
import com.tom_roush.pdfbox.pdmodel.font.PDFont;
import com.tom_roush.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;
/**
* Represents a default appearance string, as found in the /DA entry of free text annotations.
*
* <p>The default appearance string (DA) contains any graphics state or text state operators needed
* to establish the graphics state parameters, such as text size and colour, for displaying the
* field’s variable text. Only operators that are allowed within text objects shall occur in this
* string.
*
* Note: This class is not yet public, as its API is still unstable.
*/
class PDAppearanceString
{
private final List<Object> tokens;
private final PDResources defaultResources;
/**
* Constructor for reading an existing DA string.
*
* @param defaultResources DR entry
* @param defaultAppearance DA entry
* @throws IOException If the DA could not be parsed
*/
PDAppearanceString(COSString defaultAppearance, PDResources defaultResources) throws IOException
{
if (defaultAppearance == null)
{
throw new IllegalArgumentException("/DA is a required entry");
}
if (defaultResources == null)
{
throw new IllegalArgumentException("/DR is a required entry");
}
ByteArrayInputStream stream = new ByteArrayInputStream(defaultAppearance.getBytes());
PDFStreamParser parser = new PDFStreamParser(defaultAppearance.getBytes());
parser.parse();
tokens = parser.getTokens();
this.defaultResources = defaultResources;
}
/**
* Returns the font size.
*/
public float getFontSize()
{
if (!tokens.isEmpty())
{
// daString looks like "BMC /Helv 3.4 Tf EMC"
// use the fontsize of the default existing apperance stream
int fontIndex = tokens.indexOf(Operator.getOperator("Tf"));
if (fontIndex != -1)
{
return ((COSNumber) tokens.get(fontIndex - 1)).floatValue();
}
}
// default font size is 12 in Acrobat
return 12;
}
/**
* w in an appearance stream represents the lineWidth.
*
* @return the linewidth
*/
public float getLineWidth()
{
float retval = 0f;
if (tokens != null)
{
int btIndex = tokens.indexOf(Operator.getOperator("BT"));
int wIndex = tokens.indexOf(Operator.getOperator("w"));
// the w should only be used if it is before the first BT.
if (wIndex > 0 && (wIndex < btIndex || btIndex == -1))
{
retval = ((COSNumber) tokens.get(wIndex - 1)).floatValue();
}
}
return retval;
}
/**
* Returns the font.
*
* @throws IOException If the font could not be found.
*/
public PDFont getFont() throws IOException
{
COSName name = getFontResourceName();
PDFont font = defaultResources.getFont(name);
// todo: handle cases where font == null with special mapping logic (see PDFBOX-2661)
if (font == null)
{
throw new IOException("Could not find font: /" + name.getName());
}
return font;
}
/**
* Returns the name of the font in the Resources.
*/
private COSName getFontResourceName()
{
int setFontOperatorIndex = tokens.indexOf(Operator.getOperator("Tf"));
return (COSName) tokens.get(setFontOperatorIndex - 2);
}
/**
* Writes the DA string to the given content stream.
*/
void writeTo(PDPageContentStream contents, float zeroFontSize) throws IOException
{
float fontSize = getFontSize();
if (fontSize == 0)
{
fontSize = zeroFontSize;
}
contents.setFont(getFont(), fontSize);
// todo: set more state...
}
/**
* Copies any needed resources from the document’s DR dictionary into the stream’s Resources
* dictionary. Resources with the same name shall be left intact.
*/
void copyNeededResourcesTo(PDAppearanceStream appearanceStream) throws IOException
{
// make sure we have resources
PDResources streamResources = appearanceStream.getResources();
if (streamResources == null)
{
streamResources = new PDResources();
appearanceStream.setResources(streamResources);
}
// fonts
COSName fontName = getFontResourceName();
if (streamResources.getFont(fontName) == null)
{
streamResources.put(fontName, getFont());
}
// todo: other kinds of resource...
}
}