Skip to content

Commit

Permalink
Merge pull request #603 from veraPDF/pdfua2_annotation
Browse files Browse the repository at this point in the history
PDF/UA-2. Add new annotations methods
  • Loading branch information
MaximPlusov authored Oct 31, 2023
2 parents a7fbafd + 704035c commit 8fc1190
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class StaticContainers {

private static final ThreadLocal<Set<COSKey>> fileSpecificationKeys = new ThreadLocal<>();

private static final ThreadLocal<Map<COSKey, Set<COSKey>>> destinationToStructParentsMap = new ThreadLocal<>();

private static final ThreadLocal<Stack<COSKey>> transparencyVisitedContentStreams = new ThreadLocal<>();
private static final ThreadLocal<Boolean> validPDF = new ThreadLocal<>();

Expand All @@ -75,6 +77,7 @@ public static void clearAllContainers() {
cachedColorSpaces.set(new HashMap<>());
cachedFonts.set(new HashMap<>());
fileSpecificationKeys.set(new HashSet<>());
destinationToStructParentsMap.set(new HashMap<>());
transparencyVisitedContentStreams.set(new Stack<>());
cachedGlyphs.set(new HashMap<>());
noteIDSet.set(new HashSet<>());
Expand Down Expand Up @@ -125,6 +128,17 @@ public static void setInconsistentSeparations(List<String> inconsistentSeparatio
StaticContainers.inconsistentSeparations.set(inconsistentSeparations);
}

public static Map<COSKey, Set<COSKey>> getDestinationToStructParentsMap() {
if (destinationToStructParentsMap.get() == null) {
destinationToStructParentsMap.set(new HashMap<>());
}
return destinationToStructParentsMap.get();
}

public static void setDestinationToStructParentsMap(Map<COSKey, Set<COSKey>> destinationToStructParentsMap) {
StaticContainers.destinationToStructParentsMap.set(destinationToStructParentsMap);
}

public static Map<String, PDColorSpace> getCachedColorSpaces() {
if (cachedColorSpaces.get() == null) {
cachedColorSpaces.set(new HashMap<>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ private static boolean isValidPdfaStream(final InputStream toValidate, final PDF
private Set<String> noteIDSet;
private Set<COSKey> xFormKeysSet;
private Set<COSKey> fileSpecificationKeys;
private Map<COSKey, Set<COSKey>> destinationToStructParentsMap;
private Stack<COSKey> transparencyVisitedContentStreams;
private Map<String, PDFont> cachedPDFonts;
private Map<String, Map<String, Glyph>> cachedGlyphs;
Expand All @@ -164,6 +165,7 @@ private void saveStaticContainersState() {
this.cachedColorSpaces = StaticContainers.getCachedColorSpaces();
this.cachedPDFonts = StaticContainers.getCachedFonts();
this.fileSpecificationKeys = StaticContainers.getFileSpecificationKeys();
this.destinationToStructParentsMap = StaticContainers.getDestinationToStructParentsMap();
this.noteIDSet = StaticContainers.getNoteIDSet();
this.xFormKeysSet = StaticContainers.getXFormKeysSet();
this.transparencyVisitedContentStreams = StaticContainers.getTransparencyVisitedContentStreams();
Expand All @@ -190,6 +192,7 @@ private void restoreSavedSCState() {
StaticContainers.setCachedColorSpaces(this.cachedColorSpaces);
StaticContainers.setCachedFonts(this.cachedPDFonts);
StaticContainers.setFileSpecificationKeys(this.fileSpecificationKeys);
StaticContainers.setDestinationToStructParentsMap(this.destinationToStructParentsMap);
StaticContainers.setNoteIDSet(this.noteIDSet);
StaticContainers.setXFormKeysSet(this.xFormKeysSet);
StaticContainers.setTransparencyVisitedContentStreams(this.transparencyVisitedContentStreams);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,19 @@ public String getstructParentStandardType() {
return null;
}

@Override
public String getstructParentObjectKey() {
TaggedPDFRoleMapHelper taggedPDFRoleMapHelper = StaticResources.getRoleMapHelper();
if (taggedPDFRoleMapHelper != null) {
COSObject parentDictionary = getParentDictionary();
if (parentDictionary != null) {
COSKey parentKey = parentDictionary.getObjectKey();
return parentKey != null ? parentKey.toString() : null;
}
}
return null;
}

protected COSObject getParentDictionary() {
PDStructTreeRoot structTreeRoot = StaticResources.getDocument().getStructTreeRoot();
Long structParent = ((PDAnnotation) this.simplePDObject).getStructParent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public Boolean getisStructDestination() {
if (dests != null) {
COSObject dest = dests.getObject(destination.getString());
if (dest == null) {
LOGGER.log(Level.WARNING, "Named destination " + destination.getString() + " not found in the Dests name tree in the Names dictionary");
LOGGER.log(Level.WARNING, "Named destination " + destination.getString() +
" not found in the Dests name tree in the Names dictionary");
return false;
}
destination = dest;
Expand All @@ -66,7 +67,8 @@ public Boolean getisStructDestination() {
if (dests != null) {
COSObject dest = dests.getKey(destination.getName());
if (dest == null) {
LOGGER.log(Level.WARNING, "Named destination " + destination.getName() + " not found in the Dests dictionary in the catalog");
LOGGER.log(Level.WARNING, "Named destination " + destination.getName() +
" not found in the Dests dictionary in the catalog");
return false;
}
destination = dest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ public String getentries() {
}
return "";
}

@Override
public String getobjectKey() {
return simpleCOSObject != null && !simpleCOSObject.empty() ? simpleCOSObject.getObjectKey().toString() : null;
}

@Override
public String getID() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,47 @@
package org.verapdf.gf.model.impl.pd.annotations;

import org.verapdf.as.ASAtom;
import org.verapdf.cos.COSKey;
import org.verapdf.cos.COSObjType;
import org.verapdf.cos.COSObject;
import org.verapdf.gf.model.impl.containers.StaticContainers;
import org.verapdf.gf.model.impl.pd.GFPDAnnot;
import org.verapdf.gf.model.impl.pd.GFPDDestination;
import org.verapdf.gf.model.impl.pd.util.PDResourcesHandler;
import org.verapdf.model.baselayer.Object;
import org.verapdf.model.pdlayer.PDDestination;
import org.verapdf.model.pdlayer.PDLinkAnnot;
import org.verapdf.pd.PDAnnotation;
import org.verapdf.pd.PDNameTreeNode;
import org.verapdf.pd.PDNamesDictionary;
import org.verapdf.pd.PDPage;
import org.verapdf.pd.actions.PDAction;
import org.verapdf.pdfa.flavours.PDFAFlavour;
import org.verapdf.tools.StaticResources;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* @author Maxim Plushchov
*/
public class GFPDLinkAnnot extends GFPDAnnot implements PDLinkAnnot {

public static final Logger LOGGER = Logger.getLogger(GFPDLinkAnnot.class.getCanonicalName());

public static final String LINK_ANNOTATION_TYPE = "PDLinkAnnot";

public static final String DEST = "Dest";

private String differentTargetAnnotObjectKey;
private String sameTargetAnnotObjectKey;

public GFPDLinkAnnot(PDAnnotation annot, PDResourcesHandler pageResources, PDPage page) {
super(annot, pageResources, page, LINK_ANNOTATION_TYPE);
if (StaticContainers.getFlavour() == PDFAFlavour.PDFUA_2) {
calculateStructDestinationProperties();
}
}

@Override
Expand All @@ -68,4 +84,98 @@ private List<PDDestination> getDestination() {
return Collections.emptyList();
}

private void calculateStructDestinationProperties() {
COSObject parent = getParentDictionary();
if (parent == null || parent.getKey() == null) {
return;
}
COSKey structParentKey = parent.getKey();
COSObject structDestination = getStructureDestinationObject();
if (structDestination == null || structDestination.getKey() == null) {
return;
}
COSKey structDestinationKey = structDestination.getKey();
Set<COSKey> structParentsSet = StaticContainers.getDestinationToStructParentsMap().computeIfAbsent(structDestinationKey,
k -> new HashSet<>());
for (COSKey structParentObjectKey : structParentsSet) {
if (!structParentKey.equals(structParentObjectKey)) {
sameTargetAnnotObjectKey = structParentObjectKey.toString();
break;
}
}
for (Map.Entry<COSKey, Set<COSKey>> entry : StaticContainers.getDestinationToStructParentsMap().entrySet()) {
if (structDestinationKey.equals(entry.getKey())) {
continue;
}
for (COSKey structParentObjectKey : entry.getValue()) {
if (structParentKey.equals(structParentObjectKey)) {
differentTargetAnnotObjectKey = structParentObjectKey.toString();
break;
}
}
if (differentTargetAnnotObjectKey != null) {
break;
}
}
structParentsSet.add(structParentKey);
}

private COSObject getStructureDestinationObject() {
COSObject destination = simpleCOSObject;
if (simpleCOSObject.knownKey(ASAtom.A)) {
PDAction action = ((PDAnnotation) simplePDObject).getA();
if (ASAtom.GO_TO.equals(action.getSubtype())) {
destination = action.getDestination();
}
} else if (simpleCOSObject.knownKey(ASAtom.DEST)) {
destination = ((PDAnnotation) simplePDObject).getDestination();
}
if (destination == null) {
return null;
}
if (destination.getType() == COSObjType.COS_STRING) {
PDNamesDictionary namesDictionary = StaticResources.getDocument().getCatalog().getNamesDictionary();
if (namesDictionary == null) {
return null;
}
PDNameTreeNode dests = namesDictionary.getDests();
if (dests != null) {
COSObject dest = dests.getObject(destination.getString());
if (dest == null) {
LOGGER.log(Level.WARNING, "Named destination " + destination.getString() +
" not found in the Dests name tree in the Names dictionary");
return null;
}
destination = dest;
}
} else if (destination.getType() == COSObjType.COS_NAME) {
COSObject dests = StaticResources.getDocument().getCatalog().getDests();
if (dests != null) {
COSObject dest = dests.getKey(destination.getName());
if (dest == null) {
LOGGER.log(Level.WARNING, "Named destination " + destination.getName() +
" not found in the Dests dictionary in the catalog");
return null;
}
destination = dest;
}
}
if (destination.getType() == COSObjType.COS_DICT) {
return destination.getKey(ASAtom.SD);
}
if (destination.getType() == COSObjType.COS_ARRAY && destination.size() > 0) {
return destination.at(0).getKey(ASAtom.S);
}
return null;
}

@Override
public String getdifferentTargetAnnotObjectKey() {
return differentTargetAnnotObjectKey;
}

@Override
public String getsameTargetAnnotObjectKey() {
return sameTargetAnnotObjectKey;
}
}

0 comments on commit 8fc1190

Please sign in to comment.