เรียนรู้เบื้องต้นกับการใส่ลายน้ำ(Watermark)ให้กับเอกสาร PDF ของเราด้วย iTextSharp

               ในบทความนี้เราก็ยังคงอยู่กับเรื่องการทำภาพลายน้ำอย่างต่อเนื่องจาก EP. ที่แล้วที่พูดถึงการทำข้อความลายน้ำบนภาพที่เราทำการอัพโหลด ถ้าใครยังไม่ได้อ่านสามารถตามไปอ่านกันได้ ที่นี่ นะคะแต่สำหรับ EP. นี้เราจะขอเปลี่ยนบรรยากาศไปทำ ลายน้ำบนไฟล์ PDF โดยใช้เครื่องมือ iTextSharp กันบ้าง เผื่อว่าผู้อ่านบางท่านที่อาจจะกำลังใช้งาน iTextSharp ในการจัดทำไฟล์ PDF อยู่พอดี และอยากจะลองใส่ลายน้ำให้กับงานเอกสารของท่านดูบ้าง จะได้สามารถนำวิธีการนี้ไปประยุกต์ใช้กับงานของตนได้ไม่ทางใดก็ทางหนึ่งนะคะ โดยตัวอย่างที่จะนำมาแนะนำกันจะมีทั้งแบบลายน้ำที่เป็นข้อความ และลายน้ำที่เป็นภาพค่ะ นอกจากนี้ก็ยังมีทั้งแบบที่เป็นการทำภาพลายน้ำทับไฟล์เดิมที่มีอยู่ และแบบที่ทำภาพลายน้ำและบันทึกเป็นไฟล์ใหม่ค่ะ ผู้อ่านจะได้ลองในหลายๆวิธี และสามารถเลือกไปปรับใช้ให้เหมาะกับงานของแต่ละท่านได้เพื่อไม่ให้เป็นการเสียเวลา งั้นเรามาเริ่มทำภาพลายน้ำบนงานเอกสาร PDF ของเรากันเลยดีกว่าค่ะ

แบบที่ 1 : การสร้างลายน้ำแบบข้อความ ซึ่งวิธีการนี้ จะมีการทำ Template ต้นฉบับของลายน้ำแบบข้อความไว้ก่อน และเมื่อต้องการทำไฟล์ลายน้ำ จะต้องทำการอ่านไฟล์ลายน้ำจากต้นแบบมาจัดทำลายน้ำบนไฟล์ที่ต้องการได้ ดังนี้ค่ะ

1.1 ระบุ Library เพิ่มเติม

using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;

1.2 สร้างเมธอดในการสร้าง Template ลายน้ำต้นฉบับ เพื่อใช้ในการทำลายน้ำให้กับไฟล์ PDF ที่ต้องการ

        ////กรณียังไม่เคยสร้างหรือมี Template มาก่อน ให้เรียกใช้งาน CreateTemplate("ข้อความที่ต้องการให้แสดงในเทมเพลต",พาธที่จะสร้างไฟล์เทมเพลตดังกล่าว) 
        public void CreateTemplate(string watermarkText, string targetFileName)
        {
            var document = new Document();

            ////ระบุพาธที่ต้องการสร้างไฟล์เทมเพลต
            var pdfWriter = PdfWriter.GetInstance(document, new FileStream(targetFileName, FileMode.Create));
            ///ระบุค่าต่างๆเกี่ยวกับตัวอักษรที่จะแสดงผลในเทมเพลตลายน้ำที่สร้างขึ้น
            var font = new Font(Font.FontFamily.HELVETICA, 60, Font.NORMAL, BaseColor.LIGHT_GRAY);
            document.Open();

            ////ระบุค่าข้อความ และค่าต่างๆให้กับลายน้ำที่ต้องการ
            ColumnText.ShowTextAligned(pdfWriter.DirectContent, Element.ALIGN_CENTER, new Phrase(watermarkText, font), 300, 400, 45);
            document.Close();
        }

ตัวอย่าง ไฟล์ PDF ของ Template ลายน้ำที่ได้จากการเรียกใช้งานเมธอด CreateTemplate ข้างต้น

1.3 สร้างเมธอดในการจัดทำลายน้ำข้อความให้กับไฟล์ PDF จาก template ลายน้ำต้นฉบับ

