type TSPdfParagraphRange = class(TSPdfAbstractRange) public function Create(docx_to_pdf: TSDocxToPdf; page: PdfPage; components: Components; 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: PPr): 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); private docx_to_pdf_: TSDocxToPdf; docx_components_: Components; sect_ware_: TSSectWare; paragraph_: P; extra_style_id_: string; range_array_: array of TSPdfAbstractRange; page_: PdfPage; point_: TSPoint; lines_range_array_: tableArray; end; function TSPdfParagraphRange.Create(docx_to_pdf: TSDocxToPdf; page: PdfPage; components: Components; sect_ware: TSSectWare; paragraph: P); begin docx_to_pdf_ := docx_to_pdf; page_ := page; docx_components_ := Components; sect_ware_ := sect_ware; paragraph_ := paragraph; extra_style_id_ := ""; range_array_ := array(); point_ := new TSPoint(); // 动态记录段落的坐标位置 lines_range_array_ := array(); end; function TSPdfParagraphRange.Calc(): tableArray; begin self.SetPPr(paragraph_.PPr); self.SetLvlText(); ppr := new PPrUnitDecorator(paragraph_.PPr); self.ResetCoordinates(ppr); point_.X := self.X; point_.Y := self.Y; self.CheckAndAddPage(point_.Y, ppr.Spacing.Before); point_.Y -= ppr.Spacing.Before; rs := paragraph_.Rs(); if length(rs) = 0 then begin line_space := self.GetParagraphLineSpace(ppr.RPr.Sz.Val, ppr.Spacing.Line); if not self.H then self.H := ppr.Spacing.After + line_space; point_.Y -= self.H; 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); end; function TSPdfParagraphRange.Do(); begin for _,line_range in lines_range_array_ do for _,range in line_range do range.Do(); end; function TSPdfParagraphRange.SetExtraStyleId(style_id: string); begin extra_style_id_ := style_id; end; function TSPdfParagraphRange.RangesToLines(ppr: PPr); begin lines_range_array_ := array(); i := 0; max_size := 0; max_y := 0; line_range := array(); total_height := 0; while i <= length(range_array_)-1 do begin range := range_array_[i]; if i = 0 then point_.X += ppr.Ind.FirstLine; if range is class(TSPdfTextRange) and range.RPr.Sz.Val > max_size then max_size := range.RPr.Sz.Val; if range.H > max_y then max_y := range.H; line_space := self.GetParagraphLineSpace(max_size, ppr.Spacing.Line); diff := point_.Y - sect_ware_.SectPr.PgMar.Bottom; if self.CheckAndAddPage(point_.Y, max(line_space, range.H)) then total_height += diff; if_newline := point_.X + range.W - self.X > self.W + 1e-6; if if_newline and range.W < self.W then begin offset := line_space > max_y ? (line_space - max_size) / 2 + max_size - max_size / 5 : max_y; for _,item in line_range do begin item.Page := page_; item.Y := point_.Y - offset; end lines_range_array_[length(lines_range_array_)] := line_range; line_range := array(); max_value := max(line_space, max_y); max_size := 0; max_y := 0; total_height += max_value; point_.Y -= max_value; point_.X := self.X; // w:hanging sz := range.RPr.SzCs.Val ? range.RPr.SzCs.Val : range.RPr.Sz.Val ? range.RPr.Sz.Val : docx_to_pdf_.Font.GetDefaultSz(); point_.X -= ppr.Ind.HangingChars ? ppr.Ind.HangingChars * sz : ppr.Ind.Hanging; continue; end range.X := point_.X - range.X; point_.X += range.W; line_range[length(line_range)] := 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; for _,item in line_range do begin item.Page := page_; item.Y := point_.Y - offset; end lines_range_array_[length(lines_range_array_)] := line_range; max_value := max(line_space, max_y) + ppr.Spacing.After; total_height += max_value; point_.Y -= max_value; end end self.SetLinesAlignment(ppr); if not self.H then self.H := total_height; self.Y := point_.Y; end; function TSPdfParagraphRange.SetLinesAlignment(ppr: PPr); begin if length(lines_range_array_) = 0 then return; idx := length(lines_range_array_)-1; line := lines_range_array_[idx]; offset := 0; case ppr.Jc.Val of "center": begin first := line[0]; last := line[length(line)-1]; offset := (self.W - last.X + first.X - last.W) / 2; end "right": begin first := line[0]; last := line[length(line)-1]; offset := self.W - last.X + first.X - last.W; end end; if offset < 0 then return; if offset then for i:=0 to length(line)-1 do line[i].X += offset; 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(); point_.Y := 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: PPr); begin rpr := new RPrUnitDecorator(r.RPr); text := r.T.Text; if ifString(text) then self.SplitTextToTextRange(text, rpr); end; function TSPdfParagraphRange.SplitTextToTextRange(text: string; rpr: RPr); 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_.SetFontAndSize(font_obj, rpr.Sz.Val); word_width := page_.TextWidth(word); text_range := new TSPdfTextRange(); text_range.RPr := rpr; text_range.Text := word; text_range.Font := font_obj; text_range.W := word_width; text_range.H := 0; text_range.X := 0; text_range.Y := 0; range_array_[length(range_array_)] := text_range; end end; function TSPdfParagraphRange.RToDrawingRange(r: R; ppr: PPr); begin xfrm := new XfrmUnitDecorator(r.Drawing._Inline.Graphic.GraphicData.Pic.SpPr.Xfrm); rels_adapter := class(TSPdfSingletonKit).GetComponent(docx_components_, "document_rels_adapter"); id := r.Drawing._Inline.Graphic.GraphicData.Pic.BlipFill.Blip.Embed; rel := rels_adapter.Id(id); image_path := "word/" + rel.Target; image := docx_components_.Zip().Get(image_path); data := image.Data(); image_path := docx_to_pdf_.GetTempPath(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.X := xfrm.Off.X; image_range.Y := xfrm.Off.Y; image_range.W := xfrm.Ext.CX; image_range.H := 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 := class(TSPdfSingletonKit).GetComponent(docx_components_, "styles"); 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 := class(TSPdfSingletonKit).GetComponent(docx_components_, "styles_adapter"); 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 // rpr,先继承ppr,再继承样式,最后继承自己 new_rpr := new RPr(); style_id := rpr.RStyle.Val; new_rpr.Copy(ppr.RPr); self.SetRPrByStyleId(new_rpr, style_id); new_rpr.Copy(rpr); rpr.Copy(new_rpr); end; function TSPdfParagraphRange.SetRPrByStyleId(var rpr: RPr; style_id: string); begin styles := class(TSPdfSingletonKit).GetComponent(docx_components_, "styles_adapter"); 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 := class(TSPdfSingletonKit).GetComponent(docx_components_, "numbering_ware"); 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); begin // 根据段落的间距确定新的坐标 sz := ppr.RPr.SzCs.Val ? ppr.RPr.SzCs.Val : ppr.RPr.Sz.Val ? ppr.RPr.Sz.Val : docx_to_pdf_.Font.GetDefaultSz(); self.X += ppr.Ind.LeftChars ? ppr.Ind.LeftChars * sz : ppr.Ind.Left; self.W -= ppr.Ind.LeftChars ? ppr.Ind.LeftChars * sz : ppr.Ind.Left; self.W -= ppr.Ind.RightChars ? ppr.Ind.RightChars * sz : ppr.Ind.Right; end;