< >6.1節(Accessing an existing PDF with PdfReader)讀書筆記


前言

從這一節開始內容集中到操作現有的pdf文檔,如何創建pdf文檔已經在前五節中有了很詳細的說明。這一大章的英文名為Manipulating existing PDF documents,在定下這個名字之前出版社的建議是Editing PDF。但是PDF不是一個適合編輯(edit)的文檔格式,PDF是一個呈現的格式,和我們平常用到的word不一樣。在word中內容是分布在不同的頁上,所以如果用不同的應用程序打開內容就不太一致,比如對同一個文本片段用office打開會出現在頁面X上,但如果用Open Office打開就可能會出現在頁面Y上,這也是大家選擇PDF文檔的理由之一。

在PDF文檔中,不管用什么應用程序打開,文檔中莫頁的字符或者符號都有其固定位置。這是一個優勢但相應也是一個缺點,假設我們希望將一個句子中的單詞"edit"修改為"manipulate",那么我們不得不reflow文本,而且其后單詞的位置都要重新計算。所以如果我們要編輯一個pdf文檔最好的方法是從源頭開始,比如你是用word轉換工具將word轉換為pdf,那么就先修改word中的內容然后再進行轉換。這里為了對應說法我就將manipulate說成操作,現在在我們對文檔操作之前先要學會如何讀取一個pdf文檔並獲取一些信息:如文檔有多少頁啊,用的那種頁面大小等等,這些都可以通過PdfReader類來實現。

Retrieving information about the document and its pages

在我們的第一個列子中我們會檢測第一節生成的Pdf文檔,具體代碼如下:

listing 6.1 PageInformation.cs

public void Inspect(StreamWriter writer, string fileName)
{
    PdfReader reader = new PdfReader(fileName);
    writer.WriteLine(fileName);
    writer.Write("Number of pages: ");
    writer.WriteLine(reader.NumberOfPages);
    Rectangle mediabox = reader.GetPageSize(1);
    writer.Write("Size of page 1: [");
    writer.Write(mediabox.Left);
    writer.Write(',');
    writer.Write(mediabox.Bottom);
    writer.Write(',');
    writer.Write(mediabox.Right);
    writer.Write(',');
    writer.Write(mediabox.Top);
    writer.WriteLine("]");

    writer.Write("Rotation of page 1:");
    writer.WriteLine(reader.GetPageRotation(1));
    writer.Write("Page size with rotation of page 1: ");
    writer.WriteLine(reader.GetPageSizeWithRotation(1));
    writer.Write("Is rebuilt? ");
    writer.WriteLine(reader.IsRebuilt());
    writer.Write("Is encrypted? ");
    writer.WriteLine(reader.IsEncrypted());

    writer.WriteLine();
    writer.Flush();
}

以下為生產的文本文件內容:

results/part1/chapter01/hello_landscape1.pdf
Number of pages: 1
Size of page 1: [0.0,0.0,612.0,792.0]
Rotation of page 1: 90
Page size with rotation of page 1:
Rectangle: 792.0x612.0 (rot: 90 degrees)
Is rebuilt? false
Is encrypted? false


results/part1/chapter01/hello_landscape2.pdf
Number of pages: 1
Size of page 1: [0.0,0.0,792.0,612.0]
Rotation of page 1: 0
Page size with rotation of page 1:
Rectangle: 792.0x612.0 (rot: 0 degrees)
Is rebuilt? false
Is encrypted? false


results/part1/chapter03/movie_templates.pdf
Number of pages: 8
Size of page 1: [0.0,0.0,595.0,842.0]
Rotation of page 1: 90
Page size with rotation of page 1:
Rectangle: 842.0x595.0 (rot: 90 degrees)
Is rebuilt? false
Is encrypted? false


results/part1/chapter05/hero1.pdf
Number of pages: 1

Size of page 1: [-1192.0,-1685.0,1192.0,1685.0]
Rotation of page 1: 0
Page size with rotation of page 1:
Rectangle: 2384.0x3370.0 (rot: 0 degrees)
Is rebuilt? false
Is encrypted? false

在這一節中PdfReader中最重要是NumberOfPages屬性和GetPageSizeWithRotation方法,通過NumberOfPages屬性我們可以對現有文檔進行循環操作,而后一個方法是GetPageSize方法和GetPageRotation方法的組合。

PAGE SIZE

以上代碼的前兩個列子說明是一下兩行代碼的區別:

Document document = new Document(new Rectangle(792, 612));
Document document = new Document(PageSize.LETTER.Rotate());

這個區別在我們導出文檔以及和存在文檔添加格外內容的時候有很大的影響。大家還要注意的是最后一個列子中大家可以看到左下角的坐標不為(0,0)。

