<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何批量給PDF添加水印?

          共 3872字,需瀏覽 8分鐘

           ·

          2022-05-12 23:55

          我們有時候需要把一些機(jī)密文件發(fā)給多個客戶,為了避免客戶泄露文件,會在機(jī)密文件中添加水印。每個客戶收到的文件內(nèi)容相同,但是水印都不相同。這樣一來,如果資料泄露了,通過水印就知道是從誰手上泄露的。

          今天,一個做市場的朋友找我咨詢PDF加水印的問題,如下圖所示:

          他有一個Excel文件,文件里面有10000個經(jīng)銷商的名字,他要把價目表PDF發(fā)給這些經(jīng)銷商,每個經(jīng)銷商收到的PDF文件上面的水印都是這個經(jīng)銷商自己的名字。

          這個需求手動操作肯定要累死人。但是如果用Python來做,就非常簡單。代碼不超過30行。

          準(zhǔn)備環(huán)境

          要完成這個需求,需要安裝兩個模塊,分別叫做reportlabpikepdf。使用Pip安裝就可以了:

          python3?-m?pip?install?reportlab?pikepdf

          然后,需要找到一個.ttf或者.ttc格式的中文字體。你可以直接從網(wǎng)上下載中文字體文件。也可以使用系統(tǒng)自帶的中文字體。這里以尋找macOS系統(tǒng)默認(rèn)的宋體為例。

          macOS系統(tǒng)字體在/System/Library/Fonts,宋體對應(yīng)的.ttc文件地址是/System/Library/Fonts/Supplemental/Songti.ttc。對于系統(tǒng)默認(rèn)的字體,我們只需要知道它的對應(yīng)的文件名叫做Songti.ttc就可以了。如果是從網(wǎng)上下載的第三方字體,需要使用絕對路徑或者相對于項目代碼的相對路徑。

          獲得經(jīng)銷商名字對應(yīng)的列表

          由于這位朋友不會使用pandas,那么我們就盡量使用Python原生的方法來獲得經(jīng)銷商名字列表。假設(shè)經(jīng)銷商信息對應(yīng)的Excel如下圖所示:

          我們首先把這個Excel文件導(dǎo)出成csv文件:

          然后,我們用Python讀取這個csv文件,獲得經(jīng)銷商名字列表:

          import?csv
          with?open('經(jīng)銷商信息.csv')?as?f:
          ????reader?=?csv.DictReader(f)
          ????name_list?=?[x['經(jīng)銷商名字']?for?x?in?reader]

          print(name_list)

          運(yùn)行效果如下圖所示:

          生成水印PDF

          一般來說,我們不能直接把一段文字作為水印添加到另一個PDF文件中。我們只有先把這段文字生成圖片或者生成水印PDF文件,然后把這個圖片或者水印PDF作為『圖層』覆蓋到目標(biāo)PDF上面。

          因此,現(xiàn)在需要給每一個經(jīng)銷商生成對應(yīng)的水印PDF文件。這個PDF中只含有水印文字。效果如下圖所示:

          對應(yīng)的代碼create_watermark.py如下:

          import?csv
          from?pathlib?import?Path
          from?reportlab.lib?import?units
          from?reportlab.pdfgen?import?canvas
          from?reportlab.pdfbase?import?pdfmetrics
          from?reportlab.pdfbase.ttfonts?import?TTFont


          with?open('經(jīng)銷商信息.csv')?as?f:
          ????reader?=?csv.DictReader(f)
          ????name_list?=?[x['經(jīng)銷商名字']?for?x?in?reader]

          pdfmetrics.registerFont(TTFont('Songti',?'Songti.ttc'))?#?加載中文字體

          water_mark_folder?=?Path('water_pdf')?#?用一個文件夾存放所有的水印PDF
          water_mark_folder.mkdir(exist_ok=True)

          for?name?in?name_list:
          ????path?=?str(water_mark_folder?/?Path(f'{name}.pdf'))
          ????c?=?canvas.Canvas(path,?pagesize=(200?*?units.mm,?200?*?units.mm))?#?生成畫布,長寬都是200毫米
          ????c.translate(0.1?*?200?*?units.mm,?0.1?*?200?*?units.mm)??
          ????c.rotate(45)??#?把水印文字旋轉(zhuǎn)45°
          ????c.setFont('Songti',?35)??#?字體大小
          ????c.setStrokeColorRGB(0,?0,?0)??#?設(shè)置字體顏色
          ????c.setFillColorRGB(0,?0,?0)??#?設(shè)置填充顏色
          ????c.setFillAlpha(0.3)??#?設(shè)置透明度,越小越透明
          ????c.drawString(0,?0,?f'{name}專用價目表,嚴(yán)禁泄露!')
          ????c.save()

          代碼的具體作用,已經(jīng)寫到注釋中了。運(yùn)行以后會在當(dāng)前項目根目錄生成water_pdf文件夾,里面就是生成的水印PDF。

          合并水印與目標(biāo)PDF

          最后一步,把每一個經(jīng)銷商的水印PDF與目標(biāo)PDF進(jìn)行合并。水印PDF作為一個圖層覆蓋到目標(biāo)PDF上面。

          使用pikepdf完成這個工作非常簡單,編寫一個combine.py文件,代碼如下:

          import?glob
          from?pathlib?import?Path
          from?pikepdf?import?Pdf,?Page,?Rectangle


          water_pdf_list?=?glob.glob('water_pdf/*.pdf')
          result?=?Path('result')
          result.mkdir(exist_ok=True)

          col?=?2??#?每頁多少列水印
          row?=?3??#?每頁多少行水印

          for?path?in?water_pdf_list:
          ????target?=?Pdf.open('./PythonisinstanceGolang.pdf')??#?必須每次重新打開PDF,因為添加水印是inplace的操作
          ????file?=?Path(path)
          ????name?=?file.stem
          ????water_mark_pdf?=?Pdf.open(path)
          ????water_mark?=?water_mark_pdf.pages[0]

          ????for?page?in?target.pages:
          ????????for?x?in?range(col):??#?每一行顯示多少列水印
          ????????????for?y?in?range(row):?#?每一頁顯示多少行PDF
          ????????????????page.add_overlay(water_mark,
          ?????????????????????????????????Rectangle(page.trimbox[2]?*?x?/?col,
          ???????????????????????????????????????????page.trimbox[3]?*?y?/?row,
          ???????????????????????????????????????????page.trimbox[2]?*?(x?+?1)?/?col,
          ???????????????????????????????????????????page.trimbox[3]?*?(y?+?1)?/?row))

          ????result_name?=?Path('result',?f'{name}_添加水印.pdf')
          ????target.save(str(result_name))

          運(yùn)行以后,會在項目根目錄生成一個result文件夾,里面就是添加了水印的PDF文件了,如下圖所示:

          這里有必要對代碼中的一些地方進(jìn)行解釋。帶上行號的代碼如下圖所示:

          代碼第21行和22行,有兩個for循環(huán),他們的作用是給一個頁面上添加多個水印。請大家注意下圖我畫圈的地方:

          每一頁都有6個水印,分成3行2列。其中的3行對應(yīng)了變量row的值。2列對應(yīng)了變量col的值。大家也可以根據(jù)自己的需要修改這兩個數(shù)字。甚至每一頁的水印隨機(jī)變換位置,防止被去水印的程序移除。

          page.trimbox[2]是PDF頁面的寬度,page.trimbox[3]是PDF頁面的高度。

          總結(jié)

          大家注意在這篇文章中,我把任務(wù)分成了3個部分,分別是:

          • Excel轉(zhuǎn)CSV,讓Python方便讀取
          • Python讀取CSV生成水印PDF
          • 水印PDF與目標(biāo)PDF文件合并

          這三個部分的代碼是可以合并在一個.py文件里面的,但是我沒有這樣做,是考慮到問這個問題的同學(xué)不是程序員,Python水平只是入門,如果合并在一起,代碼量多了以后,出問題都不知道錯在哪里。

          在計算機(jī)領(lǐng)域,所有問題都可以通過把問題拆分成多個部分分別單獨運(yùn)行或者增加若干個中間層來解決。今天用的方法就是把問題拆分的方法。對于初學(xué)者來說,每一步都是相對獨立的,都能立刻看到效果。第二步只需要依賴第一步的結(jié)果,第三步只需要依賴第二步的結(jié)果,這樣每一步的輸入輸出非常清楚,可以顯著降低問題的復(fù)雜度。如果報錯了,也更容易知道是哪個地方有問題。

          END

          往期推薦
          1、瀏覽器可以運(yùn)行 Python 代碼了,Python 也許會變成前后端通吃的語言
          2、Python 你可能從未聽說過的5種隱藏技巧
          3、用Python寫個魂斗羅
          4、如何讓 Python 腳本在工作日運(yùn)行?
          5、一日一技:協(xié)程與多進(jìn)程的完美結(jié)合
          點擊關(guān)注公眾號,閱讀更多精彩內(nèi)容
          瀏覽 53
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜操一操网 | 久久久久青青 | 最新一区二区在线 | 国精品人伦一区二区三区蜜桃 | 成人免费网站黄 |