using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using System.Xml; using iTextSharp.text.pdf; using iTextSharp.text.xml.xmp; using PDFPatcher.Common; using PDFPatcher.Model; namespace PDFPatcher.Processor { internal sealed class DocInfoImporter { readonly float _unitFactor; readonly PdfInfoXmlDocument _infoDoc; internal PdfInfoXmlDocument InfoDoc => _infoDoc; readonly ImporterOptions _options; /// /// 从 实例创建导入器(支持无信息文件的补丁操作)。 /// /// 导入器的选项。 /// 包含信息文件的 。 internal DocInfoImporter(ImporterOptions options, PdfInfoXmlDocument infoDoc) { _options = options; _unitFactor = 1; _infoDoc = infoDoc; } internal DocInfoImporter(ImporterOptions options, string infoDocFile) { if (string.IsNullOrEmpty(infoDocFile)) { throw new FileNotFoundException("找不到信息文件。"); } var infoDoc = new PdfInfoXmlDocument(); if (FileHelper.HasExtension(infoDocFile, Constants.FileExtensions.Txt)) { OutlineManager.ImportSimpleBookmarks(infoDocFile, infoDoc); } else { infoDoc.Load(infoDocFile); } // 设置单位转换因数 _unitFactor = GetUnitFactor(infoDoc.DocumentElement); _infoDoc = infoDoc; _options = options; } internal DocInfoImporter(ImporterOptions importerOptions, PdfReader pdf, PatcherOptions patcherOptions, PdfInfoXmlDocument infoDoc) { var v = patcherOptions.ViewerPreferences; var o = new ExporterOptions() { ExportBookmarks = infoDoc?.BookmarkRoot == null && (v.RemoveZoomRate || v.CollapseBookmark != BookmarkStatus.AsIs || v.ForceInternalLink || AppContext.Encodings.BookmarkEncoding != null || (patcherOptions.UnifiedPageSettings.ScaleContent && patcherOptions.UnifiedPageSettings.PaperSize.SpecialSize != SpecialPaperSize.AsPageSize)), ExportDocProperties = false, ExtractPageLinks = (v.RemoveZoomRate || v.ForceInternalLink || (patcherOptions.UnifiedPageSettings.ScaleContent && patcherOptions.UnifiedPageSettings.PaperSize.SpecialSize != SpecialPaperSize.AsPageSize)) && patcherOptions.RemoveAnnotations == false, ExtractPageSettings = false, ExportViewerPreferences = true }; o.UnitConverter.Unit = Constants.Units.Point; var exp = new DocInfoExporter(pdf, o); Tracker.SetProgressGoal(exp.EstimateWorkload()); _options = importerOptions; _unitFactor = 1; _infoDoc = infoDoc; } internal static float GetUnitFactor(XmlElement root) { var unit = root.SelectSingleNode(Constants.Units.ThisName + "/@" + Constants.Units.Unit)?.Value; return string.IsNullOrEmpty(unit) ? 1 : ValueHelper.MapValue(unit, Constants.Units.Names, Constants.Units.Factors, 1); } internal BookmarkRootElement GetBookmarks() { if (_infoDoc == null) { return null; } var be = _infoDoc.BookmarkRoot; if (be?.HasSubBookmarks == true) { if (be.GetAttribute(Constants.DestinationAttributes.FirstPageNumber).TryParse(out int bookmarkPageShift)) { bookmarkPageShift--; } PreprocessBookmark(be, bookmarkPageShift); } return be; } /// /// 偏移页码位置并转换书签目标的尺寸单位。 /// /// /// private void PreprocessBookmark(BookmarkContainer source, int bookmarkPageShift) { foreach (BookmarkElement b in source.ChildNodes) { if (b == null) { continue; } int pageShift = GetPageShift(b, bookmarkPageShift); ShiftPageAndConvertUnits(b, pageShift); PreprocessBookmark(b, pageShift); } } private static int GetPageShift(XmlElement element, int baseShift) { int shift = baseShift; var s = element.GetAttribute(Constants.DestinationAttributes.FirstPageNumber); if (string.IsNullOrEmpty(s) == false) { if (s.TryParse(out shift)) { shift--; } else { shift = baseShift; } } return shift; } private void ShiftPageAndConvertUnits(XmlElement destination, int pageShift) { foreach (XmlAttribute a in destination.Attributes) { switch (a.Name) { case Constants.Coordinates.Top: case Constants.Coordinates.Left: case Constants.Coordinates.Bottom: case Constants.Coordinates.Right: if (_unitFactor != 1 && a.Value.TryParse(out float c)) { a.Value = (c * _unitFactor).ToText(); } break; case Constants.DestinationAttributes.Page: if (pageShift != 0 && a.Value.TryParse(out int pageNum)/* && pageNum > 0*/) { pageNum += pageShift; destination.SetAttribute(a.Name, pageNum.ToText()); } break; } } } internal GeneralInfo ImportDocumentInformation() { if (_options.ImportDocProperties == false || _infoDoc == null) { return null; } Tracker.TraceMessage("导入文档元数据信息。"); var info = _infoDoc.InfoNode; return info == null ? null : new GeneralInfo { Title = info.Title, Author = info.Author, Keywords = info.Keywords, Subject = info.Subject, Creator = info.Creator, Producer = info.Producer }; } internal static void ImportDocumentInformation(GeneralInfo info, PdfReader pdf, string pdfFileName) { if (info == null) { return; } var d = pdf.Trailer.GetAsDict(PdfName.INFO); if (d == null) { d = new PdfDictionary(); pdf.Trailer.Put(PdfName.INFO, d); } UpdateInfoValue(d, PdfName.TITLE, info.Title, pdfFileName); UpdateInfoValue(d, PdfName.SUBJECT, info.Subject, pdfFileName); UpdateInfoValue(d, PdfName.AUTHOR, info.Author, pdfFileName); UpdateInfoValue(d, PdfName.KEYWORDS, info.Keywords, pdfFileName); UpdateInfoValue(d, PdfName.CREATOR, info.Creator, pdfFileName); UpdateInfoValue(d, PdfName.PRODUCER, info.Producer, pdfFileName); if (info.RewriteXmp == false) { return; } var m = pdf.Catalog.Locate(PdfName.METADATA); if (m == null) { pdf.Catalog.Put(PdfName.METADATA, new PRStream(pdf, new byte[0])); } try { var xw = new XmpWriter(new MemoryStream(), d); var s = new MemoryStream(); xw.Serialize(s); m.SetData(s.ToArray(), false); } catch (Exception) { Trace.WriteLine("读写 XMP 属性时出现错误。"); } } internal static void UpdateInfoValue(PdfDictionary info, PdfName name, string value, FilePath pdfFileName) { if (value != null) { if (value.Contains(Constants.FileNameMacros.FileName)) { value = value.Replace(Constants.FileNameMacros.FileName, pdfFileName.FileNameWithoutExtension); } if (value.Contains(Constants.FileNameMacros.FolderName)) { value = value.Replace(Constants.FileNameMacros.FolderName, pdfFileName.Directory.FileName); } } if (info.Contains(name) && value == null) { var s = info.GetAsString(name); if (s != null) { value = s.ToUnicodeString(); } } if (value != null) { value = value.Trim(); } info.Put(name, string.IsNullOrEmpty(value) ? null : value.ToPdfString()); } internal static void ImportDocumentInformation(GeneralInfo info, iTextSharp.text.Document doc) { if (info == null) { return; } doc.AddTitle(info.Title); if (string.IsNullOrEmpty(info.Subject) == false) { doc.AddSubject(info.Subject); } if (string.IsNullOrEmpty(info.Author) == false) { doc.AddAuthor(info.Author); } if (string.IsNullOrEmpty(info.Keywords) == false) { doc.AddKeywords(info.Keywords); } } internal PdfPageLabels ImportPageLabels() { if (_options.ImportViewerPreferences == false || _infoDoc == null) { return null; } Tracker.TraceMessage("导入页码设置。"); var pn = _infoDoc.DocumentElement.SelectNodes( Constants.PageLabels + "/" + Constants.PageLabelsAttributes.Style + "[@" + Constants.PageLabelsAttributes.PageNumber + "]"); var pls = new PdfPageLabels(); bool hasPageLabels = false; foreach (XmlElement item in pn) { if (item.GetAttribute(Constants.PageLabelsAttributes.PageNumber).TryParse(out int physicalPage) == false || physicalPage < 1) { Trace.WriteLine(string.Concat("在“", Constants.PageLabels, "”的“", Constants.PageLabelsAttributes.Style, "”元素中,必须指定大于或等于 1 的“", Constants.PageLabelsAttributes.PageNumber, "”属性。")); continue; } if (item.GetAttribute(Constants.PageLabelsAttributes.StartPage).TryParse(out int firstPage) == false || firstPage < 1) { firstPage = 1; } var prefix = item.GetAttribute(Constants.PageLabelsAttributes.Prefix); int numberStyle = ValueHelper.MapValue( item.GetAttribute(Constants.PageLabelsAttributes.Style), Constants.PageLabelStyles.Names, Constants.PageLabelStyles.Values, PdfPageLabels.DECIMAL_ARABIC_NUMERALS); pls.AddPageLabel(physicalPage, numberStyle, prefix, firstPage); hasPageLabels = true; } return hasPageLabels ? pls : null; } internal static PdfPageLabels ImportPageLabels(List labels) { if (labels == null || labels.Count == 0) { return null; } var pls = new PdfPageLabels(); int i = 0; foreach (var item in labels) { if (item.PageNumber > 0 && item.StartPage > 0) { pls.AddPageLabel(item.PageNumber, ValueHelper.MapValue(item.Style, Constants.PageLabelStyles.Names, Constants.PageLabelStyles.Values, PdfPageLabels.DECIMAL_ARABIC_NUMERALS), item.Prefix, item.StartPage ); i++; } } return i > 0 ? pls : null; } internal void ImportPageLinks(PdfReader r, PdfStamper w) { if (_infoDoc == null) { return; } var ls = _infoDoc.DocumentElement.SelectNodes(Constants.PageLink + "/" + Constants.PageLinkAttributes.Link + "[@" + Constants.PageLinkAttributes.PageNumber + "]"); if (ls == null || ls.Count == 0) { return; } Tracker.TraceMessage("导入页面内连接。"); if (_options.KeepPageLinks == false) { PdfHelper.ClearPageLinks(r); } int pageCount = r.NumberOfPages; foreach (XmlElement item in ls) { if (item.GetAttribute(Constants.PageLinkAttributes.PageNumber).TryParse(out int pageNum) == false) { Trace.WriteLine("页码属性格式不正确"); continue; } if (item.GetAttribute(Constants.DestinationAttributes.FirstPageNumber).TryParse(out int pageOffset)) { pageNum += --pageOffset; } if (pageNum > pageCount) { Trace.WriteLine("页码 " + pageNum + " 超出文档最大页数。"); continue; } var acc = ImportRectangle(item); if (acc == null) { Trace.WriteLine("区域坐标不为 4 个。"); continue; } acc = Array.ConvertAll(acc, a => UnitConverter.ToPoint(a, _unitFactor)); var region = new iTextSharp.text.Rectangle(acc[0], acc[1], acc[2], acc[3]); var border = item.GetAttribute(Constants.PageLinkAttributes.Border); var ann = new PdfAnnotation(w.Writer, region); ann.Put(PdfName.TYPE, PdfName.ANNOT); ann.Put(PdfName.SUBTYPE, PdfName.LINK); ann.Put(PdfName.P, w.Writer.GetPageReference(pageNum)); var hl = item.GetAttribute(Constants.PageLinkAttributes.Style); if (string.IsNullOrEmpty(hl) == false) { PdfName h; switch (hl) { case "无": h = PdfName.N; break; case "取反内容": h = PdfName.I; break; case "取反边框": h = PdfName.O; break; case "按下": h = PdfName.P; break; default: h = PdfName.I; break; } ann.Put(PdfName.H, h); } if (item.SelectSingleNode(Constants.PageLinkAttributes.LinkAction) is XmlElement action) { ShiftPageAndConvertUnits(action, 0); ImportAction(w.Writer, ann, action, pageCount, false); } if (ann != null) { ImportColor(item, ann); if (string.IsNullOrEmpty(border) == false) { ImportBorder(border, ann); } else { if (item.SelectSingleNode("边框样式") is XmlElement bse) { var bs = ImportPdfBorderStyle(bse); if (bs != null) { ann.Put(PdfName.BS, bs); } } } w.AddAnnotation(ann, pageNum); } } } internal static void ImportColor(XmlElement item, PdfDictionary dict) { if (item.HasAttribute(Constants.Color)) { var s = item.GetAttribute(Constants.Color); if (s == Constants.Colors.Transparent) { dict.Put(PdfName.C, new PdfArray()); } var c = System.Drawing.Color.FromName(s); dict.Put(PdfName.C, new PdfArray(new float[] { c.R / 255f, c.G / 255f, c.B / 255f })); } else if (item.HasAttribute(Constants.Colors.Red) || item.HasAttribute(Constants.Colors.Green) || item.HasAttribute(Constants.Colors.Blue)) { dict.Put(PdfName.C, new PdfArray(new float[] { item.GetValue(Constants.Colors.Red, 0f), item.GetValue(Constants.Colors.Green, 0f), item.GetValue(Constants.Colors.Blue, 0f) })); } else if (item.HasAttribute(Constants.Colors.Gray)) { dict.Put(PdfName.C, new PdfArray(new float[] { item.GetValue(Constants.Colors.Gray, 0f) })); } else if (item.HasAttribute(Constants.Colors.Black) || item.HasAttribute(Constants.Colors.Cyan) || item.HasAttribute(Constants.Colors.Magenta) || item.HasAttribute(Constants.Colors.Yellow)) { dict.Put(PdfName.C, new PdfArray(new float[] { item.GetValue(Constants.Colors.Cyan, 0f), item.GetValue(Constants.Colors.Magenta, 0f), item.GetValue(Constants.Colors.Yellow, 0f), item.GetValue(Constants.Colors.Black, 0f) })); } } private static void ImportBorder(string border, PdfAnnotation ann) { var bs = ToInt32Array(border); PdfBorderArray a; if (bs == null) { return; } switch (bs.Length) { case 3: a = new PdfBorderArray(bs[0], bs[1], bs[2]); break; case 4: var dp = new int[bs.Length - 3]; bs.CopyTo(dp, 3); a = new PdfBorderArray(bs[0], bs[1], bs[2], GetPdfDashPattern(dp)); break; default: a = null; break; } if (a != null) { ann.Put(PdfName.BORDER, a); } } internal static void ImportAction(PdfWriter writer, PdfDictionary dict, XmlElement map, int maxPageNumber, bool namedAsNames) { var action = map.GetAttribute(Constants.DestinationAttributes.Action); if (string.IsNullOrEmpty(action)) { action = Constants.ActionType.Goto; } string p; PdfDictionary fs; switch (action) { case Constants.ActionType.Goto: if (string.IsNullOrEmpty(p = map.GetAttribute(Constants.DestinationAttributes.Named)) == false) { if (namedAsNames) dict.Put(PdfName.DEST, new PdfName(p)); else dict.Put(PdfName.DEST, p.ToPdfString()); } else if (string.IsNullOrEmpty(p = map.GetAttribute(Constants.DestinationAttributes.Page)) == false) { var ar = new PdfArray(); if (p.TryParse(out int pn) == false || pn > maxPageNumber) { return; } CreateDestination(writer, map, p, ar, false); dict.Put(PdfName.DEST, ar); } break; case Constants.ActionType.GotoR: var dic = new PdfDictionary(); if (string.IsNullOrEmpty(p = map.GetAttribute(Constants.DestinationAttributes.Named)) == false) { dic.Put(PdfName.D, p.ToPdfString()); } else if (string.IsNullOrEmpty(p = map.GetAttribute(Constants.DestinationAttributes.NamedN)) == false) { dic.Put(PdfName.D, new PdfName(p)); } else if (string.IsNullOrEmpty(p = map.GetAttribute(Constants.DestinationAttributes.Page)) == false) { p.TryParse(out int pn); if (pn > 0) { pn--; } var ar = new PdfArray(); CreateDestination(writer, map, pn.ToText(), ar, true); dic.Put(PdfName.D, ar); } p = map.GetAttribute(Constants.DestinationAttributes.Path); if (dic.Size > 0 && p != null) { dic.Put(PdfName.S, PdfName.GOTOR); fs = new PdfDictionary(PdfName.FILESPEC); fs.Put(PdfName.F, new PdfString(p, Encoding.Default.WebName)); fs.Put(PdfName.UF, new PdfString(p, PdfObject.TEXT_UNICODE)); dic.Put(PdfName.F, fs); var nw = map.GetAttribute(Constants.DestinationAttributes.NewWindow); if (nw != null) { dic.Put(PdfName.NEWWINDOW, nw == Constants.Boolean.True); } dict.Put(PdfName.A, dic); } break; case Constants.ActionType.Uri: p = map.GetAttribute(Constants.DestinationAttributes.Path); if (string.IsNullOrEmpty(p) == false) { var u = new PdfDictionary(); u.Put(PdfName.S, PdfName.URI); u.Put(PdfName.URI, p); dict.Put(PdfName.A, u); } break; case Constants.ActionType.Launch: p = map.GetAttribute(Constants.DestinationAttributes.Path); if (string.IsNullOrEmpty(p) == false) { var l = new PdfDictionary(); l.Put(PdfName.S, PdfName.LAUNCH); fs = new PdfDictionary(PdfName.FILESPEC); fs.Put(PdfName.F, new PdfString(p, Encoding.Default.WebName)); fs.Put(PdfName.UF, new PdfString(p, PdfObject.TEXT_UNICODE)); l.Put(PdfName.F, fs); dict.Put(PdfName.A, l); } break; case Constants.ActionType.Javascript: p = map.GetAttribute(Constants.DestinationAttributes.ScriptContent); dict.Put(PdfName.A, string.IsNullOrEmpty(p) ? null : PdfAction.JavaScript(p, writer)); break; default: Tracker.TraceMessage(Tracker.Category.Alert, string.Concat("不支持动作:", action)); break; } } private static void CreateDestination(PdfWriter writer, XmlElement map, string p, PdfArray ar, bool isRemote) { int pn; bool useDefaultPos = false; var pos = new float[4]; int posItemCount = 0; if (p.TryParse(out pn) == false || pn < (isRemote ? 0 : 1)) { return; } PdfIndirectReference pr = null; if (isRemote == false) { pr = writer.GetPageReference(pn); ar.Add(pr); } else { ar.Add(new PdfNumber(pn)); } iTextSharp.text.Rectangle box; switch (p = map.GetAttribute(Constants.DestinationAttributes.View)) { case Constants.DestinationAttributes.ViewType.XYZ: goto default; case Constants.DestinationAttributes.ViewType.Fit: case Constants.DestinationAttributes.ViewType.FitB: posItemCount = 0; break; case Constants.DestinationAttributes.ViewType.FitH: case Constants.DestinationAttributes.ViewType.FitBH: posItemCount = 1; var top = map.GetAttribute(Constants.Coordinates.Top); if (top == Constants.Coordinates.Unchanged) { pos[0] = float.NaN; } else if (top.TryParse(out pos[0]) == false) { if (pr != null && (box = PdfHelper.GetPageVisibleRectangle(PdfReader.GetPdfObject(pr) as PdfDictionary)) != null) { pos[0] = box.Top; } else { useDefaultPos = true; } } break; case Constants.DestinationAttributes.ViewType.FitV: case Constants.DestinationAttributes.ViewType.FitBV: posItemCount = 1; var left = map.GetAttribute(Constants.Coordinates.Left); if (left == Constants.Coordinates.Unchanged) { pos[0] = float.NaN; } else if (left.TryParse(out pos[0]) == false) { if (pr != null && (box = PdfHelper.GetPageVisibleRectangle(PdfReader.GetPdfObject(pr) as PdfDictionary)) != null) { pos[0] = box.Left; } else { useDefaultPos = true; } } break; case Constants.DestinationAttributes.ViewType.FitR: pos = ImportRectangle(map); if (pos == null) { useDefaultPos = true; } break; default: posItemCount = 3; left = map.GetAttribute(Constants.Coordinates.Left); top = map.GetAttribute(Constants.Coordinates.Top); if (left.TryParse(out pos[0]) == false) { pos[0] = float.NaN; } if (top.TryParse(out pos[1]) == false) { pos[1] = float.NaN; } if (map.GetAttribute(Constants.Coordinates.ScaleFactor).TryParse(out pos[2]) == false || pos[2] < 0) { pos[2] = float.NaN; } if (float.IsNaN(pos[0]) && float.IsNaN(pos[1]) && left != Constants.Coordinates.Unchanged && top != Constants.Coordinates.Unchanged) { useDefaultPos = true; } break; } if (useDefaultPos) { ar.Add(PdfName.XYZ); if (isRemote) { ar.Add(new float[] { 0, 10000, 0 }); return; } if (PdfReader.GetPdfObject(pr) is PdfDictionary page) { box = page.GetPageVisibleRectangle(); if (box != null) { if (true) { // TODO: 检测页面旋转方向并设置正确的目标 } ar.Add(PdfNull.PDFNULL); ar.Add(new PdfNumber(box.Top)); ar.Add(PdfNull.PDFNULL); } } else { ar.Add(new float[] { 0, 10000, 0 }); } } else { ar.Add(ValueHelper.MapValue(p, Constants.DestinationAttributes.ViewType.Names, Constants.DestinationAttributes.ViewType.PdfNames, PdfName.XYZ)); for (int i = 0; i < posItemCount; i++) { ref var v = ref pos[i]; if (float.IsNaN(v)) { ar.Add(PdfNull.PDFNULL); } else { if (v > 10000) { v = 10000; } ar.Add(new PdfNumber(v)); } } } } private static float[] ImportRectangle(XmlElement map) { var pos = new float[4]; if (map.GetAttribute(Constants.Coordinates.Left).TryParse(out pos[0]) == false || pos[0] < 0 || map.GetAttribute(Constants.Coordinates.Bottom).TryParse(out pos[1]) == false || pos[1] < 0 || map.GetAttribute(Constants.Coordinates.Right).TryParse(out pos[2]) == false || pos[2] < 0 || map.GetAttribute(Constants.Coordinates.Top).TryParse(out pos[3]) == false || pos[3] < 0) { return null; } return pos; } private static PdfDictionary ImportPdfBorderStyle(XmlElement item) { var borderWidth = item.GetAttribute("宽度"); var borderStyle = item.GetAttribute("样式"); var borderPattern = item.GetAttribute("线形"); var bs = new PdfDictionary(PdfName.BS); if (borderWidth.TryParse(out float bw)) { bs.Put(PdfName.W, new PdfNumber(bw)); } if (string.IsNullOrEmpty(borderStyle)) { return bs; } PdfName s; switch (borderStyle) { case "方框": s = PdfName.S; break; case "下划线": s = PdfName.U; break; case "凸起": s = PdfName.B; break; case "凹陷": s = PdfName.I; break; case "虚线": s = PdfName.D; if (string.IsNullOrEmpty(borderPattern) == false) { var p = ToInt32Array(borderPattern); if (p != null) { var dp = GetPdfDashPattern(p); if (dp != null) { bs.Put(PdfName.D, dp); } } } break; default: s = new PdfName(borderStyle); break; } bs.Put(PdfName.S, s); return bs; } private static PdfDashPattern GetPdfDashPattern(int[] p) { switch (p.Length) { case 1: return new PdfDashPattern(p[0]); case 2: return new PdfDashPattern(p[0], p[1]); case 3: return new PdfDashPattern(p[0], p[1], p[2]); default: return null; } } internal void ImportViewerPreferences(PdfReader r) { if (_options.ImportViewerPreferences == false || _infoDoc == null) return; Tracker.TraceMessage("导入阅读器设置。"); if (_infoDoc.DocumentElement.SelectSingleNode(Constants.ViewerPreferences) is not XmlElement ps) { return; } PdfName n; PdfObject v; foreach (XmlAttribute item in ps.Attributes) { switch (item.Name) { case Constants.PageLayout: v = ValueHelper.MapValue(item.Value, Constants.PageLayoutType.Names, Constants.PageLayoutType.PdfNames, PdfName.NONE); if (PdfName.NONE.Equals(v) == false) { r.Catalog.Put(PdfName.PAGELAYOUT, v); } continue; case Constants.PageMode: v = ValueHelper.MapValue(item.Value, Constants.PageModes.Names, Constants.PageModes.PdfNames, PdfName.NONE); if (PdfName.NONE.Equals(v) == false) { r.Catalog.Put(PdfName.PAGEMODE, v); } continue; case Constants.ViewerPreferencesType.Direction: n = PdfName.DIRECTION; v = ValueHelper.MapValue(item.Value, Constants.ViewerPreferencesType.DirectionType.Names, Constants.ViewerPreferencesType.DirectionType.PdfNames, PdfName.NONE); if (PdfName.NONE.Equals(v) == true) { continue; } break; default: n = ValueHelper.MapValue(item.Name, Constants.ViewerPreferencesType.Names, Constants.ViewerPreferencesType.PdfNames, new PdfName(item.Name)); v = item.Value switch { Constants.Boolean.True => PdfBoolean.PDFTRUE, Constants.Boolean.False => PdfBoolean.PDFFALSE, _ => PdfHelper.ResolvePdfName(item.Value) }; break; } if (r.Catalog.Contains(PdfName.VIEWERPREFERENCES) == false) { r.Catalog.Put(PdfName.VIEWERPREFERENCES, new PdfDictionary()); } r.Catalog.GetAsDict(PdfName.VIEWERPREFERENCES).Put(n, v); } } internal static void OverrideViewerPreferences(ViewerOptions options, PdfReader reader, PdfWriter writer) { var v = ValueHelper.MapValue(options.InitialView, Constants.PageLayoutType.Names, Constants.PageLayoutType.PdfNames, PdfName.NONE); if (PdfName.NONE.Equals(v) == false) { (reader != null ? reader.Catalog : writer.ExtraCatalog).Put(PdfName.PAGELAYOUT, v); } v = ValueHelper.MapValue(options.Direction, Constants.ViewerPreferencesType.DirectionType.Names, Constants.ViewerPreferencesType.DirectionType.PdfNames, PdfName.NONE); if (PdfName.NONE.Equals(v) == false) { if (reader != null) { var d = reader.Catalog.GetAsDict(PdfName.VIEWERPREFERENCES); if (d == null) { d = new PdfDictionary(); reader.Catalog.Put(PdfName.VIEWERPREFERENCES, d); } d.Put(PdfName.DIRECTION, v); } else { writer.AddViewerPreference(PdfName.DIRECTION, v); } } v = ValueHelper.MapValue(options.InitialMode, Constants.PageModes.Names, Constants.PageModes.PdfNames, PdfName.NONE); if (PdfName.NONE.Equals(v) == false) { (reader != null ? reader.Catalog : writer.ExtraCatalog).Put(PdfName.PAGEMODE, v); } if (options.SpecifyViewerPreferences) { var d = reader != null ? reader.Catalog : writer.ExtraCatalog; var p = d.GetAsDict(PdfName.VIEWERPREFERENCES); if (p == null) { p = new PdfDictionary(); d.Put(PdfName.VIEWERPREFERENCES, p); } p.Put(PdfName.CENTERWINDOW, options.CenterWindow ? PdfBoolean.PDFTRUE : null); p.Put(PdfName.DISPLAYDOCTITLE, options.DisplayDocTitle ? PdfBoolean.PDFTRUE : null); p.Put(PdfName.FITWINDOW, options.FitWindow ? PdfBoolean.PDFTRUE : null); p.Put(PdfName.HIDEMENUBAR, options.HideMenu ? PdfBoolean.PDFTRUE : null); p.Put(PdfName.HIDETOOLBAR, options.HideToolbar ? PdfBoolean.PDFTRUE : null); p.Put(PdfName.HIDEWINDOWUI, options.HideUI ? PdfBoolean.PDFTRUE : null); } } internal void ImportNamedDestinations(PdfReader pdf, PdfWriter w) { if (_infoDoc == null) { return; } var ds = _infoDoc.DocumentElement.SelectNodes("命名位置/位置[@名称]"); if (ds.Count == 0) { return; } var infoDs = new Dictionary(ds.Count); foreach (XmlElement item in ds) { infoDs[item.GetAttribute(Constants.DestinationAttributes.Name)] = item; } var pdfDs = pdf.GetNamedDestination(); foreach (KeyValuePair item in infoDs) { if (item.Value.GetAttribute(Constants.DestinationAttributes.Page).TryParse(out int targetPn) == false) { Trace.WriteLine("“目标页面”属性的数值格式不正确。"); continue; } var prop = new PdfArray(); CreateDestination(w, item.Value, targetPn.ToText(), prop, false); //PdfName d = PdfHelper.ResolvePdfName (item.Value.GetAttribute (Constants.DestinationDisplayMode)); //prop.ArrayList.Clear (); //prop.Add (w.GetPageReference (targetPn)); //prop.Add (d); //string disp = item.Value.GetAttribute (Constants.DestinationAttributes.Position); //if (String.IsNullOrEmpty (disp)) { // prop.Add (new float[] { 0, 10000, 0 }); //} //else { // float[] ps = PdfHelper.ToSingleArray (disp.ToLower ().Replace ("null", "0"), true); // ps = UnitConverter.ConvertUnit (ps, this._unitFactor); // prop.Add (ps); //} PdfObject a; if (pdfDs.TryGetValue(item.Key, out a)) { var sourceD = a as PdfArray; sourceD.ArrayList.Clear(); sourceD.ArrayList.AddRange(prop.ArrayList); } else { // ignore those names not in the original document } } } internal void ImportPageSettings(PdfReader pdf) { if (_options.ImportPageSettings == false || _infoDoc == null) { return; } var ps = _infoDoc.DocumentElement.SelectNodes(string.Concat(Constants.Content.PageSettings.ThisName, "/", Constants.Content.Page)); if (ps.Count == 0) { return; } Tracker.TraceMessage("导入页面设置。"); PdfDictionary p; int pn = pdf.NumberOfPages; float[] mb, cb, tb, ab, bb; int pageFilter; foreach (XmlElement item in ps) { List ranges = PageRangeCollection.Parse(item.GetAttribute(Constants.PageRange), 1, pn, true); mb = ToSingleArray(item.GetAttribute(Constants.Content.PageSettings.MediaBox), true); cb = ToSingleArray(item.GetAttribute(Constants.Content.PageSettings.CropBox), true); tb = ToSingleArray(item.GetAttribute(Constants.Content.PageSettings.TrimBox), true); ab = ToSingleArray(item.GetAttribute(Constants.Content.PageSettings.ArtBox), true); bb = ToSingleArray(item.GetAttribute(Constants.Content.PageSettings.BleedBox), true); pageFilter = ValueHelper.MapValue(item.GetAttribute(Constants.PageFilterTypes.ThisName), Constants.PageFilterTypes.Names, Constants.PageFilterTypes.Values, -1); if (item.GetAttribute(Constants.Content.PageSettings.Rotation).TryParse(out int rotate)) { rotate = rotate / 90 * 90; } else { rotate = -1; } foreach (PageRange r in ranges) { foreach (var i in r) { if (pageFilter != -1 && i % 2 != pageFilter) { continue; } p = pdf.GetPageN(i); ImportPageBox(mb, p, PdfName.MEDIABOX); ImportPageBox(cb, p, PdfName.CROPBOX); ImportPageBox(tb, p, PdfName.TRIMBOX); ImportPageBox(ab, p, PdfName.ARTBOX); ImportPageBox(bb, p, PdfName.BLEEDBOX); if (rotate != -1) { p.Put(PdfName.ROTATE, new PdfNumber(rotate)); } } } } } private bool ImportPageBox(float[] array, PdfDictionary pdfDict, PdfName pdfName) { if (array == null) { return false; } if (array.Length == 0) { pdfDict.Remove(pdfName); return true; } if (array.Length == 4) { array = Array.ConvertAll(array, a => { return UnitConverter.ToPoint(a, _unitFactor); }); pdfDict.Put(pdfName, new PdfArray(array)); return true; } return false; } static readonly char[] __ValueArraySplitChars = { ' ', '\t', ',', ';' }; public static float[] ToSingleArray(string value) { return ToSingleArray(value, false); } public static float[] ToSingleArray(string value, bool allowNegativeNumber) { if (value == null) { return null; } else if (value.Length == 0) { return new float[0]; } var parts = value.Split(__ValueArraySplitChars, StringSplitOptions.RemoveEmptyEntries); var vals = new float[parts.Length]; var ok = true; for (int i = 0; i < vals.Length; i++) { if (parts[i].TryParse(out vals[i]) == false || (allowNegativeNumber == false && vals[i] < 0)) { ok = false; break; } } if (ok == false) { return null; } return vals; } public static int[] ToInt32Array(string value) { return ToInt32Array(value, __ValueArraySplitChars, false); } public static int[] ToInt32Array(string value, bool allowNegativeNumber) { return ToInt32Array(value, __ValueArraySplitChars, allowNegativeNumber); } public static int[] ToInt32Array(string value, char[] separators, bool allowNegativeNumber) { if (value == null) { return null; } var parts = value.Split(separators, StringSplitOptions.RemoveEmptyEntries); var vals = new int[parts.Length]; for (int i = 0; i < vals.Length; i++) { if (!parts[i].TryParse(out vals[i]) || (!allowNegativeNumber && vals[i] < 0)) { return null; } } return vals; } } }