亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

[轉]為ReportViewer導出的PDF文檔加上水印

2019-11-17 01:59:18
字體:
來源:轉載
供稿:網友

[轉]為ReportViewer導出的PDF文檔加上水印

接到一個頗富挑戰性的需求,Reporting Service或RDLC報表可匯出成Excel、PDF等檔案格式,對一般麻瓜型使用者而言,PDF唯讀,Excel則可修改,業務單位希望在拿到報表紙本時加以區分;換句話說,如果能讓PDF與Excel檔的列印結果有別,即可做為報表結果是否唯讀,有無被修改可能的依據。(姑且排除使用者設法修改PDF檔或將Excel仿製成PDF樣式的情境)

我想到一個做法是為匯出的PDF檔加上浮水印。同一張報表匯出的Word、Excel、PDF檔內容理應一致,當PDF檔被加註浮水印,便足以形成區隔。在PDF檔加上浮水印非難事,用iTextSharp應可搞定,但匯出PDF檔的過程本屬ReportViewer內部運作,不容外人插手,要在匯出PDF檔時動手腳需要點Hacking,好一個讓程式魔人熱血沸騰的挑戰!

分析問題的第一步,先剖析ReportViwer匯出PDF檔的原理:

當執行PDF匯出動作時,實際上是呼叫ReportViewer加掛的HttpHandler,web.config可以看到相關設定:

<add path="Reserved.ReportViewerWebControl.axd" verb="*" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" validate="false" />

在網頁按匯出鈕時,瀏覽器被導向特定URL,傳入OpMode=Export、Format=PDF參數,由HttpHandler傳回當下檢視報表的PDF檔。如要在此過程動手腳,有個不錯的切入點是透過Global.asax或HttpModule攔截BeginRequest事件,遇到呼叫Reserved.ReportViewerWebControl.axd匯檔案時加入自訂邏輯,修改要傳回的檔案內容。但ReportViewer的HttpHandler在PDF檔產生後便立刻寫入HttPRepsonse傳回客戶端,輪不到我們插手,因此下一個挑戰是如何攔截並修改其內容。

此時,asp.net的另一個好用機制派上用場:Response.Filter,它允許我們在HttpResponse將結果byte[]寫入輸出Stream之前,先交由我們自訂的Stream物件處理,可以實現修改後再傳至客戶端的目的。

排版顯示純文字
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
public class ExpFileFilterStream : MemoryStream
{
    private Stream output = null;
    Func<byte[], byte[]> modifier = null;
    private HttpResponse response = null;
    private bool firstFlush = false;
    public ExpFileFilterStream(HttpResponse resp, Func<byte[], byte[]> modifier)
    {
        response = resp;
        output = resp.Filter;
        this.modifier = modifier;
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        //由於ReportViewer會關閉BufferOutput,並分成多段Flush傳回前端,
        //在此重新啟用Buffer功能(因必須得到檔案完整內容再處理),
        //但會漏掉第一次的Flush(),藉以以下邏輯避免第一次部分Flush()
        //註: ReportViewer在分段Flush的大小為81920,當少於此值表示不需略過Flush
        if (!response.BufferOutput && count == 81920)
        {
            response.BufferOutput = true;
            firstFlush = true;
        }
        base.Write(buffer, offset, count);
    }
    public override void Flush()
    {
        if (firstFlush)
        {
            firstFlush = false;
            return;
        }
        //Flush時,將要傳回內容byte[]交由外部邏輯處理後再取回
        byte[] buff = base.ToArray();
        if (modifier != null)
            buff = modifier(buff);
        output.Write(buff, 0, buff.Length);
    }
}

我寫了一個簡單的Filter Stream物件,原理是在Write()時先蒐集ReportViewer HttpHandler要傳回的檔案內容,當Flush()要傳回結果時,將先前接收到的PDF檔案內容(byte[])交由外部邏輯,Func<byte[], byte[]>,進行加蓋浮水印處理,再傳回修改版檔案到真正的OutputStream。

其中有小技巧: ReportViewer HttpHandler為了減少記憶體耗用及提高回應效率,會將Response.BufferOutput設為false,讓匯出檔案內容分成多段Flush()傳回(每段不超過81920 bytes)。由於我們需要接收完整檔案才能進行修改並一次回傳,故不容先傳回部分未修改內容的情形發生。在Write()將Response.BufferOutput改回true即可偷偷取消分段傳回,唯此時第一個分段的Flush()已箭在弦上,故要用一個firstFlush旗標避開第一次Flush()。之後因Response.BufferOutput已被設為true,會等到全部的PDF檔都透過Write()寫入才呼叫Flush(),此時MemoryStream所保存的便是完整PDF檔內容。