BROKEN PDFS

但我們用Adobe Reader打開一個損壞的文檔時,Adobe會有一個提示:"There was an error opening this document. The file is damaged could not be repaire."。PdfReader類對於這些文檔的處理方式也是一樣會拋出InvalidException異常還有一些格外的信息"Rebuild failed:trailer not found;original message:PDF starxref not found"。如果這種情況發生了,那么iTex也就無法處理這個文檔。

在其他情況下如果文檔損壞的不是很嚴重,Adobe Reader會自動修復並有以下警告提示:"The file is damaged but is being repaired"。PdfReader也是同樣的處理方式,具體到代碼中可以用IsRebulid方法去檢測PDF是否需要修復。這是損壞文檔的處理方式,但我們還需要知道加密文檔的處理方式。

ENCRYPTED PDFS

PDF文件由兩種密碼保護:user password和owner password。如果是被user password保護我們在打開pdf文檔之前就需要輸入密碼。如果文檔有owner password則需要將password也傳入到PdfReader的構造器中,否則會BadPasswordException異常拋出,更加詳細的加密信息大家可以看書的12節。

Reducing the memory use of PdfReader

在操作pdf文檔的代碼中我們一般會創建PdfReader的一個實例,然后為其傳入一個string類型參數(代表已存在文檔的路徑),使用這個構造器會導致PdfReader將大量的PDF對象轉換為對應的java(iText)或者Net(iTextSharp)對象。這樣一個較大的文檔就有消耗很大的內容,而你可能只有其中某幾頁有興趣,在這種情況下我們可以選擇部分讀取文檔。

PARTIAL READS

使用PdfReader將其全部讀取時和部分讀取的代碼如下:

listing 6.2 MemoryInfo.cs

public override void CreateFile(string fileName)
{
    FileStream fs = new FileStream(fileName, FileMode.Create);
    StreamWriter writer = new StreamWriter(fs);
    string movieFileName = new MovieTemplates().ContructFile();

    using (writer)
    {
        FullRead(writer, movieFileName);
        PartialRead(writer, movieFileName);
    }
}

public void  FullRead(StreamWriter writer, string fileName)
{
    long  beforeCreate = GC.GetTotalMemory(false);
    PdfReader reader = new PdfReader(fileName);
    long afterCreate = GC.GetTotalMemory(false);
    writer.WriteLine(string.Format("Full Read :Before Create: {0}, After Create: {1}, Consum :{2}", beforeCreate, afterCreate, afterCreate - beforeCreate));
    writer.Flush();
}

public void PartialRead(StreamWriter writer, string fileName)
{
    long beforeCreate = GC.GetTotalMemory(false);
    PdfReader reader = new PdfReader(new RandomAccessFileOrArray(fileName), null);
    long afterCreate = GC.GetTotalMemory(false);
    writer.WriteLine(string.Format("Part Read :Before Create: {0}, After Create: {1}, Consum :{2}", beforeCreate, afterCreate, afterCreate - beforeCreate));
    writer.Flush();
}

在以上代碼中我們使用PdfReader的另一個構造器進行部分讀取,從生成的文本文件大家可以看到內存還是減少了很多。當我們部分讀取文檔時,在開始使用PdfReader對象之前會消耗一些更多的內存,但PdfReader不會緩存那些不必要的對象,這個也是一個很大的區別。所以如果我們處理大的文檔,可以優先選擇使用RandomAccessFileOrArray對象的PdfReader構造器,使用PdfReader還有另一個減少內存損耗的方法。

SELECTING PAGES

通過指定頁面的讀取也可以減少內存的消耗,具體見以下代碼:

listing 6.3 SelectPages.cs

reader.SelectPages("4-8");

SelectPages方法的語法看起來如下:

[!][o][odd][e][even]start[-end]

我們可以使用逗號(,)來組合多個選擇范圍,!符號的意思是移除已經選擇了的頁面。選擇的范圍是遞增;以上中的start和end可以不進行設置,但如果兩個都沒有設置的話則至少有一個o(odd,選擇所有的奇數頁)或者設置一個e(even,選擇所有的偶數頁)。在以上代碼中如果在SelectPages方法之前去獲取頁面的總數我們會得知文檔有8頁,然后在SelectPages方法之后再去獲取頁數時只會返回5:page 4,5,6,7和8。這個時候如果調用一下代碼:

那么就會有NullPointerException異常,因為在PdfReader對象中沒有第六頁。

總結

這一節只是簡單的介紹了PdfReader類的一些常用屬性和方法,並對其內存損耗也進行一些討論,后續我們就會開始對現有文檔進行操作,最后就是代碼下載

同步

此文章已同步到目錄索引:iText in Action 2nd 讀書筆記。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM