Skip to content
Open
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 @@ -413,7 +413,7 @@ public void parseChunk(Operator rawOperator, List<COSBase> arguments, int operat
case Operators.F_FILL_OBSOLETE:
case Operators.F_STAR_FILL:
processh();
processf();
processf(operatorIndex);
break;
case Operators.GS:
PDExtGState extGState = this.resourcesHandler.getExtGState(getLastCOSName(arguments));
Expand Down Expand Up @@ -491,21 +491,21 @@ public void parseChunk(Operator rawOperator, List<COSBase> arguments, int operat
case Operators.B_CLOSEPATH_FILL_STROKE:
case Operators.B_STAR_CLOSEPATH_EOFILL_STROKE:
processh();
processB();
processB(operatorIndex);
break;
case Operators.B_FILL_STROKE:
case Operators.B_STAR_EOFILL_STROKE:
processB();
processB(operatorIndex);
break;
case Operators.N:
nonDrawingArtifacts = new ArrayList<>();
break;
case Operators.S_CLOSE_STROKE:
processh();
processS();
processS(operatorIndex);
break;
case Operators.S_STROKE:
processS();
processS(operatorIndex);
break;
case Operators.CM_CONCAT:
if (arguments.size() == 6) {
Expand Down Expand Up @@ -611,7 +611,12 @@ private void processh() {
path.setCurrentPoint(path.getStartX(), path.getStartY());
}

private void processB() {
/**
* @param operatorIndex position of the paint operator in the stream, so a
* line art chunk can carry it the way text and image
* chunks already do
*/
private void processB(int operatorIndex) {
if (!processLayers()) {
nonDrawingArtifacts = new ArrayList<>();
return;
Expand All @@ -622,26 +627,31 @@ private void processB() {
if (chunk instanceof LineChunk) {
LineChunk lineChunk = transformLineChunk((LineChunk)chunk, graphicsState.getLineWidth(),
graphicsState.getLineCap());
processLineChunk(boundingBox, mcid, lineChunk);
processLineChunk(boundingBox, mcid, lineChunk, operatorIndex);
} else if (chunk instanceof CurveChunk) {
CurveChunk curveChunk = CurveChunk.transformCurve((CurveChunk)chunk, graphicsState.getCTM(),
graphicsState.getLineWidth());
processBoundingBox(boundingBox, mcid, curveChunk.getBoundingBox());
processBoundingBox(boundingBox, mcid, curveChunk.getBoundingBox(), operatorIndex);
} else if (chunk instanceof Rectangle) {
LineChunk line = ((Rectangle)chunk).getLine(graphicsState.getLineWidth());
if (line != null) {
LineChunk line1 = transformLineChunk(line, line.getWidth(), LineChunk.PROJECTING_SQUARE_CAP_STYLE);
processLineChunk(boundingBox, mcid, line1);
processLineChunk(boundingBox, mcid, line1, operatorIndex);
}
}
}
if (StaticStorages.getIsIgnoreMCIDs()) {
lineArtContainer.add(mcid, boundingBox);
lineArtContainer.add(mcid, boundingBox, operatorIndex, xObjectName);
}
nonDrawingArtifacts = new ArrayList<>();
}

private void processS() {
/**
* @param operatorIndex position of the paint operator in the stream, so a
* line art chunk can carry it the way text and image
* chunks already do
*/
private void processS(int operatorIndex) {
if (!processLayers()) {
nonDrawingArtifacts = new ArrayList<>();
return;
Expand All @@ -652,36 +662,41 @@ private void processS() {
if (chunk instanceof LineChunk) {
LineChunk lineChunk = transformLineChunk((LineChunk)chunk, graphicsState.getLineWidth(),
graphicsState.getLineCap());
processLineChunk(boundingBox, mcid, lineChunk);
processLineChunk(boundingBox, mcid, lineChunk, operatorIndex);
} else if (chunk instanceof CurveChunk) {
CurveChunk curveChunk = CurveChunk.transformCurve((CurveChunk)chunk, graphicsState.getCTM(),
graphicsState.getLineWidth());
processBoundingBox(boundingBox, mcid, curveChunk.getBoundingBox());
processBoundingBox(boundingBox, mcid, curveChunk.getBoundingBox(), operatorIndex);
} else if (chunk instanceof Rectangle) {
Rectangle rectangle = (Rectangle) chunk;
if (rectangle.getHeight() < graphicsState.getLineWidth() ||
rectangle.getWidth() < graphicsState.getLineWidth()) {
LineChunk line = rectangle.getLine(graphicsState.getLineWidth());
if (line != null) {
LineChunk line1 = transformLineChunk(line, line.getWidth(), LineChunk.PROJECTING_SQUARE_CAP_STYLE);
processLineChunk(boundingBox, mcid, line1);
processLineChunk(boundingBox, mcid, line1, operatorIndex);
}
} else {
List<LineChunk> lines = rectangle.getLines(graphicsState.getLineWidth());
for (LineChunk line : lines) {
LineChunk line1 = transformLineChunk(line, graphicsState.getLineWidth(), LineChunk.PROJECTING_SQUARE_CAP_STYLE);
processLineChunk(boundingBox, mcid, line1);
processLineChunk(boundingBox, mcid, line1, operatorIndex);
}
}
}
}
if (StaticStorages.getIsIgnoreMCIDs()) {
lineArtContainer.add(mcid, boundingBox);
lineArtContainer.add(mcid, boundingBox, operatorIndex, xObjectName);
}
nonDrawingArtifacts = new ArrayList<>();
}

private void processf() {
/**
* @param operatorIndex position of the paint operator in the stream, so a
* line art chunk can carry it the way text and image
* chunks already do
*/
private void processf(int operatorIndex) {
if (!processLayers()) {
nonDrawingArtifacts = new ArrayList<>();
return;
Expand All @@ -694,42 +709,48 @@ private void processf() {
LineChunk line = ((Rectangle)chunk).getLine(0);
if (line != null) {
LineChunk line1 = transformLineChunk(line, line.getWidth(), LineChunk.PROJECTING_SQUARE_CAP_STYLE);
processLineChunk(boundingBox, mcid, line1);
processLineChunk(boundingBox, mcid, line1, operatorIndex);
}
} else if (chunk instanceof LineChunk) {
LineChunk line = parsingRectangleFromLines(i);
if (line != null) {
processLineChunk(boundingBox, mcid, line);
processLineChunk(boundingBox, mcid, line, operatorIndex);
i += 3;
} else {
LineChunk line1 = transformLineChunk((LineChunk)chunk, graphicsState.getLineWidth(),
graphicsState.getLineCap());
processBoundingBox(boundingBox, mcid, line1.getBoundingBox());
processBoundingBox(boundingBox, mcid, line1.getBoundingBox(), operatorIndex);
}
} else if (chunk instanceof CurveChunk) {
CurveChunk curveChunk = CurveChunk.transformCurve((CurveChunk)chunk, graphicsState.getCTM(),
graphicsState.getLineWidth());
processBoundingBox(boundingBox, mcid, curveChunk.getBoundingBox());
processBoundingBox(boundingBox, mcid, curveChunk.getBoundingBox(), operatorIndex);
}
}
if (StaticStorages.getIsIgnoreMCIDs()) {
lineArtContainer.add(mcid, boundingBox);
lineArtContainer.add(mcid, boundingBox, operatorIndex, xObjectName);
}
nonDrawingArtifacts = new ArrayList<>();
}

private void processLineChunk(BoundingBox boundingBox, Long mcid, LineChunk lineChunk) {
lineArtContainer.add(mcid, lineChunk);
private void processLineChunk(BoundingBox boundingBox, Long mcid, LineChunk lineChunk,
int operatorIndex) {
lineArtContainer.add(mcid, lineChunk, operatorIndex, xObjectName);
if (StaticStorages.getIsIgnoreMCIDs()) {
boundingBox.union(lineChunk.getBoundingBox());
}
}

private void processBoundingBox(BoundingBox boundingBox, Long mcid, BoundingBox newBoundingBox) {
private void processBoundingBox(BoundingBox boundingBox, Long mcid, BoundingBox newBoundingBox,
int operatorIndex) {
if (StaticStorages.getIsIgnoreMCIDs()) {
boundingBox.union(newBoundingBox);
} else {
lineArtContainer.add(mcid, newBoundingBox);
// Curves reach the container here rather than through
// processLineChunk, so the operator index has to be carried on this
// path too: a region drawn only with curve operators is otherwise
// left without one.
lineArtContainer.add(mcid, newBoundingBox, operatorIndex, xObjectName);
}
}

Expand Down Expand Up @@ -1086,9 +1107,18 @@ public void parseLineArts() {
if (lineChunks == null) {
lineChunks = new LinkedList<>();
}
// Stream position recorded for this mcid while the operators were read.
// The chunks below are built after the stream, so this is the only way
// they can carry one — and without one a vector region can never be
// given a marked content id, which is what makes it untaggable.
List<StreamInfo> lineArtStreamInfos = lineArtContainer.getStreamInfos(mcid);
if (mcid == null && parentMarkedContent == null) {
for (BoundingBox box : boundingBoxes.getValue()) {
artifacts.add(new LineArtChunk(box));
LineArtChunk artifact = new LineArtChunk(box);
for (StreamInfo info : lineArtStreamInfos) {
artifact.getStreamInfos().add(new StreamInfo(info));
}
artifacts.add(artifact);
}
artifacts.addAll(lineChunks);
}
Expand All @@ -1097,10 +1127,25 @@ public void parseLineArts() {
boundingBox.union(box);
}
if (mcid != null) {
lineArtContainer.getLineArt(mcid).setBoundingBox(boundingBox);
lineArtContainer.getLineArt(mcid).setLineChunks(lineChunks);
LineArtChunk existing = lineArtContainer.getLineArt(mcid);
existing.setBoundingBox(boundingBox);
existing.setLineChunks(lineChunks);
// Set here rather than where the chunk was created. The chunk is
// created on the region's first bounding box, when only the first
// operator has been seen, and the operators that follow never
// reach it — so a region inside marked content exposed just one of
// them. This runs once the stream has been read, so the list is
// complete.
existing.getStreamInfos().clear();
for (StreamInfo info : lineArtStreamInfos) {
existing.getStreamInfos().add(new StreamInfo(info));
}
} else {
StaticStorages.getChunks().add(parentObjectKey, parentMarkedContent, new LineArtChunk(boundingBox, lineChunks));
LineArtChunk lineArtChunk = new LineArtChunk(boundingBox, lineChunks);
for (StreamInfo info : lineArtStreamInfos) {
lineArtChunk.getStreamInfos().add(new StreamInfo(info));
}
StaticStorages.getChunks().add(parentObjectKey, parentMarkedContent, lineArtChunk);
}
}
}
Expand All @@ -1109,18 +1154,33 @@ public void processLineArts() {
if (!StaticStorages.getIsIgnoreMCIDs()) {
return;
}
// Read before the container's record is cleared below. This is the path a
// region outside any marked content takes, and the chunks it produces are
// the ones a consumer sees, so a position missing here is a region that
// cannot be tagged however well the rest is wired.
List<StreamInfo> pending = lineArtContainer.getStreamInfos(null);
List<LineChunk> lineChunks = lineArtContainer.getLineChunks(null);
if (lineChunks != null && !lineChunks.isEmpty()) {
for (LineChunk lineChunk : lineChunks) {
for (StreamInfo info : pending) {
lineChunk.getStreamInfos().add(new StreamInfo(info));
}
}
artifacts.addAll(lineChunks);
lineChunks.clear();
}
List<BoundingBox> boundingBoxes = lineArtContainer.getBoundingBoxes(null);
if (boundingBoxes != null && !boundingBoxes.isEmpty()) {
for (BoundingBox box : boundingBoxes) {
artifacts.add(new LineArtChunk(box));
LineArtChunk artifact = new LineArtChunk(box);
for (StreamInfo info : pending) {
artifact.getStreamInfos().add(new StreamInfo(info));
}
artifacts.add(artifact);
}
boundingBoxes.clear();
}
lineArtContainer.clearStreamInfos(null);
}

public boolean processLayers() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.verapdf.wcag.algorithms.entities.content.LineArtChunk;
import org.verapdf.wcag.algorithms.entities.content.LineChunk;
import org.verapdf.wcag.algorithms.entities.geometry.BoundingBox;
import org.verapdf.wcag.algorithms.semanticalgorithms.containers.StaticContainers;
import org.verapdf.wcag.algorithms.semanticalgorithms.utils.StreamInfo;

import java.util.*;

Expand All @@ -35,12 +37,22 @@ public class LineArtContainer {
private final Map<Long, List<BoundingBox>> lineArtBBoxes;
private final Map<Long, LineArtChunk> lineArts;
private final Map<Long, List<LineChunk>> lineArtLines;
/**
* Stream position of the first paint operator seen for each mcid.
*
* <p>Kept because the chunk for an untagged region is not built here — it is
* built in {@code ChunkParser.parseLineArts}, once the stream has been read,
* where no single operator is in scope any more. Without somewhere to hold
* the position, a region whose marks carry no mcid can never be given one.
*/
private final Map<Long, List<StreamInfo>> lineArtStreamInfos;
private final COSKey objectKey;

public LineArtContainer(COSKey objectKey) {
lineArtBBoxes = new HashMap<>();
lineArts = new HashMap<>();
lineArtLines = new HashMap<>();
lineArtStreamInfos = new HashMap<>();
this.objectKey = objectKey;
}

Expand All @@ -56,7 +68,31 @@ public LineArtChunk getLineArt(Long mcid) {
return lineArts.get(mcid);
}

/** Stream positions recorded for this mcid, empty when none were. */
public List<StreamInfo> getStreamInfos(Long mcid) {
List<StreamInfo> infos = lineArtStreamInfos.get(mcid);
return infos == null ? Collections.emptyList() : infos;
}

/**
* Forgets the position for this mcid, so the next region under it records its
* own. Called where the boxes are cleared: keeping the old position would
* hand a later region the operator that drew an earlier one.
*/
public void clearStreamInfos(Long mcid) {
lineArtStreamInfos.remove(mcid);
}

public void add(Long mcid, LineChunk lineChunk) {
add(mcid, lineChunk, null, null);
}

/**
* @param operatorIndex index of the paint operator that drew this line, or
* null when it is not known
* @param xObjectName name of the form the line was drawn inside, or null
*/
public void add(Long mcid, LineChunk lineChunk, Integer operatorIndex, String xObjectName) {
List<LineChunk> lineChunks = getLineChunks(mcid);
if (lineChunks != null) {
lineChunks.add(lineChunk);
Expand All @@ -65,13 +101,27 @@ public void add(Long mcid, LineChunk lineChunk) {
lineChunks.add(lineChunk);
lineArtLines.put(mcid, lineChunks);
}
add(mcid, lineChunk.getBoundingBox());
add(mcid, lineChunk.getBoundingBox(), operatorIndex, xObjectName);
}

public void add(Long mcid, BoundingBox boundingBox) {
add(mcid, boundingBox, null, null);
}

public void add(Long mcid, BoundingBox boundingBox, Integer operatorIndex, String xObjectName) {
if (boundingBox.isEmpty()) {
return;
}
// Every paint operator, not just the first. A StreamInfo names one
// operator, so a region an infographic draws with thousands of them needs
// one entry each: recording only the first wrapped one operator and left
// the rest of the region outside the tree.
if (StaticContainers.isDataLoader() && operatorIndex != null) {
List<StreamInfo> infos = lineArtStreamInfos.computeIfAbsent(mcid, k -> new ArrayList<>());
if (infos.isEmpty() || infos.get(infos.size() - 1).getOperatorIndex() != operatorIndex) {
infos.add(new StreamInfo(operatorIndex, xObjectName, null));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
List<BoundingBox> list = getBoundingBoxes(mcid);
if (list != null && !StaticStorages.getIsIgnoreMCIDs()) {
boolean isSeparateBoundingBox = true;
Expand All @@ -90,6 +140,9 @@ public void add(Long mcid, BoundingBox boundingBox) {
} else {
if (mcid != null) {
LineArtChunk lineArtChunk = new LineArtChunk();
// No stream info attached here on purpose: only the region's first
// operator has been seen at this point. ChunkParser.parseLineArts
// sets the complete list once the stream has been read.
StaticStorages.getChunks().add(objectKey, mcid, lineArtChunk);
lineArts.put(mcid, lineArtChunk);
}
Expand Down