Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PDF/UA-2. Add new annotations methods #603

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}