From d14de26d58e9b2a31975acef4a98586a84487389 Mon Sep 17 00:00:00 2001 From: csh Date: Fri, 19 Jul 2024 14:49:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TSDocxToPdf.tsf | 48 ++++++- range/Advanced/TSPdfParagraphRange.tsf | 178 +++++++++++++++++++------ range/Advanced/TSPdfTableRange.tsf | 15 --- range/Advanced/TSPdfTocRange.tsf | 3 + utils/TSPage.tsf | 5 +- utils/TSPoint.tsf | 10 -- utils/TSToc.tsf | 51 +++++++ 7 files changed, 237 insertions(+), 73 deletions(-) delete mode 100644 utils/TSPoint.tsf create mode 100644 utils/TSToc.tsf diff --git a/TSDocxToPdf.tsf b/TSDocxToPdf.tsf index 5aed558..3e45426 100644 --- a/TSDocxToPdf.tsf +++ b/TSDocxToPdf.tsf @@ -10,6 +10,9 @@ public function GetPdf(): PdfFile; function GetNextPage(page: TSPage): TSPage; function AddPage(sect_ware: TSSectWare): TSPage; + function AdjustPageNumber(page: TSPage; num: integer); + function LinkToToc(anchor: string; page: TSPage); + function AddToc(anchor: string; toc: TSToc); property Font read ReadFont; function ReadFont(); @@ -37,6 +40,17 @@ private current_page_: TSPage; point_: TSPoint; // 定位坐标点 page_array_: array of TSPage; + toc_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); @@ -50,7 +64,7 @@ begin current_page_ := nil; point_ := new TSPoint(); page_array_ := array(); - page_index_ := array(); + toc_array_ := array(); end; function TSDocxToPdf.Destroy(); @@ -76,8 +90,10 @@ begin elements := sect_ware.Elements; for _,element in elements do begin + // 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 end; @@ -114,7 +130,7 @@ begin namespace "DOCX"; docx_components_ := new TSDocxComponentsWare(); [err, msg] := docx_components_.OpenFile(alias, file, nil); - if err then raise "Create obejct 'TSDocxFile' failed."; + if err then raise "Open file error."; end; function TSDocxToPdf.InitCachePath(file: string); @@ -172,9 +188,11 @@ begin {self.}ResetCoordinates(sect_ware); len := length(page_array_); + start := ifString(sect_ware.SectPr.PgNumType.Start) and trystrtoint(sect_ware.SectPr.PgNumType.Start, r) ? r : 0; 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_; if sysparams["_PDF_PAGE_GRID_DEBUG_"] then @@ -183,6 +201,12 @@ if sysparams["_PDF_PAGE_GRID_DEBUG_"] then 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]; @@ -259,5 +283,23 @@ end; function TSDocxToPdf.TransformSdt(sect_ware: TSSectWare; sdt: Sdt); begin - w := sect_ware.SectPr.PgSz.W - sect_ware.SectPr.PgMar.Right - sect_ware.SectPr.PgMar.Left; + 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); +end; + +function TSDocxToPdf.LinkToToc(anchor: string; page: TSPage); +begin + arr := toc_array_[anchor]; + if ifnil(arr) then return; + dst := page.PdfPage.CreateDestination(); + for _,toc in arr do + toc.LinkAnnot(dst); + toc.AddPageNumber(page); end; diff --git a/range/Advanced/TSPdfParagraphRange.tsf b/range/Advanced/TSPdfParagraphRange.tsf index 76de4ce..12a5472 100644 --- a/range/Advanced/TSPdfParagraphRange.tsf +++ b/range/Advanced/TSPdfParagraphRange.tsf @@ -13,15 +13,18 @@ private function SetLvlText(); function GetImageFileType(data: binary): string; function GetParagraphLineSpace(size: real; line: integer): real; - function RangesToLines(ppr: PPr): tableArray; + function RangesToLines(ppr: PPrUnitDecorator): tableArray; function CheckAndAddPage(y: real; offset: real): boolean; function GetUtf8CharLength(byte: string): integer; - function RToTextRange(r: R; ppr: PPr); - function SplitTextToTextRange(text: string; rpr: RPr); - function RToDrawingRange(r: R; ppr: PPr); - function SetLinesAlignment(ppr: PPr); - function ResetCoordinates(ppr: PPr); + function RToTextRange(r: R; ppr: PPrUnitDecorator; link: string); + function SplitTextToTextRange(text: string; rpr: RPrUnitDecorator; link: string); + function RToDrawingRange(r: R; ppr: PPrUnitDecorator); + function SetLinesAlignment(ppr: PPrUnitDecorator); + function ResetCoordinates(ppr: PPrUnitDecorator); function NewLineRange(): TSPdfLineRange; + function BookMarkLinkToc(); + function HyperlinkToToc(ppr: PPrUnitDecorator); + function HyperlinkToTextRange(hyperlink: Hyperlink; ppr: PPrUnitDecorator); private [weakref]docx_to_pdf_: TSDocxToPdf; @@ -32,6 +35,8 @@ private extra_style_id_: string; range_array_: array of TSPdfBasicRange; line_range_array_: array of TSPdfLineRange; + hyperlink_array_: tableArray; + bookmark_array_: tableArray; end; function TSPdfParagraphRange.Create(docx_to_pdf: TSDocxToPdf; pg: TSPage; components: TSDocxComponentsWare; sect_ware: TSSectWare; paragraph: P); @@ -44,11 +49,14 @@ begin extra_style_id_ := ""; range_array_ := array(); line_range_array_ := array(); + hyperlink_array_ := array(); + bookmark_array_ := array(); {self.}TSPage := page_; end; function TSPdfParagraphRange.Calc(): tableArray; begin + // ppr.rpr是无效的,应该以ppr.pStyle为准 {self.}SetPPr(paragraph_.PPr); {self.}SetLvlText(); ppr := new PPrUnitDecorator(paragraph_.PPr); @@ -58,37 +66,115 @@ begin {self.}CheckAndAddPage({self.}EndY, ppr.Spacing.Before); {self.}EndY -= ppr.Spacing.Before; {self.}DynamicHeight += ppr.Spacing.Before; - rs := paragraph_.Rs(); - if length(rs) = 0 then + + elements := paragraph_.Elements(); + empty_flag := true; + bookmark_id := ''; + bookmark_name := ''; + bookmark_flag := false; + for _,element in elements do + begin + if not ifObj(element.XmlNode) then continue; + if element.LocalName = "r" then + begin + empty_flag := false; + {self.}SetRPr(element.RPr, ppr); + if element.Br.Type = "page" then + {self.}CheckAndAddPage(sect_ware_.SectPr.PgMar.Bottom, 1); + else if ifObj(element.Drawing.XmlNode) then {self.}RToDrawingRange(element, ppr); + else {self.}RToTextRange(element, ppr, bookmark_name); + end + else if element.LocalName = "hyperlink" then + begin + empty_flag := false; + {self.}HyperlinkToTextRange(element, ppr); + end + else if element.LocalName = "bookmarkStart" then + begin + bookmark_id := element.Id; + bookmark_name := element.Name; + bookmark_array_[bookmark_name] := array(); + end + else if element.LocalName = "bookmarkEnd" then + begin + bookmark_id := ''; + bookmark_name := ''; + end + end + if empty_flag then begin line_space := {self.}GetParagraphLineSpace(ppr.RPr.Sz.Val, ppr.Spacing.Line); {self.}DynamicHeight += ppr.Spacing.After + line_space; {self.}EndY -= {self.}DynamicHeight; end - else begin - for _, r in rs do - begin - {self.}SetRPr(r.RPr, paragraph_.PPr); - if r.Br.Type = "page" then - {self.}CheckAndAddPage(sect_ware_.SectPr.PgMar.Bottom, 1); - else if ifObj(r.Drawing.XmlNode) then {self.}RToDrawingRange(r, ppr); - else {self.}RToTextRange(r, ppr); - end - end {self.}RangesToLines(ppr); - // hyperlinks := paragraph_.Hyperlinks(); // TOC - // for _,hyperlink in hyperlinks do - // begin - // rs := hyperlink.Rs(); - // for _,r in rs do - // begin - // if r.FldChar.FldCharType = "begin" then break; - // {self.}RToTextRange(r, ppr); - // end - // end + if hyperlink_array_ then {self.}HyperlinkToToc(ppr); + if bookmark_array_ then {self.}BookMarkLinkToc(); end; -function TSPdfParagraphRange.Do(); +function TSPdfParagraphRange.BookMarkLinkToc(); +begin + for name,arr in bookmark_array_ do + if arr[0] then docx_to_pdf_.LinkToToc(name, arr[0].TSPage); +end; + +function TSPdfParagraphRange.HyperlinkToToc(ppr: PPrUnitDecorator); +begin + for anchor,arr in hyperlink_array_ do // 整理hyperlink发送到docx_to_pdf_ + begin + pg := arr[0].TSPage; + left := {self.}StartX; + bottom := arr[0].EndY; + right := left + {self.}Width; + top := bottom + arr[0].RPr.Sz.Val; + x := arr[0].EndX + arr[0].Width; + y := arr[0].EndY; + for _,range in arr do + begin + if range.TSPage <> pg then // 说明目录文字换页 + begin + rect := array(left, bottom, right, top); + toc := new TSToc(ppr, rect, pg, x, y); + docx_to_pdf_.AddToc(anchor, toc); + pg := range.TSPage; + left := range.EndX; + top := range.EndY + range.RPr.Sz.Val; + right := left; + bottom := range.EndY; + continue; + end + top := range.EndY + arr[0].RPr.Sz.Val; + x := range.EndX + range.Width; + end + rect := array(left, bottom, right, top); + font_name := ppr.RPr.RFonts.EastAsia ? ppr.RPr.RFonts.EastAsia : ppr.RPr.RFonts.Ascii; + font_obj := docx_to_pdf_.Font.GetFont(font_name, ppr.RPr.B, ppr.RPr.I); + toc := new TSToc(ppr, rect, pg, x, y, font_obj); + docx_to_pdf_.AddToc(anchor, toc); + end +end; + +function TSPdfParagraphRange.HyperlinkToTextRange(hyperlink: Hyperlink; ppr: PPrUnitDecorator); +begin + i := length(range_array_); + rs := hyperlink.Rs(); + for _,r in rs do + begin + if r.FldChar.FldCharType = "begin" then break; + r.RPr := new RPr(); + {self.}SetRPr(r.RPr, ppr); + {self.}RToTextRange(r, ppr, hyperlink.Anchor); + end + arr := array(); + while i < length(range_array_) do + begin + arr[length(arr)] := range_array_[i]; + i++; + end + hyperlink_array_[hyperlink.Anchor] := arr; +end; + +function TSPdfParagraphRange.Do();override; begin for _,line_range in line_range_array_ do line_range.Do(); @@ -108,7 +194,7 @@ begin return line_range; end; -function TSPdfParagraphRange.RangesToLines(ppr: PPr); +function TSPdfParagraphRange.RangesToLines(ppr: PPrUnitDecorator); begin line_range := {self.}NewLineRange(); i := 0; @@ -167,7 +253,7 @@ begin {self.}SetLinesAlignment(ppr); end; -function TSPdfParagraphRange.SetLinesAlignment(ppr: PPr); +function TSPdfParagraphRange.SetLinesAlignment(ppr: PPrUnitDecorator); begin if length(line_range_array_) = 0 then return; idx := length(line_range_array_)-1; @@ -200,14 +286,20 @@ begin return 4; end; -function TSPdfParagraphRange.RToTextRange(r: R; ppr: PPr); +function TSPdfParagraphRange.RToTextRange(r: R; ppr: PPrUnitDecorator; link: string); begin - rpr := new RPrUnitDecorator(r.RPr); - text := r.T.Text; - if ifString(text) then {self.}SplitTextToTextRange(text, rpr); + if r.Anchor then + begin + {self.}HyperlinkToTextRange(r, ppr); + end + else begin + rpr := new RPrUnitDecorator(r.RPr); + text := r.T.Text; + if ifString(text) then {self.}SplitTextToTextRange(text, rpr, link); + end end; -function TSPdfParagraphRange.SplitTextToTextRange(text: string; rpr: RPr); +function TSPdfParagraphRange.SplitTextToTextRange(text: string; rpr: RPrUnitDecorator; link: string); begin pos := 1; while pos <= length(text) do @@ -219,7 +311,6 @@ begin font_name := rpr.RFonts.EastAsia ? rpr.RFonts.EastAsia : rpr.RFonts.Ascii; font_obj := docx_to_pdf_.Font.GetFont(font_name, rpr.B, rpr.I); if not rpr.Sz.Val then rpr.Sz.Val := rpr.SzCs.Val ? rpr.SzCs.Val : docx_to_pdf_.Font.GetDefaultSz(); - // println("page_.PdfPage = {}, font_obj = {}, sz = {}", page_.PdfPage, font_obj, rpr.Sz.Val); page_.PdfPage.SetFontAndSize(font_obj, rpr.Sz.Val); word_width := page_.PdfPage.TextWidth(word); text_range := new TSPdfTextRange(); @@ -228,10 +319,11 @@ begin text_range.Font := font_obj; text_range.Width := word_width; range_array_[length(range_array_)] := text_range; + if ifarray(bookmark_array_[link]) then bookmark_array_[link] union= array(text_range); end end; -function TSPdfParagraphRange.RToDrawingRange(r: R; ppr: PPr); +function TSPdfParagraphRange.RToDrawingRange(r: R; ppr: PPrUnitDecorator); begin xfrm := new XfrmUnitDecorator(r.Drawing._Inline.Graphic.GraphicData.Pic.SpPr.Xfrm); rels_adapter := docx_components_ware_.GetDocumentRelsAdapter(); @@ -318,11 +410,11 @@ end; function TSPdfParagraphRange.SetRPr(var rpr; ppr: PPr); begin - // rpr,先继承ppr,再继承样式,最后继承自己 new_rpr := new RPr(); - style_id := rpr.RStyle.Val; - new_rpr.Copy(ppr.RPr); - {self.}SetRPrByStyleId(new_rpr, style_id); + styles := docx_components_ware_.GetStyles(); + new_rpr.Copy(styles.DocDefaults.RPrDefault.RPr); + {self.}SetRPrByStyleId(new_rpr, ppr.PStyle.Val); + {self.}SetRPrByStyleId(new_rpr, rpr.RStyle.Val); new_rpr.Copy(rpr); rpr.Copy(new_rpr); end; @@ -350,7 +442,7 @@ begin {self.}SplitTextToTextRange(lvl_text, rpr); end; -function TSPdfParagraphRange.ResetCoordinates(ppr: PPr); +function TSPdfParagraphRange.ResetCoordinates(ppr: PPrUnitDecorator); begin // 根据段落的间距确定新的坐标 sz := ppr.RPr.SzCs.Val ? ppr.RPr.SzCs.Val : ppr.RPr.Sz.Val ? ppr.RPr.Sz.Val : docx_to_pdf_.Font.GetDefaultSz(); diff --git a/range/Advanced/TSPdfTableRange.tsf b/range/Advanced/TSPdfTableRange.tsf index fcf6ce8..2b3d405 100644 --- a/range/Advanced/TSPdfTableRange.tsf +++ b/range/Advanced/TSPdfTableRange.tsf @@ -7,7 +7,6 @@ public private function ProcessTrData(grid_cols: array of GridCol; tr: Tr; tbl_pr: TblPr): array of TSPdfAbstractRange; function ResetCoordinates(tbl_pr: TblPr; grid_cols: array of GridCol); - function CheckAndAddPage(y: real; offset: real): boolean; function SetTblPr(tbl_pr: TblPr); function SetTblPrByStyleId(var tbl_pr: TblPr; style_id: string); function SetTrPr(var tr_pr: TrPr); @@ -22,7 +21,6 @@ private [weakref]table_: Tbl; [weakref]page_: TSPage; [weakref]range_array_: tableArray; - point_: TSPoint; end; function TSPdfTableRange.Create(docx_to_pdf: TSDocxToPdf; pg: PdfPage; components: Components; sect_ware: TSSectWare; table: Tbl); @@ -60,19 +58,6 @@ begin range.Do(); end; -function TSPdfTableRange.CheckAndAddPage(y: real; offset: real): boolean; -begin - if y - offset < sect_ware_.SectPr.PgMar.Bottom then - begin - page_ := docx_to_pdf_.GetNextPage(page_); - if ifnil(page_) then page_ := docx_to_pdf_.AddPage(sect_ware_); - point := docx_to_pdf_.GetCurrentPoint(); - {self.}EndY := point.Y; - return true; - end - return false; -end; - function TSPdfTableRange.ProcessTrData(grid_cols: array of GridCol; tr: Tr; tbl_pr: TblPr): array of TSPdfAbstractRange; begin {self.}SetTrPr(tr.TrPr); diff --git a/range/Advanced/TSPdfTocRange.tsf b/range/Advanced/TSPdfTocRange.tsf index edb31d0..dc3ccfc 100644 --- a/range/Advanced/TSPdfTocRange.tsf +++ b/range/Advanced/TSPdfTocRange.tsf @@ -1,6 +1,9 @@ type TSPdfTocRange = class(TSPdfBasicRange) public function Create(); + +public + TSPage: TSPage; end; function TSPdfTocRange.Create(); diff --git a/utils/TSPage.tsf b/utils/TSPage.tsf index 2d77780..3c4c342 100644 --- a/utils/TSPage.tsf +++ b/utils/TSPage.tsf @@ -1,5 +1,6 @@ type TSPage = class public - PdfPage; - Index; + PdfPage: PdfPage; + Index: integer; + Number: integer; // 页码 end; diff --git a/utils/TSPoint.tsf b/utils/TSPoint.tsf deleted file mode 100644 index f1298cb..0000000 --- a/utils/TSPoint.tsf +++ /dev/null @@ -1,10 +0,0 @@ -type TSPoint = class - function Create(); - begin - {self.}X := 0; - {self.}Y := 0; - end - X: real; - Y: real; -end; - diff --git a/utils/TSToc.tsf b/utils/TSToc.tsf new file mode 100644 index 0000000..68caf95 --- /dev/null +++ b/utils/TSToc.tsf @@ -0,0 +1,51 @@ +type TSToc = class +uses TSPdfEnumerations; +public + function Create(ppr: PPrUnitDecorator; rect: array of real; page: TSPage; x: real; y: real; font: PdfFont); + function LinkAnnot(dst: PdfDestination); + function AddPageNumber(page: TSPage); + +private + ppr_: PPrUnitDecorator; + rect_: array of real; + font_: PdfFont; + [weakref]tspage_: TSPage; + x_; + y_; +end; + +function TSToc.Create(ppr: PPrUnitDecorator; rect: array of real; page: TSPage; x: real; y: real; font: PdfFont); +begin + ppr_ := ppr; + rect_ := rect; + tspage_ := page; + x_ := x; + y_ := y; + font_ := font; +end; + +function TSToc.LinkAnnot(dst: PdfDestination); +begin + annot := tspage_.PdfPage.CreateLinkAnnot(rect_, dst); + annot.LinkAnnotSetHighlightMode(TSPdfEnumerations.ANNOT_NO_HIGHTLIGHT); +end; + +function TSToc.AddPageNumber(page: TSPage); +begin + number := tostring(page.Number); + symbol := "."; + tspage_.PdfPage.SetFontAndSize(font_, ppr_.RPr.Sz.Val); + w := tspage_.PdfPage.TextWidth(symbol); + number_sz := tspage_.PdfPage.TextWidth(number); + x := rect_[2] - number_sz; + tspage_.PdfPage.BeginText(); + tspage_.PdfPage.TextOut(x, y_, number); + tspage_.PdfPage.EndText(); + while x_ < x - w do + begin + tspage_.PdfPage.BeginText(); + tspage_.PdfPage.TextOut(x_, y_, symbol); + tspage_.PdfPage.EndText(); + x_ += w; + end +end;