public void AddTextWatermark(string sourceFilePath, string watermarkTemplatePath, string targetFilePath)
        {
            ///ระบุพาธไฟล์ต้นทางที่ต้องการทำข้อความลายน้ำ
            var pdfReaderSource = new PdfReader(sourceFilePath);
            ///ระบุพาธไฟล์ปลายทางที่ต้องการบันทึกไฟล์แบบมีลายน้ำ
            var pdfStamper = new PdfStamper(pdfReaderSource, new FileStream(targetFilePath, FileMode.Create));

            ///ระบุพาธของไฟล์ต้นแบบลายน้ำที่จัดทำไว้
            var pdfReaderTemplate = new PdfReader(watermarkTemplatePath);
            var page = pdfStamper.GetImportedPage(pdfReaderTemplate, 1);

            ///ทำการวนลูปเพื่อทำลายน้ำให้กับไฟล์ PDF ทีละหน้า
            for (var i = 0; i < pdfReaderSource.NumberOfPages; i++)
            {
                ///ระบุตำแหน่งในการแสดงผลลายน้ำกับเนื้อหาในไฟล์ PDF กรณีนี้คือวางไว้ใต้เนื้อหา แต่หากต้องการให้อยู่บนเนื้อหาให้เปลี่ยนเป็น GetOverContent แทน 
                var content = pdfStamper.GetUnderContent(i + 1);
                content.AddTemplate(page, 0, 0);
            }

            pdfStamper.Close();
            pdfReaderTemplate.Close();
        }

1.4 เรียกใช้งานเมธอดเพื่อทำลายน้ำให้กับไฟล์ PDF ที่ต้องการ(กรณีนี้สมมุติให้เป็นการกดปุ่มเพื่อเรียกใช้งานเมธอดดังกล่าว)

 protected void btnGenWatermark_Click(object sender, EventArgs e)
        {
             ////กำหนด Path ของไฟล์ PDF ต้นทางที่ต้องการทำลายน้ำ กรณีที่ gen จาก iTextSharp สามารถนำมาประยุกต์ใช้โดยเอาพาธดังกล่าวมาใช้เป็น strSourcePath ได้เลย
            string strSourcePath = HttpContext.Current.Server.MapPath("~/PDF/File/PDFDocument.pdf");

             ////สร้างตัวแปรชื่อไฟล์ PDF ที่จะบันทึกใหม่ในชื่อตัวแปร genName
            string genName = Guid.NewGuid().ToString() + ".pdf";

             ////กรณียังไม่เคยสร้างหรือมี Template มาก่อนให้เรียกใช้งาน CreateTemplate("ข้อความที่ต้องการให้แสดงในเทมเพลต",templatePath)  ทั้งชื่อไฟล์เทมเพลตใหม่ที่จะบันทึก
            string templatePath = HttpContext.Current.Server.MapPath("~/PDF/TemplateWatermarkPDF/" + genName);

             ////ระบุพาธปลายทางที่ต้องการบันทึกไฟล์แบบมีลายน้ำพร้อมทั้งชื่อไฟล์ใหม่ที่จะบันทึก
            string targetPath = HttpContext.Current.Server.MapPath("~/PDF/WatermarkPDF/" + genName);

             ////ตรวจสอบว่ามีไฟล์ดังกล่าวอยู่จริงตามที่อยู่ที่ระบุไว้หรือไม่
            if( File.Exists(strSourcePath))
            { 
                 ////กรณียังไม่เคยสร้างหรือมี Template มาก่อน ให้เรียกใช้งาน CreateTemplate("ข้อความที่ต้องการให้แสดงในเทมเพลต",พาธที่จะสร้างไฟล์เทมเพลตดังกล่าว) 
                CreateTemplate("Copyrights (c) 2023", templatePath);

                 ////เรียกใช้งานเมธอดในการสร้างข้อความลายน้ำตามเทมเพลตที่มี(ตามพาธที่ระบุ) โดยส่งค่าที่อยู่ของไฟล์ต้นทาง พาธเทมเพลตที่ต้องการทำลายน้ำ และพาธปลายทางที่ต้องการบันทึกไฟล์แบบมีลายน้ำ
                AddTextWatermark(strSourcePath, templatePath, targetPath);
            }
        }

