How to remove Objects from PDF File using PDFClown - pdfclown

I have a pdf which contains a lot of invisible paths. Since the amount of path produces problems later on, I would like to remove the ones that have white colors.
So far I am trying to do this with a ContentScanner:
public class FilterWhitePathScanner implements Scanner {
private static final Logger LOG = LoggerFactory.getLogger(FilterWhitePathScanner.class);
private int count = 0;
public void scan(ContentScanner level) {
if (level == null)
return;
while (level.moveNext()) {
ContentObject object = level.getCurrent();
if (object instanceof ContainerObject) {
// Scan the inner level!
scan(level.getChildLevel());
} else if (object instanceof org.pdfclown.documents.contents.objects.Path) {
AffineTransform ctm = level.getState().getCtm();
Color<?> strokeColor = level.getState().getStrokeColor();
Color<?> fillColor = level.getState().getFillColor();
if (checkWhite(fillColor) && checkWhite(strokeColor)) {
level.remove();
} else {
LOG.info("Stroke Color " + strokeColor + " - Fill Color " + fillColor);
}
} else {
LOG.info("Object:" + object);
}
}
}
It recognizes the paths correctly, but in the end these are not removed from the PDF. Here the code handling the PDF (it extracts only one page from the source pdf):
Document targetDoc = new File().getDocument();
targetDoc.getPages().add(sourceDoc.getPages().get(pageNum).clone(targetDoc));
Page page = targetDoc.getPages().get(0);
Contents contents = page.getContents();
FilterWhitePathScanner filterWhitePathScanner = new FilterWhitePathScanner();
filterWhitePathScanner.scan(new ContentScanner(contents));
LOG.info("White Paths: " + filterWhitePathScanner.getCount());
targetDoc.getFile().save(tempFilePath.toFile(), SerializationModeEnum.Standard);
The saved PDF file still contains the paths I tried to remove. How can I remove objects from the PDF finally?
Thanks,
Thomas

Finally found the solution in the Java doc:
You have to call contents.flush(); to persist the changes into the pdf file.
So I added this line to the PDF handling code before calling save and it works!

Related

Using dynamic preloaded image in Flutter/Dart

I currently have images which the image name is returned by a function and is dynamically called based on a variable like this:
String _setImage() {
if (currentQuestion > 1 && currentQuestion < 11) {
return "assets/images/image_$intensityIndex.png";
} else {
return "assets/images/image.png";
}
}
I want to switch to preloading the images and I am using the technique described at Preload images in a stateful widget on Flutter, but I am not sure how to have the function return an image which the name is dynamically determined based on another variable. Here is what I have so far:
void initState() {
super.initState();
image0 = Image.asset('assets/images/image_0.png');
image1 = Image.asset('assets/images/image_1.png');
image2 = Image.asset('assets/images/image_2.png');
image3 = Image.asset('assets/images/image_3.png');
}
void didChangeDependencies() {
super.didChangeDependencies();
precacheImage(image0.image, context);
precacheImage(image1.image, context);
precacheImage(image2.image, context);
precacheImage(image3.image, context);
}
Image _setImage() {
if (currentQuestion > 1 && currentQuestion < 11) {
return ______________;
} else {
return image0;
}
}
All help is appreciated!
Am not sure how to return an image which the name is dynamically
determined based on another variable
You don't need to return with precach because if you use exact image that you cache.
Ex:
precacheImage('assets/images/image_1.png'); // if this is the image name
Image.asset('assets/images/image_1.png'); // when you use this it is getting from the cache but the path should be same.
If I explain it another way:
precacheImage("assets/images/image_$intensityIndex.png"); // image_1.png
Image.asset('assets/images/image_1.png'); // when you do this, asset taking from the cache by looking at the path.

Find and use already embedded font?

I have create a PDF file with Adobe Illustrator that I have loaded into memory with itext7 pdfreader.
That PDF file already contains a embedded font named "Lato (Embedded)" Encoding:Ansi.
How do create a PDFFont object out of it so I and can use it to draw additional paragraphs?
First of all please note that you would only be able to write additional paragraphs with such a fond if the subset contains all the glyphs needed to write the text, or if the font was fully embedded into the PDF.
The solution below works in case the font you want to find is used to write at least one glyph in the content stream of any page in a document (including nested XObjects), and in case you don't have other fonts with similar names in the document.
Here is a small utility class that helps you extract the desired font from a document:
private static class FontFinder implements IEventListener {
private PdfFont suitableFont;
private String nameToLookFor;
private FontFinder(String nameToLookFor) {
this.nameToLookFor = nameToLookFor;
}
public static PdfFont findFont(PdfDocument pdfDocument, String fontName) {
FontFinder finder = new FontFinder(fontName);
PdfCanvasProcessor processor = new PdfCanvasProcessor(finder);
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
processor.processPageContent(pdfDocument.getPage(i));
}
return finder.suitableFont;
}
#Override
public void eventOccurred(IEventData data, EventType type) {
if (data instanceof TextRenderInfo) {
PdfFont curFont = ((TextRenderInfo) data).getFont();
String fontName = curFont.getFontProgram().getFontNames().getFontName();
if (fontName != null && fontName.contains(nameToLookFor)) {
suitableFont = curFont;
}
}
}
#Override
public Set<EventType> getSupportedEvents() {
return new HashSet<>(Arrays.asList(EventType.RENDER_TEXT));
}
}
You will need to open the PdfDocument in stamping mode (passing both PdfReader and PdfWriter to the constructor).
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inFile), new PdfWriter(outFile));
Then you can fetch your font in the following way (make sure result is not null):
PdfFont font = FontFinder.findFont(pdfDocument, "Lato");
After that you can use that font instance to draw any content, e.g. by passing it to setFont method of Paragraph, Div and so on.

