type TSDocxToPdf = class public function Create(alias: string; file: string); function Destroy(); function SaveToFile(alias: string; file: string): integer; function Transform(); function GetCurrentPoint(): Point; function GetCachePath(image_path: string): string; function GetPdf(): PdfFile; function GetNextPage(page: TSPage): TSPage; function AddPage(): TSPage;overload; function AddPage(sect_ware: TSSectWare): TSPage;overload; function AdjustPageNumber(page: TSPage; num: integer); function LinkToToc(anchor: string; page: TSPage; left: real; top: real); function AddToc(anchor: string; toc: TSToc); function SetHeaderAndFooter(); function ProcessNumpages(); property Font read ReadFont; function ReadFont(); private function InitDocxComponents(alias: string; file: string); function InitCachePath(file: string); function InitPdfEncoder(); function InitSectWare(); function AllocateElementsToSectWare(); function ResetCoordinates(sect_ware: TSSectWare); function TransformP(sect_ware: TSSectWare; paragraph: P); function TransformTbl(sect_ware: TSSectWare; table: Tbl); function TransformSdt(sect_ware: TSSectWare; sdt: Sdt); function TransformHeaderAndFooter(paragraph: P; type: string); function PrintGrid(page: PdfPage; sect_ware: TSSectWare); // test private pdf_: PdfFile; docx_components_ware_: TSDocxComponentsWare; // TSDocxComponentsWare cache_path_: string; // 临时目录,用来存放临时文件 sect_ware_array_: array of TSSectWare; // 页面布局组件数组 font_ware_: TSFontWare; // 字体部件 current_page_: TSPage; point_: TSPoint; // 定位坐标点 page_array_: array of TSPage; sect_ware_: TSSectWare; sect_pr_adapter_: SectPrAdapter; toc_array_: tableArray; toc_unmacthed_array_: tableArray; range_page_number_array_: tableArray; end; type TSPoint = class function Create(); begin {self.}X := 0; {self.}Y := 0; end X: real; Y: real; end; function TSDocxToPdf.Create(alias: string; file: string); begin pdf_ := new PdfFile(); {self.}InitPdfEncoder(); {self.}InitDocxComponents(alias, file); {self.}InitCachePath(file); {self.}InitSectWare(); font_ware_ := new TSFontWare(pdf_); current_page_ := nil; point_ := new TSPoint(); page_array_ := array(); toc_array_ := array(); toc_unmacthed_array_ := array(); range_page_number_array_ := array(); end; function TSDocxToPdf.Destroy(); begin removeDir("", cache_path_); end; function TSDocxToPdf.SaveToFile(alias: string; file: string): integer; begin return pdf_.SaveToFile(alias, file); end; function TSDocxToPdf.Transform(); begin for _,sect_ware in sect_ware_array_ do begin if sect_ware_ <> sect_ware then begin sect_ware_ := sect_ware; sect_pr_adapter_ := new SectPrAdapter(sect_ware_.SectPr.GetObject()); {self.}AddPage(sect_ware); end elements := sect_ware.Elements; for _,element in elements do begin // if _ = 64 then // if _ = 19 then // println("_ = {}", _); if element.LocalName = "p" then {self.}TransformP(sect_ware, element); else if element.LocalName = "tbl" then {self.}TransformTbl(sect_ware, element); else if element.LocalName = "sdt" then {self.}TransformSdt(sect_ware, element); end end {self.}ProcessNumpages(); end; function TSDocxToPdf.GetCurrentPoint(): Point; begin return point_; end; function TSDocxToPdf.GetCachePath(image_path: string): string; begin return cache_path_ + extractFileName(image_path); end; function TSDocxToPdf.GetPdf(): PdfFile; begin return pdf_; end; function TSDocxToPdf.ReadFont(); begin return font_ware_; end; function TSDocxToPdf.InitPdfEncoder(); begin pdf_.UseCNSFonts(); pdf_.UseCNSEncodings(); // pdf_.UseUTFEncodings(); end; function TSDocxToPdf.InitDocxComponents(alias: string; file: string); begin namespace "DOCX"; docx_components_ware_ := new TSDocxComponentsWare(); [err, msg] := docx_components_ware_.OpenFile(alias, file, nil); if err then raise "Open file error."; end; function TSDocxToPdf.InitCachePath(file: string); begin path := format("%s_%s", extractFileName(file), formatDatetime("YYYYMMDDHHNNSSZZZ", now())); cache_dir := format("%s/funcext/PdfConverter/.cache", extractFileDir(sysExecName())); createDir("", cache_dir); cache_path_ := format("%s/%s/", cache_dir, path); createDir("", cache_path_); end; function TSDocxToPdf.InitSectWare(); begin document := docx_components_ware_.Document; document.Deserialize(); sect_ware_array_ := array(); {self.}AllocateElementsToSectWare(); end; function TSDocxToPdf.AllocateElementsToSectWare(); begin elements := docx_components_ware_.Document.Body.Elements(); ware := new TSSectWare(); fp := function(ware, sect); begin sect := new SectPrUnitDecorator(sect); sect.PgSz.Orient := sect.PgSz.Orient ? "portrait" : "landscape"; ware.SectPr := sect; ware.Do(); end for i:=0 to length(elements)-1 do begin element := elements[i]; ware.AddElement(element); if element.LocalName = "p" and ifObj(element.PPr.SectPr.XmlNode) then begin ##fp(ware, element.PPr.SectPr); sect_ware_array_[length(sect_ware_array_)] := ware; ware := new TSSectWare(); end else if element.LocalName = "sectPr" and i = length(elements)-1 then begin ##fp(ware, element); sect_ware_array_[length(sect_ware_array_)] := ware; end end // println("sect_ware_array_ = {}", sect_ware_array_); end; function TSDocxToPdf.AddPage(): TSPage;overload; begin page := pdf_.AddPage(); page.SetWidth(sect_ware_.SectPr.PgSz.W); page.SetHeight(sect_ware_.SectPr.PgSz.H); len := length(page_array_); start := sect_ware.SectPr.PgNumType.Start; println("start = {}, type = {}", start, ifInt(start)); current_page_ := new TSPage(); current_page_.Index := len; current_page_.PdfPage := page; current_page_.Number := len = 0 ? start : page_array_[len-1].Number + 1; page_array_[len] := current_page_; // 页眉页脚 {self.}SetHeaderAndFooter(); end; function TSDocxToPdf.SetHeaderAndFooter(); begin type_name := "default"; if current_page_.Index = 0 then type_name := "first"; header_reference := sect_pr_adapter_.GetHeaderReferenceByType(type_name); footer_reference := sect_pr_adapter_.GetFooterReferenceByType(type_name); if ifObj(header_reference) then begin rels_adapter := docx_components_ware_.GetDocumentRelsAdapter(); rel := rels_adapter.GetRelationshipById(header_reference.Id); target := rel.Target; index := replaceStr(target, "header", ""); index := replaceStr(index, ".xml", ""); header := docx_components_ware_.Headers(strtoint(index)); elements := header.Elements(); for _,element in elements do begin if element.LocalName = "p" then {self.}TransformHeaderAndFooter(element, "header"); end end if ifObj(footer_reference) then begin rels_adapter := docx_components_ware_.GetDocumentRelsAdapter(); rel := rels_adapter.GetRelationshipById(footer_reference.Id); target := rel.Target; index := replaceStr(target, "footer", ""); index := replaceStr(index, ".xml", ""); header := docx_components_ware_.Footers(strtoint(index)); elements := header.Elements(); for _,element in elements do begin if element.LocalName = "p" then {self.}TransformHeaderAndFooter(element, "footer"); end end end; function TSDocxToPdf.TransformHeaderAndFooter(paragraph: P; type: string); begin paragraph.Deserialize(); w := sect_ware_.SectPr.PgSz.W - sect_ware_.SectPr.PgMar.Right - sect_ware_.SectPr.PgMar.Left; range := new TSPdfParagraphRange2(self, current_page_, docx_components_ware_, sect_ware_, paragraph); y := type = "header" ? sect_ware_.SectPr.PgSz.H - sect_ware_.SectPr.PgMar.Header : sect_ware_.SectPr.PgMar.Footer; range.StartX := point_.X; range.StartY := y; range.Width := w; r := range.Calc(); if r then range.Do(); else range_page_number_array_[length(range_page_number_array_)] := range; end; function TSDocxToPdf.ProcessNumpages(); begin nums := page_array_[length(page_array_)-1].Number; for _,range in range_page_number_array_ do begin range.SetNumPages(nums); range.RangesToLines(); range.Do(); end end; function TSDocxToPdf.AddPage(sect_ware: TSSectWare): TSPage;overload; begin page := pdf_.AddPage(); page.SetWidth(sect_ware.SectPr.PgSz.W); page.SetHeight(sect_ware.SectPr.PgSz.H); {self.}ResetCoordinates(sect_ware); len := length(page_array_); start := sect_ware.SectPr.PgNumType.Start; current_page_ := new TSPage(); current_page_.Index := len; current_page_.PdfPage := page; current_page_.Number := len = 0 ? start : page_array_[len-1].Number + 1; page_array_[len] := current_page_; {self.}SetHeaderAndFooter(); if sysparams["_PDF_PAGE_GRID_DEBUG_"] then {self.}PrintGrid(page, sect_ware); return current_page_; end; function TSDocxToPdf.AdjustPageNumber(page: TSPage; num: integer); begin for i:=page.Index to length(page_array_)-1 do page_array_[i].Number := num++; end; function TSDocxToPdf.GetNextPage(page: TSPage); begin return page_array_[page.Index + 1]; end; function TSDocxToPdf.ResetCoordinates(sect_ware: TSSectWare); begin point_.X := sect_ware.SectPr.PgMar.Left; point_.Y := sect_ware.SectPr.PgSz.H - max(sect_ware.SectPr.PgMar.Top, sect_ware.SectPr.PgMar.Header); end; function TSDocxToPdf.PrintGrid(page: PdfPage; sect_ware: TSSectWare); begin i := 0; while true do begin y := point_.Y - i * sect_ware.SectPr.DocGrid.LinePitch; if y <= sect_ware.SectPr.PgMar.Bottom then break; page.SetLineWidth(0.05); page.SetGrayStroke(0.75); page.MoveTo(sect_ware.SectPr.PgMar.Left, y); page.LineTo(sect_ware.SectPr.PgSz.W- sect_ware.SectPr.PgMar.Right, y); page.Stroke(); i++; end x1 := sect_ware.SectPr.PgMar.Left; y1 := sect_ware.SectPr.PgSz.H - sect_ware.SectPr.PgMar.Top; x2 := sect_ware.SectPr.PgSz.W - sect_ware.SectPr.PgMar.Right; y2 := y1; x3 := x1; y3 := sect_ware.SectPr.PgMar.Bottom; x4 := x2; y4 := y3; page.SetLineWidth(0.05); page.SetGrayStroke(0.5); page.MoveTo(x1, y1); page.LineTo(x2, y2); page.Stroke(); page.MoveTo(x1, y1); page.LineTo(x3, y3); page.Stroke(); page.MoveTo(x2, y2); page.LineTo(x4, y4); page.Stroke(); page.MoveTo(x3, y3); page.LineTo(x4, y4); page.Stroke(); end; function TSDocxToPdf.TransformP(sect_ware: TSSectWare; paragraph: P); begin w := sect_ware.SectPr.PgSz.W - sect_ware.SectPr.PgMar.Right - sect_ware.SectPr.PgMar.Left; range := new TSPdfParagraphRange(self, current_page_, docx_components_ware_, sect_ware, paragraph); range.StartX := point_.X; range.StartY := point_.Y; range.Width := w; range.Calc(); range.Do(); point_.Y := range.EndY; end; function TSDocxToPdf.TransformTbl(sect_ware: TSSectWare; table: Tbl); begin w := sect_ware.SectPr.PgSz.W - sect_ware.SectPr.PgMar.Right - sect_ware.SectPr.PgMar.Left; range := new TSPdfTableRange(self, current_page_, docx_components_ware_, sect_ware, table); range.StartX := point_.X; range.StartY := point_.Y; range.Width := w; range.Calc(); range.Do(); point_.Y := range.EndY; end; function TSDocxToPdf.TransformSdt(sect_ware: TSSectWare; sdt: Sdt); begin ps := sdt.SdtContent.Ps(); for _,p in ps do {self.}TransformP(sect_ware, p); end; function TSDocxToPdf.AddToc(anchor: string; toc: TSToc); begin if ifarray(toc_array_[anchor]) then toc_array_[anchor] union= array(toc); else toc_array_[anchor] := array(toc); if toc_unmacthed_array_[anchor] then begin toc := toc_unmacthed_array_[anchor]; {self.}LinkToToc(anchor, toc[0], toc[1], toc[2]); toc_unmacthed_array_[anchor] := nil; end end; function TSDocxToPdf.LinkToToc(anchor: string; page: TSPage; left: real; top: real); begin arr := toc_array_[anchor]; if ifnil(arr) then begin toc_unmacthed_array_[anchor] := array(page, left, top); return; end dst := page.PdfPage.CreateDestination(); dst.SetXYZ(left, top, 1); for _,toc in arr do toc.LinkAnnot(dst); toc.AddPageNumber(page); end;