/*
* Copyright 2006-2017 ICEsoft Technologies Canada Corp.
*
* Licensed 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.icepdf.core.pobjects.annotations;
import org.icepdf.core.pobjects.*;
import org.icepdf.core.pobjects.graphics.GraphicsState;
import org.icepdf.core.util.Library;
import java.util.HashMap;
/**
* As mentioned in 12.5.2, "Annotation Dictionaries," the meaning of an
* annotation’s Contents entry varies by annotation type. Typically, it is the
* text that shall be displayed for the annotation or, if the annotation does not
* display text, an alternate description of the annotation’s contents in
* human-readable form. In either case, the Contents entry is useful when
* extracting the document’s contents in support of accessibility to users with
* disabilities or for other purposes (see 14.9.3, "Alternate Descriptions").
* <br>
* Many annotation types are defined as markup annotations because they are used
* primarily to mark up PDF documents (see Table 170). These annotations have
* text that appears as part of the annotation and may be displayed in other ways
* by a conforming reader, such as in a Comments pane.
* <br>
* Markup annotations may be divided into the following groups:
* <ul>
* <li>Free text annotations display text directly on the page. The annotation’s
* Contents entry specifies the displayed text.</li>
* <li>Most other markup annotations have an associated pop-up window that may
* contain text. The annotation’s Contents entry specifies the text that shall
* be displayed when the pop-up window is opened. These include text, line,
* square, circle, polygon, polyline, highlight, underline, squiggly-underline,
* strikeout, rubber stamp, caret, ink, and file attachment annotations. </li>
* <li>Sound annotations do not have a pop-up window but may also have associated
* text specified by the Contents entry.</li>
* </ul>
*
* @since 5.0
*/
public abstract class MarkupAnnotation extends Annotation {
/**
* Optional; PDF 1.1) The text label that shall be displayed in the title bar
* of the annotation’s pop-up window when open and active. This entry shall
* identify the user who added the annotation.
*/
public static final Name T_KEY = new Name("T");
/**
* (Optional; PDF 1.4) The constant opacity value that shall be used in
* painting the annotation (see Sections 11.2, "Overview of Transparency,"
* and 11.3.7, "Shape and Opacity Computations"). This value shall apply to
* all visible elements of the annotation in its closed state (including its
* background and border) but not to the pop-up window that appears when the
* annotation is opened.
*/
public static final Name CA_KEY = new Name("CA");
/**
* (Optional; PDF 1.5) A rich text string (see 12.7.3.4, "Rich Text Strings")
* that shall be displayed in the pop-up window when the annotation is opened.
*/
public static final Name RC_KEY = new Name("RC");
/**
* (Optional; PDF 1.5) The date and time (7.9.4, "Dates") when the
* annotation was created.
*/
public static final Name CREATION_DATE_KEY = new Name("CreationDate");
/**
* (Required if an RT entry is present, otherwise optional; PDF 1.5) A
* reference to the annotation that this annotation is "in reply to." Both
* annotations shall be on the same page of the document. The relationship
* between the two annotations shall be specified by the RT entry.
* <br>
* If this entry is present in an FDF file (see 12.7.7, "Forms Data Format"),
* its type shall not be a dictionary but a text string containing the
* contents of the NM entry of the annotation being replied to, to allow for
* a situation where the annotation being replied to is not in the same FDF
* file.
*/
public static final Name IRT_KEY = new Name("IRT");
/**
* (Optional; PDF 1.5) Text representing a short description of the subject
* being addressed by the annotation.
*/
public static final Name SUBJ_KEY = new Name("Subj");
/**
* (Optional; PDF 1.3) An indirect reference to a pop-up annotation for
* entering or editing the text associated with this annotation.
*/
public static final Name POPUP_KEY = new Name("Popup");
/**
* Optional; meaningful only if IRT is present; PDF 1.6) A name specifying
* the relationship (the "reply type") between this annotation and one
* specified by IRT. Valid values are:
* <br>
* R - The annotation shall be considered a reply to the annotation specified
* by IRT. Conforming readers shall not display replies to an annotation
* individually but together in the form of threaded comments.
* <br>
* Group - The annotation shall be grouped with the annotation specified by
* IRT; see the discussion following this Table.
* <br>
* Default value: R.
*/
public static final Name RT_KEY = new Name("RT");
/**
* (Optional; PDF 1.6) A name describing the intent of the markup annotation.
* Intents allow conforming readers to distinguish between different uses
* and behaviors of a single markup annotation type. If this entry is not
* present or its value is the same as the annotation type, the annotation
* shall have no explicit intent and should behave in a generic manner in a
* conforming reader.
* <br>
* Free text annotations (Table 174), line annotations (Table 175), polygon
* annotations (Table 178), and (PDF 1.7) polyline annotations (Table 178)
* have defined intents, whose values are enumerated in the corresponding
* tables.
*/
public static final Name IT_KEY = new Name("IT");
/**
* (Optional; PDF 1.7) An external data dictionary specifying data that shall
* be associated with the annotation. This dictionary contains the following
* entries:
* <br>
* Type - (optional) If present, shall be ExData.
* <br>
* Subtype - (required) a name specifying the type of data that the markup
* annotation shall be associated with. The only defined value is Markup3D.
* Table 298 lists the values that correspond to a subtype of Markup3D.
*/
public static final Name EX_DATA_KEY = new Name("ExData");
/**
* Named graphics state name used to store transparency values.
*/
public static final Name EXT_GSTATE_NAME = new Name("ip1");
protected String titleText;
protected PopupAnnotation popupAnnotation;
protected float opacity = 1.0f;
protected String richText;
protected PDate creationDate;
protected MarkupAnnotation inReplyToAnnotation;
protected String subject;
protected Name replyToRelation = new Name("R");
protected Name intent;
// exData not implemented
public MarkupAnnotation(Library l, HashMap h) {
super(l, h);
}
public void init() throws InterruptedException {
super.init();
// title text
titleText = getString(T_KEY);
// rich text
richText = getString(RC_KEY);
// subject text
subject = getString(SUBJ_KEY);
// creation date
Object value = library.getObject(entries, CREATION_DATE_KEY);
if (value != null && value instanceof StringObject) {
creationDate = new PDate(securityManager, getString(CREATION_DATE_KEY));
}
// popup child
value = library.getObject(entries, POPUP_KEY);
if (value != null && value instanceof PopupAnnotation) {
popupAnnotation = (PopupAnnotation) value;
}
// opacity
float ca = library.getFloat(entries, CA_KEY);
if (ca != 0.0f) {
opacity = ca;
}
// in reply to annotation
value = library.getObject(entries, IRT_KEY);
if (value != null && value instanceof MarkupAnnotation) {
inReplyToAnnotation = (MarkupAnnotation) value;
}
// in reply to annotation
value = library.getName(entries, RT_KEY);
if (value != null) {
replyToRelation = (Name) value;
}
// intent of annotation
value = library.getName(entries, IT_KEY);
if (value != null) {
intent = (Name) value;
}
}
public String getTitleText() {
return titleText;
}
public PopupAnnotation getPopupAnnotation() {
return popupAnnotation;
}
protected static void generateExternalGraphicsState(Form form, float opacity) {
// add the transparency graphic context settings.
if (form != null) {
Resources resources = form.getResources();
HashMap<Object, Object> graphicsProperties = new HashMap<Object, Object>(2);
HashMap<Object, Object> graphicsState = new HashMap<Object, Object>(1);
graphicsProperties.put(GraphicsState.CA_STROKING_KEY, opacity);
graphicsProperties.put(GraphicsState.CA_NON_STROKING_KEY, opacity);
graphicsState.put(EXT_GSTATE_NAME, graphicsProperties);
resources.getEntries().put(Resources.EXTGSTATE_KEY, graphicsState);
form.setResources(resources);
}
}
/**
* Gets the opacity value for a markup annotation. This value can be optionally used to apply a global
* opacity value when painting or regenerating the contentStream.
*
* @return current opacity value in the range of 0.0 ... 1.0
*/
public float getOpacity() {
return opacity;
}
/**
* Get the opacity value in the range of 0 ... 255.
*
* @return current opacity value in the range of 0 ... 255.
*/
public int getOpacityNormalized() {
return Math.round(opacity * 255);
}
/**
* Set the opacity value of the /CA key in the markup annotation dictionary.
*
* @param opacity opacity in the range of 0.0 ... 1.0.
*/
public void setOpacity(float opacity) {
if (this.opacity >= 0 && this.opacity <= 1.0) {
this.opacity = opacity;
entries.put(CA_KEY, this.opacity);
}
}
/**
* Set the opacity value of the /CA key in the markup annotation dictionary.
*
* @param opacity opacity in the range of 0 ... 255.
*/
public void setOpacity(int opacity) {
if (this.opacity >= 0 && this.opacity <= 255) {
this.opacity = Math.round(opacity / 2.55f) / 100.0f;
entries.put(CA_KEY, this.opacity);
}
}
public String getRichText() {
return richText;
}
public PDate getCreationDate() {
return creationDate;
}
public MarkupAnnotation getInReplyToAnnotation() {
return inReplyToAnnotation;
}
public String getSubject() {
return subject;
}
public Name getReplyToRelation() {
return replyToRelation;
}
public Name getIntent() {
return intent;
}
public void setTitleText(String titleText) {
this.titleText = setString(T_KEY, titleText);
}
public void setPopupAnnotation(PopupAnnotation popupAnnotation) {
this.popupAnnotation = popupAnnotation;
entries.put(POPUP_KEY, popupAnnotation.getPObjectReference());
}
public void setRichText(String richText) {
this.richText = setString(RC_KEY, richText);
}
public void setCreationDate(String creationDate) {
this.creationDate = new PDate(securityManager, creationDate);
setString(CREATION_DATE_KEY, creationDate);
}
public void setInReplyToAnnotation(MarkupAnnotation inReplyToAnnotation) {
this.inReplyToAnnotation = inReplyToAnnotation;
entries.put(IRT_KEY, inReplyToAnnotation.getPObjectReference());
}
public void setSubject(String subject) {
this.subject = setString(SUBTYPE_KEY, subject);
}
public String toString() {
return getTitleText();
}
}