web-dev-qa-db-ja.com

iTextSharp既存のPDF)のテキストをフォーマットを失うことなく置き換える

私は2週間インターネットを検索していて、私の問題に対するいくつかの興味深い解決策を見つけましたが、何も私に答えを与えていないようです。

私の目標は次のことを行うことです。

静的PDFファイルでテキストを見つけて、このテキストを別のテキストに置き換えたい。コンテンツのデザインはそのままにしておきたいです。本当に大変ですか?

私は方法を見つけましたが、すべての情報を失いました:

 using (PdfReader reader = new PdfReader(path))
        {

            StringBuilder text = new StringBuilder();
            for (int i = 1; i <= reader.NumberOfPages; i++)
            {
                text.Append(PdfTextExtractor.GetTextFromPage(reader, i));
                text.Replace(txt_SuchenNach.Text, txt_ErsetzenMit.Text);
            }

            return text.ToString();
        }

2回目の試行ははるかに優れていましたが、内部のテキストを変更できるフィールドが必要です。

 string fileNameExisting =path;
        string fileNameNew = @"C:\TEST.pdf";

        using (FileStream existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
        using (FileStream newFileStream = new FileStream(fileNameNew, FileMode.Create))
        {
            // PDF öffnen
            PdfReader pdfReader = new PdfReader(existingFileStream);


            PdfStamper stamper = new PdfStamper(pdfReader, newFileStream);

            var form = stamper.AcroFields;
            var fieldKeys = form.Fields.Keys;
            foreach (string fieldKey in fieldKeys)
            {                    
                var value = pdfReader.AcroFields.GetField(fieldKey);
                form.SetField(fieldKey, value.Replace(txt_SuchenNach.Text, txt_ErsetzenMit.Text));
            }

            // Textfeld unbearbeitbar machen (sieht aus wie normaler text)
            stamper.FormFlattening = true;

            stamper.Close();
            pdfReader.Close();
        }

これにより、残りのテキストのフォーマットが維持され、検索したテキストのみが変更されます。テキストフィールドにないテキストの解決策が必要です。

あなたのすべての答えとあなたの助けに感謝します。

8
Kevin Plaul

一般的な問題は、テキストオブジェクトが、特定の文字に特定のグリフが割り当てられた埋め込みフォントを使用する場合があることです。つまり「abcdef」のようなテキストを含むテキストオブジェクトがある場合、埋め込まれたフォントには、これらの文字(「abcdef」文字)のグリフのみが含まれる場合がありますが、他の文字のグリフは含まれません。したがって、「abcdef」を「xyz」に置き換えると、これらの文字を表示するために使用できるグリフがないため、PDFはこれらの「xyz」を表示しません。

したがって、次のワークフローを検討します。

  • すべてのテキストオブジェクトを繰り返し処理します。
  • 新しいテキストオブジェクトを追加 PDFファイルの上に最初から作成し、同じプロパティ(フォント、位置など)を設定しますが、テキストを変更します。この手順では、元のPDF)で使用されたものと同じフォントがインストールされている必要がありますが、インストールされているフォントを確認し、新しいテキストオブジェクトに別のフォントを使用できます。このようにiTextSharpまたは別のPDFツールは、新しいテキストオブジェクトの新しいフォントオブジェクトを埋め込みます。
  • 複製されたテキストオブジェクトを作成したら、元のテキストオブジェクトを削除します。
  • 上記のワークフローですべてのテキストオブジェクトを処理します。
  • 変更したPDFドキュメントを新しいファイルに保存します。
4
Eugene

私は同じ要件に取り組んできましたが、次の手順でこれを達成できます。

ステップ1:ソースPDFファイルと宛先ファイルのパスを見つける

ステップ2:ソースPDFファイルを読み取り、置換する文字列の場所を検索します

ステップ3:文字列を新しいものと交換します。

using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using PDFExtraction;    
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace PDFReplaceTextUsingItextSharp
{
    public partial class ExtractPdf : System.Web.UI.Page
    {
        static iTextSharp.text.pdf.PdfStamper stamper = null;
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void Replace_Click(object sender, EventArgs e)
        {
            string ReplacingVariable = txtReplace.Text; 
            string sourceFile = "Source File Path";
            string descFile = "Destination File Path";
            PdfReader pReader = new PdfReader(sourceFile);
            stamper = new iTextSharp.text.pdf.PdfStamper(pReader, new System.IO.FileStream(descFile, System.IO.FileMode.Create));
            PDFTextGetter("ExistingVariableinPDF", ReplacingVariable , StringComparison.CurrentCultureIgnoreCase, sourceFile, descFile);
            stamper.Close();
            pReader.Close();
        }


        /// <summary>
        /// This method is used to search for the location words in pdf and update it with the words given from replacingText variable
        /// </summary>
        /// <param name="pSearch">Searchable String</param>
        /// <param name="replacingText">Replacing String</param>
        /// <param name="SC">Case Ignorance</param>
        /// <param name="SourceFile">Path of the source file</param>
        /// <param name="DestinationFile">Path of the destination file</param>
        public static void PDFTextGetter(string pSearch, string replacingText, StringComparison SC, string SourceFile, string DestinationFile)
        {
            try
            {
                iTextSharp.text.pdf.PdfContentByte cb = null;
                iTextSharp.text.pdf.PdfContentByte cb2 = null;
                iTextSharp.text.pdf.PdfWriter writer = null;
                iTextSharp.text.pdf.BaseFont bf = null;

                if (System.IO.File.Exists(SourceFile))
                {
                    PdfReader pReader = new PdfReader(SourceFile);


                    for (int page = 1; page <= pReader.NumberOfPages; page++)
                    {
                        myLocationTextExtractionStrategy strategy = new myLocationTextExtractionStrategy();
                        cb = stamper.GetOverContent(page);
                        cb2 = stamper.GetOverContent(page);

                        //Send some data contained in PdfContentByte, looks like the first is always cero for me and the second 100, 
                        //but i'm not sure if this could change in some cases
                        strategy.UndercontentCharacterSpacing = (int)cb.CharacterSpacing;
                        strategy.UndercontentHorizontalScaling = (int)cb.HorizontalScaling;

                        //It's not really needed to get the text back, but we have to call this line ALWAYS, 
                        //because it triggers the process that will get all chunks from PDF into our strategy Object
                        string currentText = PdfTextExtractor.GetTextFromPage(pReader, page, strategy);

                        //The real getter process starts in the following line
                        List<iTextSharp.text.Rectangle> MatchesFound = strategy.GetTextLocations(pSearch, SC);

                        //Set the fill color of the shapes, I don't use a border because it would make the rect bigger
                        //but maybe using a thin border could be a solution if you see the currect rect is not big enough to cover all the text it should cover
                        cb.SetColorFill(BaseColor.WHITE);

                        //MatchesFound contains all text with locations, so do whatever you want with it, this highlights them using PINK color:

                        foreach (iTextSharp.text.Rectangle rect in MatchesFound)
                        {
                            //width
                            cb.Rectangle(rect.Left, rect.Bottom, 60, rect.Height);
                            cb.Fill();
                            cb2.SetColorFill(BaseColor.BLACK);
                            bf = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);

                            cb2.SetFontAndSize(bf, 9);

                            cb2.BeginText();
                            cb2.ShowTextAligned(0, replacingText, rect.Left, rect.Bottom, 0);   
                            cb2.EndText();
                            cb2.Fill();
                        }

                    }
                }

            }
            catch (Exception ex)
            {

            }

        }

    }
}
2
Pradeep Kumar