在BeginRequest事件加掛Response.Filter的工作,則寫成一個HttpModule。程式很單純,較花工夫的是透過iTextSharp在PDF左上角印上PDF yyyy/MM/dd HH:mm:ss樣式的半透明浮水印,iText歷史悠久、功能強大,網路上不難找到現成範例,很順利就完成了。

排版顯示純文字
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Web;
using iTextSharp.text.pdf;
using iText = iTextSharp.text;
using iTextSharp.text;
using System.IO;
namespace ReportViewerHacking
{
    public class WaterMarkModule : IHttpModule
    {
        #region IHttpModule Members
        public void Dispose()
        {
        }
        public void Init(Httpapplication context)
        {
            context.BeginRequest += context_BeginRequest;
        }
        void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            string url = app.Context.Request.RawUrl;
            var context = app.Context;
            if (url.Contains("Reserved.ReportViewerWebControl.axd"))
            {
                var req = context.Request;
                var resp = context.Response;
                string opType = req["OpType"];
                string name = req["Name"];
                string format = req["Format"];
                if (opType == "Export" && format == "PDF")
                {
                    resp.BufferOutput = true;
                    resp.Filter = new ExpFileFilterStream(resp, (buff) =>
                    {
                        //輸入PDF內容,加上浮水印
                        PdfReader pr = new PdfReader(buff);
                        iText.Rectangle dimension = pr.GetPageSize(1);
                        MemoryStream ms = new MemoryStream();
                        PdfStamper stmp = new PdfStamper(pr, ms);
                        //REF: http://bit.ly/10qirzK
                        BaseFont bf =
                            BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);
                        iText.Font fnt = new iText.Font(bf, 6, iText.Font.NORMAL, BaseColor.BLACK);
                        PdfContentByte cb = stmp.GetOverContent(1);
                        //設定半透明文字
                        PdfGState gstate = new PdfGState();
                        gstate.FillOpacity = 0.2f;
                        gstate.StrokeOpacity = 0.2f;
                        cb.SetGState(gstate);
                        cb.BeginText();
                        cb.SetFontAndSize(bf, 6);
                        cb.SetColorFill(BaseColor.BLACK);
                        cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT,
                            string.Format("PDF {0:yyyy-MM-dd HH:mm:ss}", DateTime.Now),
                            dimension.GetLeft(1), dimension.GetTop(5), 0);
                        cb.EndText();
                        stmp.Close();
                        pr.Close();
                        return ms.ToArray();
                    });
                }
            }
        }
        #endregion
    }
}

