Skip to content

Commit

Permalink
GH-234 text redaction annotation tool and export document to burn (#313)
Browse files Browse the repository at this point in the history
  • Loading branch information
pcorless committed Nov 8, 2023
1 parent 255b5ce commit 93f97e5
Show file tree
Hide file tree
Showing 111 changed files with 2,374 additions and 647 deletions.
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Denote all files that are truly binary and should not be modified.
# stop messing up test data.
*.pdf binary
29 changes: 25 additions & 4 deletions core/core-awt/src/main/java/org/icepdf/core/pobjects/Document.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.icepdf.core.pobjects.acroform.FieldDictionary;
import org.icepdf.core.pobjects.acroform.InteractiveForm;
import org.icepdf.core.pobjects.annotations.AbstractWidgetAnnotation;
import org.icepdf.core.pobjects.annotations.RedactionAnnotation;
import org.icepdf.core.pobjects.graphics.WatermarkCallback;
import org.icepdf.core.pobjects.graphics.images.ImageUtility;
import org.icepdf.core.pobjects.graphics.text.PageText;
Expand Down Expand Up @@ -564,7 +565,7 @@ public void dispose() {
* @return The length of the PDF file copied
* @throws IOException if there is some problem reading or writing the PDF data
*/
public long writeToOutputStream(OutputStream out) throws IOException {
public long writeToOutputStream(OutputStream out) throws IOException, InterruptedException {
return writeToOutputStream(out, WriteMode.INCREMENT_UPDATE);
}

Expand All @@ -578,7 +579,7 @@ public long writeToOutputStream(OutputStream out) throws IOException {
* @return The length of the PDF file copied
* @throws IOException if there is some problem reading or writing the PDF data
*/
public long writeToOutputStream(OutputStream out, WriteMode writeMode) throws IOException {
public long writeToOutputStream(OutputStream out, WriteMode writeMode) throws IOException, InterruptedException {
if (documentFileChannel != null) {
synchronized (library.getMappedFileByteBufferLock()) {
ByteBuffer documentByteBuffer = library.getMappedFileByteBuffer();
Expand Down Expand Up @@ -617,7 +618,7 @@ public long writeToOutputStream(OutputStream out, WriteMode writeMode) throws IO
* @return The length of the PDF file saved
* @throws IOException if there is some problem reading or writing the PDF data
*/
public long saveToOutputStream(OutputStream out) throws IOException {
public long saveToOutputStream(OutputStream out) throws IOException, InterruptedException {
return writeToOutputStream(out, WriteMode.INCREMENT_UPDATE);
}

Expand All @@ -630,7 +631,7 @@ public long saveToOutputStream(OutputStream out) throws IOException {
* @return The length of the PDF file saved
* @throws IOException if there is some problem reading or writing the PDF data
*/
public long saveToOutputStream(OutputStream out, WriteMode writeMode) throws IOException {
public long saveToOutputStream(OutputStream out, WriteMode writeMode) throws IOException, InterruptedException {
return writeToOutputStream(out, writeMode);
}

Expand Down Expand Up @@ -717,6 +718,26 @@ public PageText getPageViewText(int pageNumber) throws InterruptedException {
}
}

public boolean hasRedactions() {
// check state manager first as this will be a bit cheaper than scanning each page in the document.
if (stateManager.hasRedactions()) {
return true;
} else {
PageTree pageTree = catalog.getPageTree();
Page page;
List<RedactionAnnotation> redactions;
for (int i = 0, max = pageTree.getNumberOfPages(); i < max; i++) {
page = pageTree.getPage(i);
redactions = page.getRedactionAnnotations();
if (redactions != null && redactions.size() > 1) {
return true;
}
}
return false;
}
}


/**
* Gets the security manager for this document. If the document has no
* security manager null is returned.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public synchronized void init() throws InterruptedException {
if (in != null) {
try {
logger.log(Level.FINER, () -> "Parsing form " + getPObjectReference());
shapes = cp.parse(new byte[][]{in}, new Reference[]{this.getPObjectReference()}, null).getShapes();
shapes = cp.parse(Stream.fromByteArray(in, this.getPObjectReference()), null).getShapes();
inited = true;
} catch (InterruptedException e) {
// the initialization was interrupted so we need to make sure we bubble up the exception
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.icepdf.core.pobjects.fonts.Font;
import org.icepdf.core.pobjects.fonts.FontFile;
import org.icepdf.core.pobjects.security.SecurityManager;
import org.icepdf.core.util.StringOffsetBuilder;
import org.icepdf.core.util.Utils;

import java.util.logging.Level;
Expand Down Expand Up @@ -222,7 +223,7 @@ public String getLiteralString() {
* @return StringBuffer which contains all renderaable characters for the
* given font.
*/
public StringBuilder getLiteralStringBuffer(final int fontFormat, FontFile font) {
public StringOffsetBuilder getLiteralStringBuffer(final int fontFormat, FontFile font) {
if (fontFormat == Font.SIMPLE_FORMAT) {
stringData = new StringBuilder(normalizeHex(stringData, 2).toString());
int charOffset = 2;
Expand All @@ -245,34 +246,34 @@ public StringBuilder getLiteralStringBuffer(final int fontFormat, FontFile font)
lastIndex += charOffset;
}
}
return tmp;
return new StringOffsetBuilder(tmp, 2);
} else if (fontFormat == Font.CID_FORMAT) {
stringData = new StringBuilder(normalizeHex(stringData, 4).toString());
int charOffset = 2;
int length = getLength();
int charValue;
StringBuilder tmp = new StringBuilder(length);
// attempt to detect mulibyte encoded strings.
StringOffsetBuilder tmp = new StringOffsetBuilder(length);
// attempt to detect multibyte encoded strings.
for (int i = 0; i < length; i += charOffset) {
String first = stringData.substring(i, i + 2);
if (first.charAt(0) != '0') {
// check range for possible 2 byte char ie mixed mode.
charValue = getUnsignedInt(first);
if (font.getByteEncoding() == FontFile.ByteEncoding.MIXED_BYTE &&
font.canDisplay((char) charValue) && font.getSource() != null) {
tmp.append((char) charValue);
tmp.append((char) charValue, 2);
} else {
charValue = getUnsignedInt(i, 4);
if (font.canDisplay((char) charValue)) {
tmp.append((char) charValue);
tmp.append((char) charValue, 4);
i += 2;
}
}
} else {
charValue = getUnsignedInt(i, 4);
// should never have a 4 digit zero value.
if (font.canDisplay((char) charValue)) {
tmp.append((char) charValue);
tmp.append((char) charValue, 4);
i += 2;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.icepdf.core.pobjects.fonts.Font;
import org.icepdf.core.pobjects.fonts.FontFile;
import org.icepdf.core.pobjects.security.SecurityManager;
import org.icepdf.core.util.StringOffsetBuilder;
import org.icepdf.core.util.Utils;

/**
Expand Down Expand Up @@ -57,7 +58,7 @@ public LiteralStringObject(StringBuilder chars, boolean dif) {

/**
* <p>Creates a new literal string object so that it represents the same
* sequence of character data specifed by the argument.</p>
* sequence of character data specified by the argument.</p>
*
* @param string the initial contents of the literal string object
*/
Expand Down Expand Up @@ -192,38 +193,38 @@ public String getLiteralString() {

/**
* <p>Gets a literal String representation of this object's data using the
* specifed font and format. The font is used to verify that the
* specific character codes can be rendered; if they cannot they may be
* specified font and format. The font is used to verify that the
* specific character codes can be rendered; if they cannot, they may be
* removed or combined with the next character code to get a displayable
* character code.
*
* @param fontFormat the type of pdf font which will be used to display
* the text. Valid values are CID_FORMAT and SIMPLE_FORMAT for Adobe
* Composite and Simple font types respectively
* @param font font used to render the the literal string data.
* @param font font used to render the literal string data.
* @return StringBuffer which contains all renderable characters for the
* given font.
*/
public StringBuilder getLiteralStringBuffer(final int fontFormat, FontFile font) {
public StringOffsetBuilder getLiteralStringBuffer(final int fontFormat, FontFile font) {

if (fontFormat == Font.SIMPLE_FORMAT
|| (font.getByteEncoding() == FontFile.ByteEncoding.ONE_BYTE)) {
return stringData;
return new StringOffsetBuilder(stringData, 1);
} else if (fontFormat == Font.CID_FORMAT) {
int length = getLength();
int charValue;
StringBuilder tmp = new StringBuilder(length);
StringOffsetBuilder tmp = new StringOffsetBuilder(length);
if (font.getByteEncoding() == FontFile.ByteEncoding.MIXED_BYTE) {
int charOffset = 1;
for (int i = 0; i < length; i += charOffset) {
// check range for possible 2 byte char.
charValue = getUnsignedInt(i, 1);
if (font.canDisplay((char) charValue)) {
tmp.append((char) charValue);
tmp.append((char) charValue, 1);
} else {
int charValue2 = getUnsignedInt(i, 2);
if (font.canDisplay((char) charValue2)) {
tmp.append((char) charValue2);
tmp.append((char) charValue2, 2);
i += 1;
}
}
Expand All @@ -234,7 +235,7 @@ public StringBuilder getLiteralStringBuffer(final int fontFormat, FontFile font)
for (int i = 0; i < length; i += charOffset) {
int charValue2 = getUnsignedInt(i, 2);
if (font.canDisplay((char) charValue2)) {
tmp.append((char) charValue2);
tmp.append((char) charValue2, 2);
}
}
}
Expand All @@ -244,7 +245,7 @@ public StringBuilder getLiteralStringBuffer(final int fontFormat, FontFile font)
}

/**
* The length of the the underlying object's data.
* The length of the underlying object's data.
*
* @return length of objcts data.
*/
Expand Down
Loading

0 comments on commit 93f97e5

Please sign in to comment.