不太懂分類檢索的人可以參考 此網頁 中的資訊.
開發工具與環境:
- iTextSharp: Java 最常用於操作 PDF 的套件是 iText, 此套件也有 .Net 版, 叫做 iTextSharp. ( 下載點)
因為 iTextSharp 的功能跟 Java 版的 iText 大同小異, 所以文件的部分可參考 iText 的 document. (online版) - Visual Studio 2008
- XMP: 請參考 Adobe 的 網頁說明.
- 下載 iTextSharp 並解壓縮後, 會有一個 itextsharp.dll 檔, 記得在專案中將此 dll 加入參考.
因為本文所用到的類別與函式並不多, 所以只需要 using 以下的命名空間:123456using
System;
using
System.IO;
using
System.Text;
using
System.Xml;
using
iTextSharp.text.pdf;
using
iTextSharp.text.xml.xmp;
- 首先是讀取 pdf 中的 XMP 資訊:
- 使用 PdfReader 這個類別.
- 轉換後的字串可以利用 XmlDocument 做其他利用.
- 若要對 XmlDocument 做進一步的查詢, 可參考 此篇 做 XmlNamespaceManager 的初始設定.
- 需注意有可能無法取得 pdf 中的 XMP 資訊, 此時的 Metadata 會是 null.
123456789PdfReader pdfSourceReader =
new
PdfReader(
@"c:\source.pdf"
);
XmlDocument docSource =
new
XmlDocument();
if
(pdfSourceReader.Metadata !=
null
)
{
//用UTF-8編碼, 以避免XML的亂碼
string
strMetadata = Encoding.UTF8.GetString(pdfSourceReader.Metadata);
//載入至XmlDocument
docSource.LoadXml(strMetadata);
}
- 將 XMP 資訊寫入至 pdf 檔中:
- 使用 PdfStamper 這個類別, 做 pdf 檔的複製.
- 由於分類檢索主要是 Dublin Code 的資訊, 所以利用 iTextSharp 的 DublinCoreSchema 類別進行設定.
- 利用 XmpWriter 這個類別包裝新的 XMP 資訊.
- 若要對 XmlDocument 做進一步的查詢, 又不太知道 xmlns 的設定值為何, 可參考 此篇 做 XmlNamespaceManager 的初始設定.
我在這部分寫了一個 GetNs() 函式來處理, 如下:12345678910111213141516171819public
static
XmlNamespaceManager GetNs(XmlDocument doc)
{
XmlNamespaceManager mgr =
new
XmlNamespaceManager(doc.NameTable);
XmlNodeList lstNodes = doc.SelectNodes(
"//child::*"
);
foreach
(XmlNode node
in
lstNodes)
{
foreach
(XmlAttribute attr
in
node.Attributes)
{
if
(attr.Prefix.Equals(
"xmlns"
))
{
if
(!mgr.HasNamespace(attr.LocalName))
{
mgr.AddNamespace(attr.LocalName, attr.Value);
}
}
}
}
return
mgr;
}
- 要從 XmlDocumentA 複製一個 XmlNode 到 XmlDocumentB, 可參考 此網頁 的說明.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758//建立一個設定分類檢索資訊的DublinCoreSchema物件
DublinCoreSchema dcSchema =
new
DublinCoreSchema();
dcSchema.SetProperty(DublinCoreSchema.TITLE,
new
LangAlt(
"RSS 詮釋資料基準範例"
));
dcSchema.SetProperty(DublinCoreSchema.CREATOR,
new
LangAlt(
"行政院研究發展考核委員會"
));
dcSchema.SetProperty(DublinCoreSchema.SUBJECT,
new
LangAlt(
"RSS 詮釋資料基準範例"
));
dcSchema.SetProperty(DublinCoreSchema.DESCRIPTION,
new
LangAlt(
"RSS 詮釋資料基準範例"
));
dcSchema.SetProperty(DublinCoreSchema.CONTRIBUTOR,
new
LangAlt(
"行政院研究發展考核委員會"
));
dcSchema.SetProperty(DublinCoreSchema.TYPE,
new
LangAlt(
"text/pdf"
));
dcSchema.SetProperty(DublinCoreSchema.FORMAT,
new
LangAlt(
"pdf"
));
dcSchema.SetProperty(DublinCoreSchema.SOURCE,
new
LangAlt(
"行政院研究發展考核委員會"
));
dcSchema.SetProperty(DublinCoreSchema.LANGUAGE,
new
LangAlt(
"中文"
));
dcSchema.SetProperty(DublinCoreSchema.COVERAGE,
new
LangAlt(
"2009-03-31~2015-12-20"
));
dcSchema.SetProperty(DublinCoreSchema.PUBLISHER,
new
LangAlt(
"行政院研究發展考核委員會"
));
dcSchema.SetProperty(DublinCoreSchema.DATE,
new
LangAlt(
"2009-03-31T15:49:18+08:00"
));
dcSchema.SetProperty(DublinCoreSchema.IDENTIFIER,
new
LangAlt(
"341000000A"
));
dcSchema.SetProperty(DublinCoreSchema.RIGHTS,
new
LangAlt(
"行政院研究發展考核委員會"
));
dcSchema.SetProperty(
"Category.Theme"
,
new
LangAlt(
"000"
));
dcSchema.SetProperty(
"Category.Cake"
,
new
LangAlt(
"000"
));
dcSchema.SetProperty(
"Category.Service"
,
new
LangAlt(
"I60"
));
//建立一個存放最後要輸出的XmlDocument物件
XmlDocument docTarget =
new
XmlDocument();
//利用MemoryStream做XmpWriter的輸出Stream
using
(MemoryStream ms =
new
MemoryStream())
{
XmpWriter xmpWriter =
new
XmpWriter(ms);
//將存放分類檢索資訊的DublinCoreSchema透過XmpWriter輸出到Stream
xmpWriter.AddRdfDescription(dcSchema);
//最後記得要呼叫Close(), 不然XMP的資訊會不完整
xmpWriter.Close();
//將XMP的資訊載入至欲輸出的XmlDocument
docTarget.LoadXml(Encoding.UTF8.GetString(ms.ToArray()));
}
//如果要把原始pdf中的XMP資訊也append到新的pdf檔中, 可參考以下的Code:
//---------- Import Start ----------
//設定原始XMP中的NamespaceManager
XmlNamespaceManager mgrSource = GetNs(docSource);
//取得原始XMP的<rdf:RDF>
XmlNode nodeSourceRdf = docSource.SelectSingleNode(
"//rdf:RDF"
, mgrSource);
//設定新XMP中的NamespaceManager
XmlNamespaceManager mgrTarget = GetNs(docTarget);
//取得新XMP的<rdf:RDF>
XmlNode nodeTargetRdf = docTarget.SelectSingleNode(
"//rdf:RDF"
, mgrTarget);
//如果有原始的XMP資訊,開始進行<rdf:Description>的複製
if
(nodeSourceRdf !=
null
)
{
foreach
(XmlNode nodeDesc
in
nodeSourceRdf.ChildNodes)
{
nodeTargetRdf.AppendChild(nodeTargetRdf.OwnerDocument.ImportNode(nodeDesc,
true
));
}
}
//---------- Import End ----------
//設定要輸出的pdf.(第一個參數是讀取原始pdf檔的PdfReader物件)
PdfStamper pdfStamper =
new
PdfStamper(pdfSourceReader,
new
FileStream(
@"c:\stamped.pdf"
, FileMode.OpenOrCreate, FileAccess.Write));
//將新的XMP資訊設定至欲輸出的pdf檔
pdfStamper.XmpMetadata = Encoding.UTF8.GetBytes(docTarget.OuterXml);
//呼叫Close(), 以完成pdf檔的輸出(複製)程序
pdfStamper.Close();
因此建議程式開發過程中要多一點錯誤判斷 (ex: try…catch), 以避免程式出錯或當掉.
補充: 網路上還有一套可讀寫 XMP 的套件 (C# XMP Toolkit), 有興趣的也可以試試.
沒有留言:
張貼留言