將HttpModule掛進ASP.NET網站,之後只要ReportViewer匯出PDF檔,就一律會被偷偷加上浮水印,讓我過了小小當駭客的癮,哈!!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲成人激情在线观看| 国产日韩在线视频| 欧美综合国产精品久久丁香| 成人淫片在线看| 国产精品视频一| 国产小视频国产精品| 97视频网站入口| 色综合视频网站| 久久久久久久久久久网站| 成人中文字幕+乱码+中文字幕| 国产精品丝袜高跟| 精品久久久久久久久久久| 91色精品视频在线| 色播久久人人爽人人爽人人片视av| 日韩视频―中文字幕| 福利一区福利二区微拍刺激| 欧美国产一区二区三区| 国产精品视频免费在线| 欧美性高潮在线| 国产一区二区欧美日韩| 久久九九全国免费精品观看| 影音先锋日韩有码| 91色中文字幕| 一区二区日韩精品| 久久久国产一区| 日韩中文字幕第一页| 国产91精品久久久久| 国产做受高潮69| 国产欧美日韩中文字幕在线| 日韩一区视频在线| 国产精品视频在线观看| 久久天天躁日日躁| 国产精品久久久久久久久久久久| 久久九九精品99国产精品| 国产欧美精品一区二区三区介绍| 欧美精品一区二区三区国产精品| 欧美老少做受xxxx高潮| 日韩有码片在线观看| 成人精品视频久久久久| 日韩精品极品在线观看| 国产精品专区h在线观看| 欧美性xxxxx极品娇小| 亚洲天堂免费在线| 91精品在线影院| 国产欧美一区二区三区四区| 国产精品久久久久久久久久久新郎| 国产精品免费久久久久影院| 欧美色道久久88综合亚洲精品| 精品亚洲一区二区三区四区五区| 97免费视频在线播放| 欧美俄罗斯性视频| 欧美成人午夜剧场免费观看| 亚洲色图五月天| 亚洲精品美女久久| 国产午夜精品一区二区三区| 成人久久久久久| 欧美性极品少妇精品网站| 国产一区二中文字幕在线看| 亚洲新中文字幕| 伊人久久久久久久久久| 亚洲免费电影在线观看| 亚洲一区二区三区四区在线播放| 精品丝袜一区二区三区| 亚洲国产欧美一区二区三区久久| 在线激情影院一区| 伊人精品在线观看| 中文字幕久热精品在线视频| 欧美插天视频在线播放| 高跟丝袜一区二区三区| 91精品国产91久久久久久| 欧美亚州一区二区三区| 97成人精品视频在线观看| 国产在线视频一区| 中文字幕国产亚洲2019| 国产精品久久久久福利| 欧美性猛交xxxx久久久| 中文字幕亚洲欧美一区二区三区| 日韩精品在线播放| 欧美高清视频在线播放| 欧美激情视频在线免费观看 欧美视频免费一| 欧美黑人又粗大| 8050国产精品久久久久久| 亚洲3p在线观看| 欧美激情国产日韩精品一区18| 亚洲精品国产精品久久清纯直播| 国外成人在线播放| 国产精品高潮视频| 亚洲天堂精品在线| 国产精品成久久久久三级| 欧美亚洲免费电影| 国产女精品视频网站免费| 欧美性xxxx极品高清hd直播| 亚洲欧美精品在线| 一区二区三区精品99久久| 一本色道久久综合狠狠躁篇的优点| 欧美日本在线视频中文字字幕| 色综合男人天堂| 黑人巨大精品欧美一区二区一视频| 亚洲欧洲在线看| 色多多国产成人永久免费网站| 亚洲综合中文字幕68页| 亚洲另类欧美自拍| 日韩美女在线播放| 91久久国产综合久久91精品网站| 精品女厕一区二区三区| 国产做受高潮69| 国产精品专区第二| 国产精品自拍网| 国产99久久精品一区二区 夜夜躁日日躁| 久久国产精品视频| 欧美激情免费视频| 国产日韩欧美中文在线播放| 成人免费直播live| 亚洲一区二区免费在线| 中文国产成人精品| 欧美激情在线视频二区| 亚洲高清一二三区| 欧美大学生性色视频| 91精品国产亚洲| 亚洲精品一区二区三区不| 午夜欧美不卡精品aaaaa| 91亚洲精华国产精华| 亚洲欧美国产一本综合首页| 欧美激情一区二区三区高清视频| 欧美色播在线播放| 欧美午夜无遮挡| 日韩一区在线视频| 亚洲成色777777在线观看影院| 亚洲va欧美va国产综合剧情| 91精品视频免费观看| 亚洲aaaaaa| 青青久久av北条麻妃海外网| 亚洲国产成人精品女人久久久| 成人在线视频福利| 理论片在线不卡免费观看| 色偷偷av亚洲男人的天堂| 中文字幕免费国产精品| 久久香蕉国产线看观看av| 久久久成人的性感天堂| 日韩中文在线视频| 精品久久香蕉国产线看观看gif| 国产精品视频永久免费播放| 久久精品夜夜夜夜夜久久| 欧美性生交xxxxx久久久| 日韩精品久久久久久久玫瑰园| 精品少妇一区二区30p| 国产91在线播放精品91| 欧美日韩美女在线| 国产精品综合久久久| 精品一区二区三区电影| 国产一区二区激情| 日韩国产高清污视频在线观看| 国产又爽又黄的激情精品视频| 亚洲激情视频在线| 国产精品亚洲视频在线观看| 在线国产精品视频| 精品久久久久久久久久久久久| 欧美日韩免费在线观看| 国产精品视频公开费视频| 成人美女av在线直播| www.美女亚洲精品| 欧美激情中文字幕在线| 国产伦精品一区二区三区精品视频| 国产欧美日韩中文|