unit DTPModules; interface uses DTPAdvancedRanges, DocxMLAdapter, SharedMLAdapter; type DocxComponentsModule = class(DocxComponents) public function Create(); function GetStyles(): Styles; function GetStylesAdapter(): StylesAdapter; function GetDocumentRelsAdapter(): RelationShipsAdapter; function GetNumberingModule(): NumberingModule; function GetTblStylePrByType(style_id: string; type: string): TblStylePr; function GetFtr(target: string): Ftr; function GetHdr(target: string): Hdr; function GetFtrRelsAdapter(target: string): RelationShipsAdapter; function GetHdrRelsAdapter(target: string): RelationShipsAdapter; function GetFootnotesAdapter(): FootnotesAdapter; private styles_deserialize_flag_: boolean; styles_adapter_: StylesAdapter; document_rels_adapter_: RelationShipsAdapter; numbering_module_: NumberingModule; tbl_style_pr_hash_: array of TblStylePr; ftr_hash_: array of Ftr; hdr_hash_: array of Hdr; hdr_rel_hash_: array of RelationShipsAdapter; ftr_rel_hash_: array of RelationShipsAdapter; footnotes_adapter_: FootnotesAdapter; end; type FontModule = class public function Create(pdf: PdfFile); function GetAsciiFont(name: string; bold: boolean; italic: boolean): PdfFont; function GetCNSFont(name: string; bold: boolean; italic: boolean): PdfFont; function GetSymbolFont(): PdfFont; function GetZapfDingbatsFont(): PdfFont; function UseExternalFont(); function SetSubstitutionRules(source: string; target: string); function SetDefaultSz(value: real); function GetDefaultSz(): real; private function GetExternalFont(name:string; bold: boolean; italic: boolean): PdfFont; function GetBuiltInCNSFont(name:string; bold: boolean; italic: boolean): PdfFont; function GetBuiltInAsciiFont(name:string; bold: boolean; italic: boolean): PdfFont; private [weakref]pdf_: PdfFile; use_built_in_font_: boolean; // 是否使用内置字体 substitution_rules_: array of string; // 替换规则 external_reference_: array of string; default_sz_: real; end; type NoteModule = class public function Create(docx_to_pdf: TSDocxToPdf); function AddFootnoteId(id: string); function GetFootnoteOrderNumber(): string; function GetFootnoteIndex(): string; private function CalculateNumber(num_fmt: string; num: integer): string; function ChineseCountingThousand(n: integer): string; private [weakref]docx_to_pdf_: TSDocxToPdf; footnote_hash_: hash; footnote_index_: integer; endnote_hash_: hash; endnote_index_: integer; end; type NumberingModule = class public function Create(number: NumberingAdapter); function GetNumberLvl(ppr: PPr); private function CalculateNumber(num_fmt: string; num: integer): string; function ChineseCountingThousand(n: integer): string; private numbering_adapter_: NumberingAdapter; num_hash_: hash; end; type SectModule = class public function Create(); function AddElement(element: tslobj); public SectPr: SectPrUnitDecorator; Elements: array of tslobj; BaseSize: real; end; type TocModule = class public function Create(); function UpdateDocxNumPages(); function CalculateRect(anchor: string); function LinkToToc(anchor: string; range: BasicRange);overload; function LinkToToc(anchor: string);overload; function AddToc(anchor: string; toc: Toc);overload; function AddDocxPage(anchor: string; r: R); private toc_hash_: hash; toc_unmatched_hash_: hash; update_docx_num_pages_: boolean; // 开放给docx文件是否更新docx文件的页码 docx_page_hash_: hash; end; // 坐标 type Point = class public function Create(); public X: real; Y: real; end; // Page延迟建立,存基本信息即可 type Page = class public function Create(pdf_file: PdfFile); function OriginalTextCoordinates(): array of real; function PrintGrid(); property PdfPage read ReadPdfPage; function ReadPdfPage(): PdfPage; public Index: integer; Number: integer; SectPr: SectPrUnitDecorator; TextPoint: Point; // 正文坐标 FtrPoint: Point; // 页脚坐标 HdrPoint: Point; // 页眉坐标 UpperBound: real; // 上界 LowerBound: real; // 下界 private [weakref]pdf_file_: PdfFile; pdf_page_: PdfPage; end; type PageManagerModule = class public function Create(pdf_file: PdfFile); function Operator[](index: uinteger): Page; function NewPage(): Page; function NextPage(page: Page): Page; function Count(): integer; private [weakref]pdf_file_: PdfFile; page_array_: array of Page; end; implementation // DocxComponentsModule function DocxComponentsModule.Create(); begin class(DocxComponents).Create(); tbl_style_pr_hash_ := array(); ftr_hash_ := array(); hdr_hash_ := array(); hdr_rel_hash_ := array(); ftr_rel_hash_ := array(); end; function DocxComponentsModule.GetStyles(): Styles; begin if styles_deserialize_flag_ then return {self.}Styles; {self.}Styles.Deserialize(); styles_deserialize_flag_ := true; return {self.}Styles; end; function DocxComponentsModule.GetStylesAdapter(): StylesAdapter; begin if styles_adapter_ then return styles_adapter_; styles_adapter_ := new StylesAdapter({self.}GetStyles()); return styles_adapter_; end; function DocxComponentsModule.GetDocumentRelsAdapter(): RelationShipsAdapter; begin if document_rels_adapter_ then return document_rels_adapter_; {self.}DocumentRels.Deserialize(); document_rels_adapter_ := new RelationShipsAdapter({self.}DocumentRels); return document_rels_adapter_; end; function DocxComponentsModule.GetNumberingModule(): NumberingModule; begin if numbering_module_ then return numbering_module_; if not {self.}Numbering then return nil; {self.}Numbering.Deserialize(); numbering_module_ := new NumberingModule({self.}Numbering); return numbering_module_; end; function DocxComponentsModule.GetTblStylePrByType(style_id: string; type: string): TblStylePr; begin if tbl_style_pr_hash_[style_id][type] then return tbl_style_pr_hash_[style_id][type]; styles_adapter := {self.}GetStylesAdapter(); style := styles_adapter.GetStyleByStyleId(style_id); style := new StyleAdapter(style); tbl_style_pr := style.GetTblStylePrByType(type); tbl_style_pr_hash_[style_id][type] := tbl_style_pr; return tbl_style_pr; end; function DocxComponentsModule.GetFtr(target: string): Ftr; begin if ftr_hash_[target] then return ftr_hash_[target]; index := replaceStr(replaceStr(target, "footer", ""), ".xml", ""); obj := {self.}Footers(strtoint(index)); obj.Deserialize(); ftr_hash_[target] := obj; return obj; end; function DocxComponentsModule.GetHdr(target: string): Hdr; begin if hdr_hash_[target] then return hdr_hash_[target]; index := replaceStr(replaceStr(target, "header", ""), ".xml", ""); obj := {self.}Headers(strtoint(index)); obj.Deserialize(); hdr_hash_[target] := obj; return obj; end; function DocxComponentsModule.GetHdrRelsAdapter(target: string): RelationShipsAdapter; begin if hdr_rel_hash_[target] then return hdr_rel_hash_[target]; index := replaceStr(replaceStr(target, "header", ""), ".xml", ""); obj := {self.}HeaderRels(strtoint(index)); obj.Deserialize(); rels_adapter := new RelationShipsAdapter(obj); hdr_rel_hash_[target] := rels_adapter; return rels_adapter; end; function DocxComponentsModule.GetFtrRelsAdapter(target: string): RelationShipsAdapter; begin if ftr_rel_hash_[target] then return ftr_rel_hash_[target]; index := replaceStr(replaceStr(target, "footer", ""), ".xml", ""); obj := {self.}FooterRels(strtoint(index)); obj.Deserialize(); rels_adapter := new RelationShipsAdapter(obj); ftr_rel_hash_[target] := rels_adapter; return rels_adapter; end; function DocxComponentsModule.GetFootnotesAdapter(): FootnotesAdapter; begin if footnotes_adapter_ then return footnotes_adapter_; obj := {self.}Footnotes; obj.Deserialize(); footnotes_adapter_ := new FootnotesAdapter(obj); return footnotes_adapter_; end; // FontModule function FontModule.Create(pdf: PdfFile); begin pdf_ := pdf; use_built_in_font_ := true; substitution_rules_ := array("宋体": "SimSun", "黑体": "SimHei", "Courier New": "Courier", "Helvetica": "Helvetica", "Times New Roman": "Times-Roman", ); external_reference_ := array(); default_sz_ := 10.5; end; function FontModule.SetDefaultSz(value: real); begin default_sz_ := value; end; function FontModule.GetDefaultSz(); begin return default_sz_; end; function FontModule.UseExternalFont(); begin use_built_in_font_ := false; end; function FontModule.GetExternalFont(name:string; bold: boolean; italic: boolean); begin if ifnil(name) or name = '' then name := "等线"; if not ifnil(external_font_cache_[name]) then return external_font_cache_[name]; value := external_reference_[name]; if ifnil(value) then return nil; if value["ext"] = ".ttf" then font_name := pdf_.LoadTTFontFromFile("", value["path"], true); else if value["ext"] = ".ttc" then font_name := pdf_.LoadTTFontFromFile2("", value["path"], 0, true); if not ifString(font_name) then return nil; font := pdf_.GetFont(font_name, "UTF-8"); external_font_cache_[name] := font; return font; end; function FontModule.GetCNSFont(name: string; bold: boolean; italic: boolean): PdfFont; begin return use_built_in_font_ ? {self.}GetBuiltInCNSFont(name, bold, italic) : {self.}GetExternalFont(name, bold, italic); end; function FontModule.GetBuiltInCNSFont(name: string; bold: boolean; italic: boolean): PdfFont; begin font_name := substitution_rules_[name]; if ifnil(font_name) then font_name := "SimSun"; if bold and italic then font_name += ",BoldItalic"; else if bold then font_name += ",Bold"; else if italic then font_name += ",Italic"; return pdf_.GetFont(font_name, "GBK-EUC-H"); end; function FontModule.GetAsciiFont(name: string; bold: boolean; italic: boolean): PdfFont; begin return use_built_in_font_ ? {self.}GetBuiltInAsciiFont(name, bold, italic) : {self.}GetExternalFont(name, bold, italic); end; function FontModule.GetBuiltInAsciiFont(name:string; bold: boolean; italic: boolean): PdfFont; begin font_name := substitution_rules_[name]; if ifnil(font_name) then font_name := "Times-Roman"; if font_name = "Courier" or font_name = "Helvetica" then begin if bold and italic then font_name += "-BoldOblique"; else if bold then font_name += "-Bold"; else if italic then font_name += "-Oblique"; end else if font_name = "Times-Roman" then begin if bold and italic then font_name := "Times-BoldItalic"; else if bold then font_name := "Times-Bold"; else if italic then font_name := "Times-Italic"; end else begin font_name := "Courier"; end return pdf_.GetFont(font_name, ""); end; function FontModule.SetSubstitutionRules(source: string; target: string); begin substitution_rules_[source] := target; end; function FontModule.GetSymbolFont(): PdfFont; begin return pdf_.GetFont("Symbol", ""); end; function FontModule.GetZapfDingbatsFont(): PdfFont; begin return pdf_.GetFont("ZapfDingbats", ""); end; // NoteModule function NoteModule.Create(docx_to_pdf: TSDocxToPdf); begin docx_to_pdf_ := docx_to_pdf; footnote_hash_ := array(); endnote_hash_ := array(); footnote_index_ := 0; endnote_index_ := 0; end; function NoteModule.AddFootnoteId(id: string); begin footnotes_adapter := docx_to_pdf_.DocxComponents.GetFootnotesAdapter(); footnote := footnotes_adapter.GetFootnoteById(id); sect_pr := docx_to_pdf_.CurrentSect.SectPr; w := sect_pr.SectPr.PgSz.W - sect_pr.SectPr.PgMar.Right - sect_pr.SectPr.PgMar.Left; lb := 0; arr := array(); elements := footnote.Elements(); for _,element in elements do begin if element.LocalName = "p" then begin range := new PRange(docx_to_pdf_, element); range.Width := w; range.LowerBound := lb; range.Parent := self; range.Calc(); arr[length(arr)] := range; end end footnote_hash_[id] := arr; end; // function NoteModule.RunFootnote(id: string; x: real; y: real; pg: Page); // begin // sx := x; // sy := y; // spg := pg; // height := 0; // arr := array(); // hash := footnote_hash_[id]; // for id,range in hash do // begin // range.StartX := x; // range.StartY := y; // range.StartPage := spg; // range.LowerBound := 0; // range.Run(); // sy := range.EndY; // height += range.DynamicHeight; // arr union= range.PLineRanges(); // footnote_hash_[id] := arr; // end // return height; // end; function NoteModule.GetFootnoteOrderNumber(); begin num_fmt := docx_to_pdf_.CurrentSect.SectPr.FootnotePr.NumFmt.Val; if ifnil(num_fmt) then num_fmt := "decimal"; return CalculateNumber(num_fmt, ++footnote_index_); end; function NoteModule.GetFootnoteIndex(): string; begin return inttostr(footnote_index_); end; function NoteModule.CalculateNumber(num_fmt: string; num: integer): string; begin if num_fmt = "decimal" then return format("%d", num); else if num_fmt = "chineseCountingThousand" then return {self.}ChineseCountingThousand(num); else return format("%d", num); end; function NoteModule.ChineseCountingThousand(n: integer): string; begin chinese_digits := array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九"); chinese_units := array("", "十", "百", "千"); if n < 10 then return chinese_digits[n]; result := ""; num_str := inttostr(n); len := length(num_str); for i:=1 to len do begin digit_int := strtoint(num_str[i]); if digit_int <> 0 then result += chinese_digits[digit_int] + chinese_units[len - i]; end if length(result) >= 6 and result[:6] = "一十" then result := result[4:]; return result; end; // SectModule function SectModule.Create(); begin {self.}Elements := array(); {self.}SectPr := nil; end; function SectModule.AddElement(element: tslobj); begin {self.}Elements[length({self.}Elements)] := element; end; // NumberingModule function NumberingModule.Create(number: NumberingAdapter); begin numbering_adapter_ := new NumberingAdapter(number); num_hash_ := array(); end; function NumberingModule.GetNumberLvl(ppr: PPr); begin num_id := ppr.NumPr.NumId.Val; if ifnil(num_id) then return array("", nil); ilvl := ppr.NumPr.Ilvl.Val; pstyle := ppr.PStyle.Val; num := numbering_adapter_.GetNumByNumId(num_id); if ifnil(num) then return array("", nil); abstract_id := num.AbstractNumId.Val; abstract_num := numbering_adapter_.GetAbstractNumByAbstractNumId(abstract_id); lvls := abstract_num.Lvls(); if ifnil(ilvl) then ilvl := "0"; for k,v in lvls do begin if ilvl and v.Ilvl = ilvl then begin lvl_text := v.LvlText.Val; if not ifarray(num_hash_[num_id]) then num_hash_[num_id] := array(0, 0, 0, 0, 0, 0, 0, 0, 0); ilvl_int := strtoint(ilvl); for i:=0 to ilvl_int do begin source_str := "%" $ (i + 1); n := num_hash_[num_id][i]; if i = ilvl_int then begin n := num_hash_[num_id][i] + 1; for j:=i+1 to length(num_hash_[num_id])-1 do num_hash_[num_id][j] := 0; end n := i = ilvl_int ? num_hash_[num_id][i] + 1 : num_hash_[num_id][i]; dest_str := {self.}CalculateNumber(v.NumFmt.Val, n); lvl_text := replaceStr(lvl_text, source_str, dest_str); end num_hash_[num_id][ilvl_int]++; if v.Suff.Val = "space" then lvl_text += " "; return array(lvl_text, v); end end return array("", nil); end function NumberingModule.CalculateNumber(num_fmt: string; num: integer): string; begin if num_fmt = "decimal" then return format("%d", num); else if num_fmt = "chineseCountingThousand" then return {self.}ChineseCountingThousand(num); else return format("%d", num); end; function NumberingModule.ChineseCountingThousand(n: integer): string; begin chinese_digits := array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九"); chinese_units := array("", "十", "百", "千"); if n < 10 then return chinese_digits[n]; result := ""; num_str := inttostr(n); len := length(num_str); for i:=1 to len do begin digit_int := strtoint(num_str[i]); if digit_int <> 0 then result += chinese_digits[digit_int] + chinese_units[len - i]; end if length(result) >= 6 and result[:6] = "一十" then result := result[4:]; return result; end; // Page function Page.Create(pdf_file: PdfFile); begin pdf_file_ := pdf_file; {self.}TextPoint := new Point(); {self.}FtrPoint := new Point(); {self.}HdrPoint := new Point(); end; function Page.OriginalTextCoordinates(): array of real; begin x := {self.}SectPr.PgMar.Left; y := min({self.}SectPr.PgSz.H - max({self.}SectPr.PgMar.Top, {self.}SectPr.PgMar.Header), {self.}HdrPoint.Y); return array(x, y); end; function Page.ReadPdfPage(): PdfPage; begin if ifnil(pdf_page_) then begin pdf_page_ := pdf_file_.AddPage(); pdf_page_.SetWidth({self.}SectPr.PgSz.W); pdf_page_.SetHeight({self.}SectPr.PgSz.H); if sysparams["_PDF_PAGE_GRID_DEBUG_"] then {self.}PrintGrid(); end return pdf_page_; end; function Page.PrintGrid(); begin i := 0; while true do begin y := {self.}SectPr.PgSz.H - max({self.}SectPr.PgMar.Top, {self.}SectPr.PgMar.Header); y := min(y, {self.}HdrPoint.Y); y := y - i * {self.}SectPr.DocGrid.LinePitch; if y <= {self.}SectPr.PgMar.Bottom then break; pdf_page_.SetLineWidth(0.05); pdf_page_.SetGrayStroke(0.75); pdf_page_.MoveTo({self.}SectPr.PgMar.Left, y); pdf_page_.LineTo({self.}SectPr.PgSz.W- {self.}SectPr.PgMar.Right, y); pdf_page_.Stroke(); i++; end x1 := {self.}SectPr.PgMar.Left; y1 := {self.}SectPr.PgSz.H - {self.}SectPr.PgMar.Top; x2 := {self.}SectPr.PgSz.W - {self.}SectPr.PgMar.Right; y2 := y1; x3 := x1; y3 := {self.}SectPr.PgMar.Bottom; x4 := x2; y4 := y3; pdf_page_.SetLineWidth(0.05); pdf_page_.SetGrayStroke(0.70); pdf_page_.MoveTo(x1, y1); pdf_page_.LineTo(x2, y2); pdf_page_.Stroke(); pdf_page_.MoveTo(x1, y1); pdf_page_.LineTo(x3, y3); pdf_page_.Stroke(); pdf_page_.MoveTo(x2, y2); pdf_page_.LineTo(x4, y4); pdf_page_.Stroke(); pdf_page_.MoveTo(x3, y3); pdf_page_.LineTo(x4, y4); pdf_page_.Stroke(); end; // PageManagerModule function PageManagerModule.Create(pdf_file: PdfFile); begin pdf_file_ := pdf_file; page_array_ := array(); end; function Operator PageManagerModule.[](index: uinteger): Page; begin return page_array_[index]; end; function PageManagerModule.NewPage(): Page; begin len := length(page_array_); page := new Page(pdf_file_); page.Index := len; page_array_[len] := page; return page; end; function PageManagerModule.Count(): integer; begin return length(page_array_); end; // TocModule function TocModule.Create(); begin toc_hash_ := array(); toc_unmatched_hash_ := array(); update_docx_num_pages_ := false; docx_page_hash_ := array(); end; function TocModule.UpdateDocxNumPages(); begin update_docx_num_pages_ := true; end; function TocModule.CalculateRect(anchor: string); begin arr := toc_hash_[anchor]; for _,toc in arr do toc.CalculateRect(); end; function TocModule.LinkToToc(anchor: string; range: BasicRange);overload; begin arr := toc_hash_[anchor]; if ifnil(arr) then // 未匹配到链接,说明链接可能在后面 begin toc_unmatched_hash_[anchor] := range; return; end pg := range.EndPage; dst := pg.PdfPage.CreateDestination(); dst.SetXYZ(range.EndX, range.EndY + range.RPr.Sz.Val, 1); for _,toc in arr do toc.LinkAnnot(dst); toc.AddPageNumber(pg); if update_docx_num_pages_ then begin r := docx_page_hash_[anchor]; r.T.Text := pg.Number; r.Serialize(); end end; function TocModule.LinkToToc(anchor: string);overload; begin range := toc_unmatched_hash_[anchor]; if range then begin {self.}LinkToToc(anchor, range); toc_unmatched_hash_[anchor] := nil; end end; function TocModule.AddToc(anchor: string; toc: Toc); begin if ifarray(toc_hash_[anchor]) then toc_hash_[anchor] union= array(toc); else toc_hash_[anchor] := array(toc); end; function TocModule.AddDocxPage(anchor: string; r: R); begin docx_page_hash_[anchor] := r; end; end.