Rimozione di filigrana da PDF iTextSharp

Ho seguito la soluzione suggerita qui ma il mio problema è un po ‘diverso. Nella soluzione fornita al link precedente, è ansible rimuovere la filigrana solo se iTextSharp viene utilizzato per aggiungere anche la filigrana. Nel mio caso, sto aggiungendo una filigrana in alcuni casi usando Microsoft Word. Quando uso il seguente codice, la filigrana scompare dal PDF ma quando converto il PDF in Word, la filigrana appare di nuovo come un’immagine. Secondo la mia comprensione, ciò che fa il codice sotto è che cambia il valore di opacità della filigrana a 0 e quindi scompare.

private static void removeWatermark(string watermarkedFile, string unwatermarkedFile) { PdfReader.unethicalreading = true; PdfReader reader = new PdfReader(watermarkedFile); reader.RemoveUnusedObjects(); int pageCount = reader.NumberOfPages; for (int i = 1; i <= pageCount; i++) { var page = reader.GetPageN(i); PdfDictionary resources = page.GetAsDict(PdfName.RESOURCES); PdfDictionary extGStates = resources.GetAsDict(PdfName.EXTGSTATE); if (extGStates == null) continue; foreach (PdfName name in extGStates.Keys) { var obj = extGStates.Get(name); PdfDictionary extGStateObject = (PdfDictionary)PdfReader.GetPdfObject(obj); var stateNumber = extGStateObject.Get(PdfName.ca); if (stateNumber == null) continue; var caNumber = (PdfNumber)PdfReader.GetPdfObject(stateNumber); if (caNumber.FloatValue != 1f) { extGStateObject.Remove(PdfName.ca); extGStateObject.Put(PdfName.ca, new PdfNumber(0f)); } } } using (FileStream fs = new FileStream(unwatermarkedFile, FileMode.Create, FileAccess.Write, FileShare.None)) { using (PdfStamper stamper = new PdfStamper(reader, fs)) { stamper.SetFullCompression(); stamper.Close(); } } } 

C’è un modo per essere in grado di eliminare questa filigrana modificando il codice?

Come già menzionato dall’OP, se hai il controllo completo sul processo che originariamente creava la filigrana, puoi fare come @ChrisHaas ha spiegato nella sua risposta alla domanda a cui l’OP si riferiva .

Se invece lo strumento con cui crei la filigrana lo fa a modo suo, avrai bisogno di un metodo personalizzato per quelle filigrane.

In genere questo metodo richiede la modifica di un stream di contenuti. La soluzione di @ChrisHaas, a proposito, lo fa anche tu.

Per semplificare questo, si dovrebbe iniziare creando una generica funzionalità di modifica del stream di contenuti e quindi utilizzare questa funzionalità solo per modificare tali filigrane.

Quindi, qui in un primo momento una class di editor di stream di contenuto generico di esempio e quindi una soluzione basata su di esso per modificare la filigrana di esempio dell’OP.

Una class dell’editor di stream del contenuto generico

Questa class PdfContentStreamEditor analizza l’istruzione del stream di contenuti originale mediante istruzioni che tengono traccia di una parte dello stato grafico; le istruzioni vengono inoltrate al metodo Write che, per impostazione predefinita, le ristampa esattamente come appaiono, creando effettivamente una copia identica o almeno equivalente del stream originale.