ตัวอย่างผลลัพธ์

หมายเหตุ : 

  1. บางครั้งอาจพบว่าการใช้ GetUnderContent อาจทำให้ข้อความลายน้ำโดนเนื้อหาบดบังได้ในบางกรณี จนอาจจะต้องใช้ GetOverContent แทนดังเช่นตัวอย่างต่อไปนี้ 

แต่สำหรับเนื้อหาในไฟล์ PDF โดยทั่วไปแล้ว สามารถใช้งาน GetUnderContent ได้ตามปกติ เพื่อไม่ให้ข้อความลายน้ำบดบังเนื้อหาในเอกสาร

แบบที่ 2 การสร้างลายน้ำแบบรูปภาพให้กับเอกสาร PDF โดยจะขอยกตัวอย่างให้ดู 2 แบบนะคะ คือแบบที่สร้างลายน้ำบนไฟล์ PDF ใหม่ และแบบที่สร้างลายน้ำบนไฟล์ต้นทางเดิมค่ะ 

2.1 แบบสร้างลายน้ำบนไฟล์ใหม่ โดยการสร้างเมธอด AddImageWatermark

    public void AddImageWatermark(string sourceFilePath, string watermarkImagePath, string targetFilePath)
        {
            ///ระบุพาธไฟล์ต้นทางที่ต้องการทำภาพลายน้ำ
            var pdfReader = new PdfReader(sourceFilePath);

            ///ระบุพาธไฟล์ปลายทางที่ต้องการบันทึกไฟล์แบบมีภาพลายน้ำ
            var pdfStamper = new PdfStamper(pdfReader, new FileStream(targetFilePath, FileMode.Create));

            ///ระบุพาธของไฟล์ภาพที่ต้องการนำมาทำภาพลายน้ำ
            var image = iTextSharp.text.Image.GetInstance(watermarkImagePath);
            image.SetAbsolutePosition(200, 400);

            ///ทำการวนลูปเพื่อทำภาพลายน้ำให้กับไฟล์ PDF ทีละหน้า
            for (var i = 0; i < pdfReader.NumberOfPages; i++)
            {
                var content = pdfStamper.GetUnderContent(i + 1);
                content.AddImage(image);
            }

            pdfStamper.Close();
        }