Not able to set Redaction Color in iText7 (C#)

I'm not able to change PDF redaction's color in iText7 + PDFSweep using the C# code below. The RED redaction box takes effect only on the first page of the PDF file, then on subsequent pages the color of the redaction box reverts back to BLACK
String input = SRC_FOLDER + "/report.pdf";
String output = SRC_FOLDER + "/report_redacted.pdf";
CompositeCleanupStrategy strategy = new CompositeCleanupStrategy();
strategy.Add(new RegexBasedCleanupStrategy(#"(\d\d\d\d)").SetRedactionColor(ColorConstants.RED));
PdfDocument pdf = new PdfDocument(new PdfReader(input), new PdfWriter(output));
PdfAutoSweep autoSweep = new PdfAutoSweep(strategy);
autoSweep.CleanUp(pdf);
pdf.Close();
It's a bug in pdfSweep.
ìText handles documents on a page by page basis.
In order to be able to re-use the same strategy on different pages, every ICleanupStrategy needs to provide a reset method.
The current implementation for that reset method for RegexBasedCleanupStragegy is
public ICleanupStrategy reset() {
return new RegexBasedCleanupStrategy(this.pattern);
}
Which copies the strategy's pattern, but not its color. As a result, on every page but the first one, the color will default back to black.
To fix this, simply create your own implementation that overrides this behavior to also copy the color.
I will report this as a bug (iText developer here)
for the sake of completion, this would be the fixed approach:
public class RegexBasedCleanupStrategy extends
RegexBasedLocationExtractionStrategy implements ICleanupStrategy {
private Pattern pattern;
private Color redactionColor = ColorConstants.BLACK;
public RegexBasedCleanupStrategy(String regex) {
super(regex);
this.pattern = Pattern.compile(regex);
}
public RegexBasedCleanupStrategy(Pattern pattern) {
super(pattern);
this.pattern = pattern;
}
#Override
public Color getRedactionColor(IPdfTextLocation location) {
return redactionColor;
}
public RegexBasedCleanupStrategy setRedactionColor(Color color) {
this.redactionColor = color;
return this;
}
public ICleanupStrategy reset() {
RegexBasedCleanupStrategy copy = new RegexBasedCleanupStrategy(pattern);
copy.redactionColor = redactionColor;
return copy;
}
}

How could I choose one particular file to load with loadStrings

The title is explicit enough, I want to let the user choose the text file he want to open.
I do not know if there is an explorer or an input field already implemented on processing.
Any help would be great.
Use selectInput. From the Processing reference:
Opens a platform-specific file chooser dialog to select a file for input. After the selection is made, the selected File will be passed to the 'callback' function. If the dialog is closed or canceled, null will be sent to the function, so that the program is not waiting for additional input. The callback is necessary because of how threading works.
I've modified the example sketch they provide in the reference to include loading the file with the loadStrings method.
String[] txtFile;
void setup() {
selectInput("Select a file to process:", "fileSelected");
}
void fileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
} else {
String filepath = selection.getAbsolutePath();
println("User selected " + filepath);
// load file here
txtFile = loadStrings(filepath);
}
}
There is no Implemented method, but you could could make a buffer and monitor key presses like so:
String[] File;
String keybuffer = "";
Char TriggerKey = Something;
void setup(){
//do whatever here
}
void draw(){
//Optional, to show the current buffer
background(255);
text(keybuffer,100,100);
}
void keyPressed(){
if(keyCode >= 'a' && keyCode <= 'z'){
keybuffer = keybuffer + key;
}
if(key == TriggerKey){
File = loadStrings(keybuffer + ".txt");
}
}
when triggerkey is pressed, it loads the file

xuggler: no video in encoded 3gp file

i am trying to encode videos into 3gp format using xuggler, i somehow got it to work, work as in the program stopped throwing errors and exceptions, but the new file that is created does not have any video. Now there is no error or exception for me to work with so i have stuck a wall.
EDIT: Note the audio is working as it shud.
This is the code for the main function where the listeners are configured
IMediaReader reader = ToolFactory.makeReader("/home/hp/mms/b.flv");
IMediaWriter writer = ToolFactory.makeWriter("/home/hp/mms/xuggle/a_converted.3gp", reader);
IMediaDebugListener debugListener = ToolFactory.makeDebugListener();
writer.addListener(debugListener);
ConvertVideo convertor = new ConvertVideo(new File("/home/hp/mms/b.flv"), new File("/home/hp/mms/xuggle/a_converted.3gp"));
// convertor.addListener(writer);
reader.addListener(writer);
writer.addListener(convertor);
while (reader.readPacket() == null)
;
And this is the code for the convertor that i wrote.
public ConvertVideo(File inputFile, File outputFile)
{
this.outputFile = outputFile;
reader = ToolFactory.makeReader(inputFile.getAbsolutePath());
reader.addListener(this);
}
private IVideoResampler videoResampler = null;
private IAudioResampler audioResampler = null;
#Override
public void onAddStream(IAddStreamEvent event)
{
if (writer == null)
{
writer = ToolFactory.makeWriter(outputFile.getAbsolutePath(), reader);
}
int streamIndex = event.getStreamIndex();
IStreamCoder streamCoder = event.getSource().getContainer().getStream(streamIndex).getStreamCoder();
if (streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO)
{
streamCoder.setFlag(IStreamCoder.Flags.FLAG_QSCALE, false);
writer.addAudioStream(streamIndex, 0, 1, 8000);
}
else if (streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO)
{
streamCoder.setFlag(IStreamCoder.Flags.FLAG_QSCALE, false);
streamCoder.setCodec(ICodec.findEncodingCodecByName("h263"));
writer.addVideoStream(streamIndex, 0, VIDEO_WIDTH, VIDEO_HEIGHT);
}
super.onAddStream(event);
}
// //
#Override
public void onVideoPicture(IVideoPictureEvent event)
{
IVideoPicture pic = event.getPicture();
if (videoResampler == null)
{
videoResampler = IVideoResampler.make(VIDEO_WIDTH, VIDEO_HEIGHT, pic.getPixelType(), pic.getWidth(), pic.getHeight(), pic.getPixelType());
}
IVideoPicture out = IVideoPicture.make(pic.getPixelType(), VIDEO_WIDTH, VIDEO_HEIGHT);
videoResampler.resample(out, pic);
IVideoPictureEvent asc = new VideoPictureEvent(event.getSource(), out, event.getStreamIndex());
super.onVideoPicture(asc);
out.delete();
}
#Override
public void onAudioSamples(IAudioSamplesEvent event)
{
IAudioSamples samples = event.getAudioSamples();
if (audioResampler == null)
{
audioResampler = IAudioResampler.make(1, samples.getChannels(), 8000, samples.getSampleRate());
}
if (event.getAudioSamples().getNumSamples() > 0)
{
IAudioSamples out = IAudioSamples.make(samples.getNumSamples(), samples.getChannels());
audioResampler.resample(out, samples, samples.getNumSamples());
AudioSamplesEvent asc = new AudioSamplesEvent(event.getSource(), out, event.getStreamIndex());
super.onAudioSamples(asc);
out.delete();
}
}
I just cant seem to figure out where the problem is. I wud be thankful if someone wud plz point me in the right direction.
EDIT: If i see the properties of my newly encoded video, its audio properties are set and its video properties are not i.e in video properties, dimension= 0 x 0, frame rate= N/A and codec= h.263. The problem here is the 0 x 0 dimension.
well i found the answer, well not exactly the answer but a way to do what i was doing. Right now i am not quiet sure why my code was not working but the hre u can find the solution that worked for me. the author here just makes a seperate resizer class, adds it as a listener to the reader,. It has the onPictureEvent method overridden. Then he makes another class MyVideoListener and overrides the onAddStream method and adds it as a listener to the writer. and then he links the two parts by adding writer as a listener to resizer. works like a a charm.

Resources