Per modificare effettivamente lo stream, è sufficiente sovrascrivere questo metodo Write e inoltrare solo le istruzioni desiderate nel stream dei risultati al metodo Write base.

 public class PdfContentStreamEditor : PdfContentStreamProcessor { /** * This method edits the immediate contents of a page, ie its content stream. * It explicitly does not descent into form xobjects, patterns, or annotations. */ public void EditPage(PdfStamper pdfStamper, int pageNum) { PdfReader pdfReader = pdfStamper.Reader; PdfDictionary page = pdfReader.GetPageN(pageNum); byte[] pageContentInput = ContentByteUtils.GetContentBytesForPage(pdfReader, pageNum); page.Remove(PdfName.CONTENTS); EditContent(pageContentInput, page.GetAsDict(PdfName.RESOURCES), pdfStamper.GetUnderContent(pageNum)); } /** * This method processes the content bytes and outputs to the given canvas. * It explicitly does not descent into form xobjects, patterns, or annotations. */ public void EditContent(byte[] contentBytes, PdfDictionary resources, PdfContentByte canvas) { this.canvas = canvas; ProcessContent(contentBytes, resources); this.canvas = null; } /** * This method writes content stream operations to the target canvas. The default * implementation writes them as they come, so it essentially generates identical * copies of the original instructions the {@link ContentOperatorWrapper} instances * forward to it. * * Override this method to achieve some fancy editing effect. */ protected virtual void Write(PdfContentStreamProcessor processor, PdfLiteral operatorLit, List operands) { int index = 0; foreach (PdfObject pdfObject in operands) { pdfObject.ToPdf(canvas.PdfWriter, canvas.InternalBuffer); canvas.InternalBuffer.Append(operands.Count > ++index ? (byte) ' ' : (byte) '\n'); } } // // constructor giving the parent a dummy listener to talk to // public PdfContentStreamEditor() : base(new DummyRenderListener()) { } // // Overrides of PdfContentStreamProcessor methods // public override IContentOperator RegisterContentOperator(String operatorString, IContentOperator newOperator) { ContentOperatorWrapper wrapper = new ContentOperatorWrapper(); wrapper.setOriginalOperator(newOperator); IContentOperator formsrOperator = base.RegisterContentOperator(operatorString, wrapper); return formsrOperator is ContentOperatorWrapper ? ((ContentOperatorWrapper)formsrOperator).getOriginalOperator() : formsrOperator; } public override void ProcessContent(byte[] contentBytes, PdfDictionary resources) { this.resources = resources; base.ProcessContent(contentBytes, resources); this.resources = null; } // // members holding the output canvas and the resources // protected PdfContentByte canvas = null; protected PdfDictionary resources = null; // // A content operator class to wrap all content operators to forward the invocation to the editor // class ContentOperatorWrapper : IContentOperator { public IContentOperator getOriginalOperator() { return originalOperator; } public void setOriginalOperator(IContentOperator originalOperator) { this.originalOperator = originalOperator; } public void Invoke(PdfContentStreamProcessor processor, PdfLiteral oper, List operands) { if (originalOperator != null && !"Do".Equals(oper.ToString())) { originalOperator.Invoke(processor, oper, operands); } ((PdfContentStreamEditor)processor).Write(processor, oper, operands); } private IContentOperator originalOperator = null; } // // A dummy render listener to give to the underlying content stream processor to feed events to // class DummyRenderListener : IRenderListener { public void BeginTextBlock() { } public void RenderText(TextRenderInfo renderInfo) { } public void EndTextBlock() { } public void RenderImage(ImageRenderInfo renderInfo) { } } } 

Alcuni sfondi :

Questa class estende PdfContentStreamProcessor dallo spazio dei nomi del parser iTextSharp. Questa class è stata originariamente progettata per analizzare semplicemente i flussi di contenuto per restituire informazioni per l’estrazione di testo, immagini o grafica. Lo sfruttiamo per tenere traccia di una parte dello stato grafico, più esattamente di quei parametri dello stato grafico rilevanti per l’estrazione del testo.

Se per attività di modifica specifiche è necessario anche informazioni pre-elaborate, ad esempio il testo disegnato dall’istruzione corrente, è ansible utilizzare IRenderListener personalizzata IRenderListener per recuperare tali informazioni anziché il DummyRenderListener utilizzato qui che semplicemente lo ignora.

Questa architettura di class è ispirata al PdfCleanUpProcessor dalla libreria supplementare iTextSharp.xtra .

Un editor per hide la filigrana dell’OP

Come l’OP ha già scoperto, le sue filigrane possono essere riconosciute come le uniche parti del documento usando la trasparenza definita in un object ExtGState come valore ca. Per hide la filigrana dobbiamo quindi

  • riconoscere le modifiche dello stato della grafica rispetto a quel valore e
  • non disegnare nulla quando il valore ca corrente riconosciuto è inferiore a 1.

In realtà la filigrana è costruita usando operazioni di grafica vettoriale. Pertanto, possiamo limitare il nostro editing a tali operazioni. Possiamo anche limitarlo a modificare l’istruzione di disegno finale (“tratto” / “riempimento” / “riempimento-e-tratto” oltre a determinate variazioni) per non eseguire la parte (riempimento o tracciatura) che genera contenuto trasparente.

 public class TransparentGraphicsRemover : PdfContentStreamEditor { protected override void Write(PdfContentStreamProcessor processor, PdfLiteral oper, List operands) { String operatorString = oper.ToString(); if ("gs".Equals(operatorString)) { updateTransparencyFrom((PdfName) operands[0]); } if (operatorMapping.Keys.Contains(operatorString)) { // Downgrade the drawing operator if transparency is involved // For details cf. the comment before the operatorMapping declaration PdfLiteral[] mapping = operatorMapping[operatorString]; int index = 0; if (strokingAlpha < 1) index |= 1; if (nonStrokingAlpha < 1) index |= 2; oper = mapping[index]; operands[operands.Count - 1] = oper; } base.Write(processor, oper, operands); } // The current transparency values; beware: save and restore state operations are ignored! float strokingAlpha = 1; float nonStrokingAlpha = 1; void updateTransparencyFrom(PdfName gsName) { PdfDictionary extGState = getGraphicsStateDictionary(gsName); if (extGState != null) { PdfNumber number = extGState.GetAsNumber(PdfName.ca); if (number != null) nonStrokingAlpha = number.FloatValue; number = extGState.GetAsNumber(PdfName.CA); if (number != null) strokingAlpha = number.FloatValue; } } PdfDictionary getGraphicsStateDictionary(PdfName gsName) { PdfDictionary extGStates = resources.GetAsDict(PdfName.EXTGSTATE); return extGStates.GetAsDict(gsName); } // // Map from an operator name to an array of operations it becomes depending // on the current graphics state: // // * [0] the operation in case of no transparency // * [1] the operation in case of stroking transparency // * [2] the operation in case of non-stroking transparency // * [3] the operation in case of stroking and non-stroking transparency // Dictionary operatorMapping = new Dictionary(); public TransparentGraphicsRemover() { PdfLiteral _S = new PdfLiteral("S"); PdfLiteral _s = new PdfLiteral("s"); PdfLiteral _f = new PdfLiteral("f"); PdfLiteral _fStar = new PdfLiteral("f*"); PdfLiteral _B = new PdfLiteral("B"); PdfLiteral _BStar = new PdfLiteral("B*"); PdfLiteral _b = new PdfLiteral("b"); PdfLiteral _bStar = new PdfLiteral("b*"); PdfLiteral _n = new PdfLiteral("n"); operatorMapping["S"] = new PdfLiteral[]{ _S, _n, _S, _n }; operatorMapping["s"] = new PdfLiteral[]{ _s, _n, _s, _n }; operatorMapping["f"] = new PdfLiteral[]{ _f, _f, _n, _n }; operatorMapping["F"] = new PdfLiteral[]{ _f, _f, _n, _n }; operatorMapping["f*"] = new PdfLiteral[]{ _fStar, _fStar, _n, _n }; operatorMapping["B"] = new PdfLiteral[]{ _B, _f, _S, _n }; operatorMapping["B*"] = new PdfLiteral[]{ _BStar, _fStar, _S, _n }; operatorMapping["b"] = new PdfLiteral[] { _b, _f, _s, _n }; operatorMapping["b*"] = new PdfLiteral[]{ _bStar, _fStar, _s, _n }; } } 

Attenzione: questo editor di esempio è molto semplice:

  • Considera solo la trasparenza creata dai parametri ExtGState ca e CA , in particolare ignora le maschere.
  • Non cerca operazioni che salvano o ripristinano lo stato della grafica.

Queste limitazioni possono essere facilmente revocate ma richiedono più codice di quello appropriato per una risposta StackOverflow.

Applicando questo editor al file di esempio dell’OP come questo

 string source = @"test3.pdf"; string dest = @"test3-noTransparency.pdf"; using (PdfReader pdfReader = new PdfReader(source)) using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(dest, FileMode.Create, FileAccess.Write))) { PdfContentStreamEditor editor = new TransparentGraphicsRemover(); for (int i = 1; i <= pdfReader.NumberOfPages; i++) { editor.EditPage(pdfStamper, i); } } 

risultati in un file PDF senza la filigrana.

Non ho gli strumenti con cui l'OP ha esportato il contenuto con cui scrivere , NitroPDF e Foxit , quindi non ho potuto eseguire un test finale. Adobe Acrobat (versione 9.5) almeno all'esportazione in Word non include la filigrana.

Se gli strumenti dell'OP presentano ancora tracce della filigrana nei file di Word esportati, è ansible migliorare facilmente questa class per eliminare effettivamente le operazioni di creazione e disegno del percorso mentre la trasparenza è triggers.

Lo stesso in Java

Ho iniziato a implementarlo per iText in Java e solo successivamente ho realizzato che l'OP aveva iTextSharp in .Net nella sua mente. Ecco le classi Java equivalenti:

 public class PdfContentStreamEditor extends PdfContentStreamProcessor { /** * This method edits the immediate contents of a page, ie its content stream. * It explicitly does not descent into form xobjects, patterns, or annotations. */ public void editPage(PdfStamper pdfStamper, int pageNum) throws IOException { PdfReader pdfReader = pdfStamper.getReader(); PdfDictionary page = pdfReader.getPageN(pageNum); byte[] pageContentInput = ContentByteUtils.getContentBytesForPage(pdfReader, pageNum); page.remove(PdfName.CONTENTS); editContent(pageContentInput, page.getAsDict(PdfName.RESOURCES), pdfStamper.getUnderContent(pageNum)); } /** * This method processes the content bytes and outputs to the given canvas. * It explicitly does not descent into form xobjects, patterns, or annotations. */ public void editContent(byte[] contentBytes, PdfDictionary resources, PdfContentByte canvas) { this.canvas = canvas; processContent(contentBytes, resources); this.canvas = null; } /** * 

* This method writes content stream operations to the target canvas. The default * implementation writes them as they come, so it essentially generates identical * copies of the original instructions the {@link ContentOperatorWrapper} instances * forward to it. *

*

* Override this method to achieve some fancy editing effect. *

*/ protected void write(PdfContentStreamProcessor processor, PdfLiteral operator, List operands) throws IOException { int index = 0; for (PdfObject object : operands) { object.toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer()); canvas.getInternalBuffer().append(operands.size() > ++index ? (byte) ' ' : (byte) '\n'); } } // // constructor giving the parent a dummy listener to talk to // public PdfContentStreamEditor() { super(new DummyRenderListener()); } // // Overrides of PdfContentStreamProcessor methods // @Override public ContentOperator registerContentOperator(String operatorString, ContentOperator operator) { ContentOperatorWrapper wrapper = new ContentOperatorWrapper(); wrapper.setOriginalOperator(operator); ContentOperator formsrOperator = super.registerContentOperator(operatorString, wrapper); return formsrOperator instanceof ContentOperatorWrapper ? ((ContentOperatorWrapper)formsrOperator).getOriginalOperator() : formsrOperator; } @Override public void processContent(byte[] contentBytes, PdfDictionary resources) { this.resources = resources; super.processContent(contentBytes, resources); this.resources = null; } // // members holding the output canvas and the resources // protected PdfContentByte canvas = null; protected PdfDictionary resources = null; // // A content operator class to wrap all content operators to forward the invocation to the editor // class ContentOperatorWrapper implements ContentOperator { public ContentOperator getOriginalOperator() { return originalOperator; } public void setOriginalOperator(ContentOperator originalOperator) { this.originalOperator = originalOperator; } @Override public void invoke(PdfContentStreamProcessor processor, PdfLiteral operator, ArrayList operands) throws Exception { if (originalOperator != null && !"Do".equals(operator.toString())) { originalOperator.invoke(processor, operator, operands); } write(processor, operator, operands); } private ContentOperator originalOperator = null; } // // A dummy render listener to give to the underlying content stream processor to feed events to // static class DummyRenderListener implements RenderListener { @Override public void beginTextBlock() { } @Override public void renderText(TextRenderInfo renderInfo) { } @Override public void endTextBlock() { } @Override public void renderImage(ImageRenderInfo renderInfo) { } } }

( PdfContentStreamEditor.java )

 public class TransparentGraphicsRemover extends PdfContentStreamEditor { @Override protected void write(PdfContentStreamProcessor processor, PdfLiteral operator, List operands) throws IOException { String operatorString = operator.toString(); if ("gs".equals(operatorString)) { updateTransparencyFrom((PdfName) operands.get(0)); } PdfLiteral[] mapping = operatorMapping.get(operatorString); if (mapping != null) { int index = 0; if (strokingAlpha < 1) index |= 1; if (nonStrokingAlpha < 1) index |= 2; operator = mapping[index]; operands.set(operands.size() - 1, operator); } super.write(processor, operator, operands); } // The current transparency values; beware: save and restore state operations are ignored! float strokingAlpha = 1; float nonStrokingAlpha = 1; void updateTransparencyFrom(PdfName gsName) { PdfDictionary extGState = getGraphicsStateDictionary(gsName); if (extGState != null) { PdfNumber number = extGState.getAsNumber(PdfName.ca); if (number != null) nonStrokingAlpha = number.floatValue(); number = extGState.getAsNumber(PdfName.CA); if (number != null) strokingAlpha = number.floatValue(); } } PdfDictionary getGraphicsStateDictionary(PdfName gsName) { PdfDictionary extGStates = resources.getAsDict(PdfName.EXTGSTATE); return extGStates.getAsDict(gsName); } // // Map from an operator name to an array of operations it becomes depending // on the current graphics state: // // * [0] the operation in case of no transparency // * [1] the operation in case of stroking transparency // * [2] the operation in case of non-stroking transparency // * [3] the operation in case of stroking and non-stroking transparency // static Map operatorMapping = new HashMap(); static { PdfLiteral _S = new PdfLiteral("S"); PdfLiteral _s = new PdfLiteral("s"); PdfLiteral _f = new PdfLiteral("f"); PdfLiteral _fStar = new PdfLiteral("f*"); PdfLiteral _B = new PdfLiteral("B"); PdfLiteral _BStar = new PdfLiteral("B*"); PdfLiteral _b = new PdfLiteral("b"); PdfLiteral _bStar = new PdfLiteral("b*"); PdfLiteral _n = new PdfLiteral("n"); operatorMapping.put("S", new PdfLiteral[]{ _S, _n, _S, _n }); operatorMapping.put("s", new PdfLiteral[]{ _s, _n, _s, _n }); operatorMapping.put("f", new PdfLiteral[]{ _f, _f, _n, _n }); operatorMapping.put("F", new PdfLiteral[]{ _f, _f, _n, _n }); operatorMapping.put("f*", new PdfLiteral[]{ _fStar, _fStar, _n, _n }); operatorMapping.put("B", new PdfLiteral[]{ _B, _f, _S, _n }); operatorMapping.put("B*", new PdfLiteral[]{ _BStar, _fStar, _S, _n }); operatorMapping.put("b", new PdfLiteral[]{ _b, _f, _s, _n }); operatorMapping.put("b*", new PdfLiteral[]{ _bStar, _fStar, _s, _n }); } } 

( TransparentGraphicsRemover.java )

 @Test public void testRemoveTransparentGraphicsTest3() throws IOException, DocumentException { try ( InputStream resource = getClass().getResourceAsStream("test3.pdf"); OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "test3-noTransparency.pdf"))) { PdfReader pdfReader = new PdfReader(resource); PdfStamper pdfStamper = new PdfStamper(pdfReader, result); PdfContentStreamEditor editor = new TransparentGraphicsRemover(); for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) { editor.editPage(pdfStamper, i); } pdfStamper.close(); } } 

(estratto da EditPageContent.java )