type TSPdfParagraphRange = class(TSPdfBasicRange) public function Create(docx_to_pdf: TSDocxToPdf; pg: TSPage; components: TSDocxComponentsWare; sect_ware: TSSectWare; paragraph: P); function Calc(); function Do();override; function SetExtraStyleId(style_id: string); private function SetPPr(var ppr: PPr); function SetPPrByStyleId(var ppr: PPr; style_id: string); function SetRPr(var rpr; ppr: PPr); function SetRPrByStyleId(var rpr: RPr; style_id: string); function SetLvlText(); function GetImageFileType(data: binary): string; function GetParagraphLineSpace(size: real; line: integer): real; function RangesToLines(ppr: PPrUnitDecorator): tableArray; function CheckAndAddPage(y: real; offset: real): boolean; function GetUtf8CharLength(byte: string): integer; 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; [weakref]docx_components_ware_: TSDocxComponentsWare; [weakref]sect_ware_: TSSectWare; [weakref]paragraph_: P; [weakref]page_: TSPage; 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); begin docx_to_pdf_ := docx_to_pdf; page_ := pg; docx_components_ware_ := components; sect_ware_ := sect_ware; paragraph_ := paragraph; 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); {self.}ResetCoordinates(ppr); {self.}EndX := {self.}StartX; {self.}EndY := {self.}StartY; {self.}CheckAndAddPage({self.}EndY, ppr.Spacing.Before); {self.}EndY -= ppr.Spacing.Before; {self.}DynamicHeight += ppr.Spacing.Before; 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 {self.}RangesToLines(ppr); if hyperlink_array_ then {self.}HyperlinkToToc(ppr); if bookmark_array_ then {self.}BookMarkLinkToc(); end; 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(); end; function TSPdfParagraphRange.SetExtraStyleId(style_id: string); begin extra_style_id_ := style_id; end; function TSPdfParagraphRange.NewLineRange(): TSPdfLineRange; begin line_range := new TSPdfLineRange(page_); line_range.StartX := {self.}EndX; line_range.StartY := {self.}EndY; line_range.Width := {self.}Width; return line_range; end; function TSPdfParagraphRange.RangesToLines(ppr: PPrUnitDecorator); begin line_range := {self.}NewLineRange(); i := 0; max_size := 0; max_y := 0; while i <= length(range_array_)-1 do begin range := range_array_[i]; if i = 0 then {self.}EndX += ppr.Ind.FirstLine; if range is class(TSPdfTextRange) and range.RPr.Sz.Val > max_size then max_size := range.RPr.Sz.Val; if range.DynamicHeight > max_y then max_y := range.DynamicHeight; line_space := {self.}GetParagraphLineSpace(max_size, ppr.Spacing.Line); diff := {self.}EndY - sect_ware_.SectPr.PgMar.Bottom; if {self.}CheckAndAddPage({self.}EndY, max(line_space, range.DynamicHeight)) then {self.}DynamicHeight += diff; if_newline := {self.}EndX + range.Width - {self.}StartX > {self.}Width + 1e-6; if if_newline and range.Width < {self.}Width then begin offset := line_space > max_y ? (line_space - max_size) / 2 + max_size - max_size / 5 : max_y; max_value := max(line_space, max_y); line_range.TSPage := page_; line_range.EndY := {self.}EndY - max_value; line_range.DynamicHeight := max_value; line_range.SetAllRangeProp(pg: page_, ey: {self.}EndY - offset); line_range_array_[length(line_range_array_)] := line_range; line_range := {self.}NewLineRange(); max_size := 0; max_y := 0; {self.}DynamicHeight += max_value; {self.}EndY -= max_value; {self.}EndX := {self.}StartX; // w:hanging sz := range.RPr.SzCs.Val ? range.RPr.SzCs.Val : range.RPr.Sz.Val ? range.RPr.Sz.Val : docx_to_pdf_.Font.GetDefaultSz(); {self.}EndX -= ppr.Ind.HangingChars ? ppr.Ind.HangingChars * sz : ppr.Ind.Hanging; continue; end range.EndX := {self.}EndX - range.StartX; {self.}EndX += range.Width; line_range.AddRange(range); i++; if i = length(range_array_) then begin offset := line_space > max_y ? (line_space - max_size) / 2 + max_size - max_size / 5 : max_y; max_value := max(line_space, max_y) + ppr.Spacing.After; line_range.TSPage := page_; line_range.EndY := {self.}EndY - max_value; line_range.DynamicHeight := max_value; line_range.SetAllRangeProp(pg: page_, ey: {self.}EndY - offset); line_range_array_[length(line_range_array_)] := line_range; {self.}DynamicHeight += max_value; {self.}EndY -= max_value; end end {self.}SetLinesAlignment(ppr); end; function TSPdfParagraphRange.SetLinesAlignment(ppr: PPrUnitDecorator); begin if length(line_range_array_) = 0 then return; idx := length(line_range_array_)-1; line_range := line_range_array_[idx]; line_range.Align(ppr.Jc.Val); end; function TSPdfParagraphRange.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 TSPdfParagraphRange.GetUtf8CharLength(byte: string): integer; begin if (_and(ord(byte), 0b10000000)) = 0 then return 1; else if (_and(ord(byte), 0b11100000)) = 0b11000000 then return 2; else if (_and(ord(byte), 0b11110000)) = 0b11100000 then return 3; else if (_and(ord(byte), 0b11111000)) = 0b11110000 then return 4; end; function TSPdfParagraphRange.RToTextRange(r: R; ppr: PPrUnitDecorator; link: string); begin 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: RPrUnitDecorator; link: string); begin pos := 1; while pos <= length(text) do begin num := {self.}GetUtf8CharLength(text[pos]); word := text[pos : pos+num-1]; word := utf8ToAnsi(word); pos += num; 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(); page_.PdfPage.SetFontAndSize(font_obj, rpr.Sz.Val); word_width := page_.PdfPage.TextWidth(word); text_range := new TSPdfTextRange(); text_range.RPr := rpr; text_range.Text := word; 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: PPrUnitDecorator); begin xfrm := new XfrmUnitDecorator(r.Drawing._Inline.Graphic.GraphicData.Pic.SpPr.Xfrm); rels_adapter := docx_components_ware_.GetDocumentRelsAdapter(); id := r.Drawing._Inline.Graphic.GraphicData.Pic.BlipFill.Blip.Embed; rel := rels_adapter.Id(id); image_path := "word/" + rel.Target; image := docx_components_ware_.Zip().Get(image_path); data := image.Data(); image_path := docx_to_pdf_.GetCachePath(image_path); writeFile(rwBinary(), "", image_path, 0, length(data)-1, data); image := nil; case {self.}GetImageFileType(data) of "png": image := docx_to_pdf_.GetPdf().LoadPngImageFromFile("", image_path); "jpg": image := docx_to_pdf_.GetPdf().LoadJpegImageFromFile("", image_path); end; image_range := new TSPdfImageRange(); image_range.Image := image; image_range.StartX := xfrm.Off.X; image_range.StartY := xfrm.Off.Y; image_range.Width := xfrm.Ext.CX; image_range.DynamicHeight := xfrm.Ext.CY; fileDelete("", image_path); range_array_[length(range_array_)] := image_range; end; function TSPdfParagraphRange.GetImageFileType(data: binary): string; begin stream := new TMemoryStream(); size := length(data); stream.Write(data[0:size-1], size); def := array( ('name': 'png', 'position': 0, 'value': array(0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A)), ('name': 'jpg', 'position': 0, 'value': array(0xFF, 0xD8)), ); for i:=0 to length(def)-1 do begin value := def[i]['value']; stream.Seek(def[i]['position']); for j:=0 to length(value)-1 do begin r := 0; stream.Read(r, 1); if r <> value[j] then break; if j = length(value)-1 then return def[i]['name']; end end return ''; end; function TSPdfParagraphRange.GetParagraphLineSpace(size: real; line: integer): real; begin if not line then line := 12; lines := roundto(line / 12, -1); multi := ceil(size / sect_ware_.BaseSize) * lines; return sect_ware_.SectPr.DocGrid.LinePitch * multi; end; function TSPdfParagraphRange.SetPPr(var ppr: PPr); begin new_ppr := new PPr(); styles := docx_components_ware_.GetStyles(); new_ppr.Copy(styles.DocDefaults.PPrDefault.PPr); new_ppr.RPr.Copy(styles.DocDefaults.RPrDefault.RPr); {self.}SetPPrByStyleId(new_ppr, extra_style_id_); {self.}SetPPrByStyleId(new_ppr, ppr.PStyle.Val); new_ppr.Copy(ppr); ppr.Copy(new_ppr); end; function TSPdfParagraphRange.SetPPrByStyleId(var ppr: PPr; style_id: string); begin styles := docx_components_ware_.GetStylesAdapter(); style := styles.StyleId(style_id); if ifObj(style) then begin based_on := style.BasedOn.Val; {self.}SetPPrByStyleId(ppr, based_on); ppr.Copy(style.PPr); ppr.RPr.Copy(style.RPr); end end; function TSPdfParagraphRange.SetRPr(var rpr; ppr: PPr); begin new_rpr := new RPr(); 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; function TSPdfParagraphRange.SetRPrByStyleId(var rpr: RPr; style_id: string); begin styles := docx_components_ware_.GetStylesAdapter(); style := styles.StyleId(style_id); if ifObj(style) then begin based_on := style.BasedOn.Val; {self.}SetRPrByStyleId(rpr, based_on); rpr.Copy(style.RPr); end end; function TSPdfParagraphRange.SetLvlText(); begin numbering_ware := docx_components_ware_.GetNumberingWare(); if not ifObj(numbering_ware) then return; [lvl_text, lvl] := numbering_ware.GetNumberLvl(paragraph_.PPr); if lvl_text = "" and ifnil(lvl) then return; {self.}SetRPr(lvl.RPr, paragraph_.PPr); rpr := new RPrUnitDecorator(lvl.RPr); {self.}SplitTextToTextRange(lvl_text, rpr); end; 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(); {self.}StartX += ppr.Ind.LeftChars ? ppr.Ind.LeftChars * sz : ppr.Ind.Left; {self.}Width -= ppr.Ind.LeftChars ? ppr.Ind.LeftChars * sz : ppr.Ind.Left; {self.}Width -= ppr.Ind.RightChars ? ppr.Ind.RightChars * sz : ppr.Ind.Right; end;