วิธีเรียกใช้งาน (กรณีนี้สมมุติให้เป็นการกดปุ่มเพื่อเรียกใช้งานเมธอดดังกล่าว)

        protected void btnGenWatermark_Click(object sender, EventArgs e)
        {
            ////กำหนด Path ของไฟล์ภาพที่ต้องการทำลายน้ำ
            string strWaterMark = HttpContext.Current.Server.MapPath("~/images/clientpreview.png");
            
////ระบุ Path ของไฟล์ PDF ต้นทางที่ต้องการทำลายน้ำ string strSourcePath = HttpContext.Current.Server.MapPath("~/PDF/File/job_apply_form.pdf");
////สร้างตัวแปรชื่อไฟล์ PDF ที่จะบันทึกใหม่ในชื่อตัวแปร genName string genName = Guid.NewGuid().ToString() + ".pdf";
////ระบุพาธปลายทางที่ต้องการบันทึกไฟล์แบบมีลายน้ำพร้อมทั้งชื่อไฟล์ใหม่ที่จะบันทึก string targetPath = HttpContext.Current.Server.MapPath("~/PDF/WatermarkPDF/" + genName);
////ตรวจสอบว่ามีไฟล์ภาพลายน้ำ และไฟล์ PDF ต้นทางดังกล่าวอยู่จริงตามที่อยู่ที่ระบุไว้หรือไม่ if (File.Exists(strWaterMark) && File.Exists(strSourcePath)) { AddImageWatermark(strSourcePath, strWaterMark, targetPath); } }

2.2 แบบสร้างลายน้ำบนไฟล์เดิม โดยการสร้างเมธอด AddImageWatermarkInExistingFile

 private void AddImageWatermarkInExistingFile(string sourceFilePath,string watermarkImagePath )
        {
             ///อ่านค่าจากพาธไฟล์ต้นทางที่ต้องการทำภาพลายน้ำ
            byte[] bytes = File.ReadAllBytes(sourceFilePath);

             ///ระบุพาธของไฟล์ภาพที่ต้องการนำมาทำภาพลายน้ำ
            var img = iTextSharp.text.Image.GetInstance(watermarkImagePath);

            img.SetAbsolutePosition(200, 400);
            PdfContentByte waterMark;

            using (MemoryStream stream = new MemoryStream())
            {
                 ///ทำภาพลายน้ำในไฟล์ต้นทางที่ต้องการทำภาพลายน้ำ
                PdfReader reader = new PdfReader(bytes);
                using (PdfStamper stamper = new PdfStamper(reader, stream))
                {
                    ///ทำการวนลูปเพื่อทำภาพลายน้ำให้กับไฟล์ PDF ทีละหน้า
                    int pages = reader.NumberOfPages;
                    for (int i = 1; i <= pages; i++)
                    {
                        waterMark = stamper.GetUnderContent(i);
                        waterMark.AddImage(img);
                    }
                }
                bytes = stream.ToArray();
            }
            File.WriteAllBytes(sourceFilePath, bytes);
        }

วิธีเรียกใช้งาน (กรณีนี้สมมุติให้เป็นการกดปุ่มเพื่อเรียกใช้งานเมธอดดังกล่าว)

        protected void btnGenWatermark_Click(object sender, EventArgs e)
        {
            ////กำหนด Path ของไฟล์ภาพที่ต้องการทำลายน้ำ
            string strWaterMark = HttpContext.Current.Server.MapPath("~/images/clientpreview.png");

            ////ระบุ Path ของไฟล์ PDF ต้นทางที่ต้องการทำลายน้ำ
            string strSourcePath = HttpContext.Current.Server.MapPath("~/PDF/File/job_apply_form.pdf");

            ////สร้างตัวแปรชื่อไฟล์ PDF ที่จะบันทึกใหม่ในชื่อตัวแปร genName
            string genName = Guid.NewGuid().ToString() + ".pdf";

             ////ตรวจสอบว่ามีไฟล์ภาพลายน้ำ และไฟล์ PDF ต้นทางดังกล่าวอยู่จริงตามที่อยู่ที่ระบุไว้หรือไม่
            if (File.Exists(strWaterMark) && File.Exists(strSourcePath))
            {
                AddImageWatermarkInExistingFile(strSourcePath,strWaterMark);
             }
        }

ตัวอย่างผลลัพธ์

หมายเหตุ :
1. ทั้งสองวิธีข้างต้นจะให้ผลลัพธ์ของภาพลายน้ำแบบเดียวกัน แต่ต่างกันตรงที่ไฟล์ที่บันทึกภาพลายน้ำ โดยแบบแรกจะสร้างเป็นไฟล์ใหม่ แต่แบบที่สองจะบันทึกลงบนไฟล์เดิม

2. ในการใช้ภาพทำภาพลายน้ำ อาจจะต้องระวังเกี่ยวกับขนาดภาพที่นำมาใช้ในการแสดงผล และอาจต้องทดสอบการจัดวางตำแหน่งของภาพ เพื่อให้ภาพลายน้ำดังกล่าวออกมาสวยงาม และตรงกับความต้องการในการใช้งาน เพราะหากใช้ภาพที่ใหญ่หรือเล็กเกินไป อาจทำให้ไม่เหมาะกับการนำมาใช้งานได้

           ซึ่งทั้งหมดนี้ก็เป็นเพียงวิธีเบื้องต้นในการสร้างภาพลายน้ำด้วย iTextSharp เท่านั้น เราอาจจะต้องลองผิดลองถูก ประยุกต์วิธีการนำไปใช้ไปด้วยกัน และหวังว่าบทความนี้จะเป็นประโยชน์กับผู้อ่านเพื่อให้แต่ละท่านสามารถนำวิธีดังกล่าวไปประยุกต์ใช้เพิ่มเติมกับงานพัฒนาที่ท่านกำลังทำอยู่ หรือหากผู้อ่านท่านใดมีข้อเสนอแนะสามารถให้คำแนะนำมาได้นะคะ ทางเรายินดีรับฟังทุกข้อเสนอแนะ เพื่อเป็นการต่อยอดความรู้ร่วมกันนะคะ แล้วพบกันใหม่ในบทความหน้า และขอบคุณที่ติดตามกันนะคะ ^^

แหล่งอ้างอิง