type TSDocxToPdf = class uses TSColorToolKit, TSPdfEnumerations; public function Create(alias, file); function SaveToFile(alias, file); function Transform(); property Font read ReadFont; function ReadFont(); private function Init(); function InitSectPr(); function InitEncoder(); function InitPoint(); function InitStyles(); function TransformParagraph(paragraph); function TransformDrawing(drawing); function TransformTable(table); function AddPage(); function CheckAndAddPage(offset); function ResetCoordinates(); function CalcPagragraphPt(size, line); function FloatN(R, N); function ParagraphWordsToLine(ware); function GetElementType(element); function SetPageItalic(page, x, y); // 模拟倾斜[废弃] // test用 function PrintGrid(); private docx_components_; // Components@DOCX styles_adapter_; // StylesAdapter@Docx pdf_; // PdfFile point_; // Point sect_; base_size_; // 基准字体大小 font_ware_; current_page_; // 当前page end; type Point = class // 定位当前的位置 public X; Y; end; function TSDocxToPdf.Create(alias, file); begin NameSpace "DOCX"; docx_components_ := new Components(); [err, msg] := docx_components_.OpenFile(alias, file, nil); if err then raise "Create obejct 'TSDocxFile' failed."; self.Init(); end; function TSDocxToPdf.Init(); begin pdf_ := new PdfFile(); self.InitStyles(); self.InitEncoder(); self.InitSectPr(); self.InitPoint(); self.AddPage(); base_size_ := integer(sect_.DocGrid.LinePitch / 1.38, 2); font_ware_ := new TSFontWare(pdf_); end; function TSDocxToPdf.InitEncoder(); begin pdf_.UseCNSFonts(); pdf_.UseCNSEncodings(); // pdf_.UseUTFEncodings(); end; function TSDocxToPdf.InitSectPr(); begin document := docx_components_.Document; document.Deserialize(); sect_ := document.Body.SectPr; // 装饰器进行转换 sect_ := new SectPrUnitDecorator(sect_); sect_.PgSz.Orient := sect_.PgSz.Orient ? "portrait" : "landscape"; // println("LinePitch = {}, Type = {}", sect_.DocGrid.LinePitch, sect_.DocGrid.Type); // println("Width = {}, Height = {}", sect_.PgSz.W, sect_.PgSz.H); // println("Top = {}, Right = {}, Bottom = {}, Left = {}, Header = {}, Footer = {}\n", // sect_.PgMar.Top, sect_.PgMar.Right, sect_.PgMar.Bottom, sect_.PgMar.Left, sect_.PgMar.Header, sect_.PgMar.Footer); end; function TSDocxToPdf.InitPoint(); begin // 起始y的位置应为max(top, header) + 行距 point_ := new Point(); self.ResetCoordinates(); end; function TSDocxToPdf.InitStyles(); begin styles := docx_components_.Styles; styles.Deserialize(); styles_adapter_ := new StylesAdapter(styles); end; function TSDocxToPdf.AddPage(); begin current_page_ := pdf_.AddPage(); current_page_.SetWidth(sect_.PgSz.W); current_page_.SetHeight(sect_.PgSz.H); self.PrintGrid(); end; function TSDocxToPdf.ResetCoordinates(); begin point_.X := sect_.PgMar.Left; point_.Y := sect_.PgSz.H - sect_.PgMar.Top; end; ///返回:err function TSDocxToPdf.SaveToFile(alias, file); begin return pdf_.SaveToFile(alias, file); end; function TSDocxToPdf.PrintGrid(); // test用 begin page := current_page_; i := 0; while true do begin y := point_.Y - i * sect_.DocGrid.LinePitch; if y <= sect_.PgMar.Bottom then break; page.SetLineWidth(0.05); page.SetGrayStroke(0.75); page.MoveTo(sect_.PgMar.Left, y); page.LineTo(sect_.PgSz.W- sect_.PgMar.Right, y); page.Stroke(); i++; end x1 := sect_.PgMar.Left; y1 := sect_.PgSz.H - sect_.PgMar.Top; x2 := sect_.PgSz.W - sect_.PgMar.Right; y2 := y1; x3 := x1; y3 := sect_.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.GetElementType(element); begin if element.LocalName = 'p' then return 1; if element.LocalName = 'tbl' then return 3; return 0; end; function TSDocxToPdf.Transform(); begin elements := docx_components_.Document.Body.Elements(); for i:=0 to length(elements)-1 do // for i:=0 to 2 do begin println("i = {}", i); case self.GetElementType(elements[i]) of 1: self.TransformParagraph(elements[i]); // 普通段落 2: self.TransformDrawing(elements[i]); // 图片段落 3: self.TransformTable(elements[i]); // 表格 end; end end; function TSDocxToPdf.CheckAndAddPage(offset); begin offset := ifnil(offset) ? 0 : offset; if point_.Y - offset <= sect_.PgMar.Bottom then begin self.AddPage(); self.ResetCoordinates(); self.PrintGrid(); return true; end return false; end; function TSDocxToPdf.ParagraphWordsToLine(ware); begin self.CheckAndAddPage(); page := current_page_; lines := array(); x := point_.X + ware.Paragraph.PPr.Ind.FirstLine; y := point_.Y; max_size := 0; i := 0; begin_index := 0; words := ware.GetWords(); while i <= length(words)-1 do begin [word, rpr] := words[i]; if rpr.Sz.Val > max_size then max_size := rpr.Sz.Val; font_obj := font_ware_.GetFont(rpr.RFonts.EastAsia, rpr.B, rpr.I); lines[i]["page"] := page; lines[i]["font"] := font_obj; lines[i]["word"] := word; lines[i]["rpr"] := rpr; lines[i]["x"] := x; page.SetFontAndSize(font_obj, rpr.Sz.Val); w := page.TextWidth(word); // println("word = {}, x = {}, w = {}, sz = {}", word, x, w, rpr.Sz.Val); x += w; if ware.Paragraph.PPr.AutoSpaceDN and i < length(words)-1 then begin current_len := length(word); next_len := length(words[i+1][0]); if (current_len = 1 and next_len >= 2) or (current_len >= 2 and next_len = 1) then begin cord := current_len = 1 ? ord(word) : ord(words[i+1][0]); if cord >= 48 and cord <= 57 then x += rpr.Sz.Val * 0.27; end end if ware.Paragraph.PPr.AutoSpaceDE and i < length(words)-1 then begin current_len := length(word); next_len := length(words[i+1][0]); if (current_len = 1 and next_len >= 2) or (current_len >= 2 and next_len = 1) then begin cord := current_len = 1 ? ord(word) : ord(words[i+1][0]); if (cord >= 97 and cord <= 122) or (cord >= 65 and cord <= 90) then x += rpr.Sz.Val * 0.27; end end line_pt := self.CalcPagragraphPt(max_size, ware.Paragraph.PPr.Spacing.Line); offset := (line_pt - max_size) / 2; if self.CheckAndAddPage(line_pt) then // 换页,x不变,y变 begin page := current_page_; x := point_.X; y := point_.Y; i := begin_index; max_size := 0; continue; end if x >= sect_.PgSz.W - sect_.PgMar.Right then // 换行 begin y := y - offset - max_size + max_size / 5; for j:=begin_index to i-1 do lines[j]["y"] := y; // 重置参数 begin_index := i; max_size := 0; x := point_.X; y := point_.Y - line_pt; point_.Y := y; // 换页 if self.CheckAndAddPage() then begin page := current_page_; x := point_.X; y := point_.Y; end end else begin i++; if i > length(words)-1 then // 到了末尾仍未换行 begin y := y - offset - max_size + max_size / 5; for j:=begin_index to length(words)-1 do lines[j]["y"] := y; point_.Y -= line_pt; end end end return lines; end; function TSDocxToPdf.SetPageItalic(page, x, y); begin angle := 130; rad := angle / 180 / Pi(); page.SetTextMatrix(1, 0, tan(rad), 1, x, y); end; function TSDocxToPdf.TransformParagraph(paragraph); begin // 1. 字体大小 // 2. 字体颜色 // 3. 字体斜体 // 4. 字体粗体 // 5. 首行间距 // 6. 字符间距 // 7. 下划线 // 8. 删除线 // 9. 上下标 // 10. 对齐方式 paragraph_ware := new TSParagraphWare(docx_components_, styles_adapter_, paragraph); paragraph_ware.Do(); // 将段落中间件的每一个字符序列化成每一行 lines := self.ParagraphWordsToLine(paragraph_ware); // 开始写入pdf for i:=0 to length(lines)-1 do begin line := lines[i]; word := line["word"]; rpr := line["rpr"]; [r, g, b] := array(0, 0, 0); if rpr.Color.Val then [r, g, b] := TSColorToolKit.HexToRGB(rpr.Color.Val); page := line["page"]; page.SetRGBFill(r / 255, g / 255, b / 255); page.SetFontAndSize(line["font"], rpr.Sz.Val); x := line["x"]; y := line["y"]; if draw_bold then begin // 1. 调整偏移位置重复绘制 -- 当前参数的效果不太好 // z := 0.0005 * sqrt(x*x + y*y) * sqrt(2) / 2; // left_x := x - z; // 左下角x位置 // left_y := y - z; // 左下角y位置 // right_x := x + z; // 右上角x位置 // right_y := y + z; // 右上角y位置 // offset := word.WordProperty.Size / 1000; // 每次偏移千分之一 // println("z = {}", z); // println("x = {}, y = {}, offset = {}", x, y, offset); // println("left_x = {}, left_y = {}, right_x = {}, left_y = {}\n", left_x, left_y, right_x, right_x); // while left_x <= right_x and left_y <= right_y do // begin // if draw_italic then self.SetPageItalic(page, left_x, left_y); // page.TextOut(left_x, left_y, word.Word); // left_x += offset; // left_y += right_y; // end // 2. 调整字体大小,x,y位置重复绘制 multi := 1.030; size := word.WordProperty.Size; target_size := size * multi; offset := size * 0.01; size /= multi; while size <= target_size do begin page.SetFontAndSize(font, size); page.beginText(); if draw_italic then self.SetPageItalic(page, x, y); page.TextOut(x, y, word.Word); page.endText(); size += offset; end end else begin page.beginText(); page.TextOut(x, y, word); page.endText(); end page.SetRGBFill(0, 0, 0); end end; function TSDocxToPdf.TransformDrawing(drawing); begin end; function TSDocxToPdf.TransformTable(table); begin end; function TSDocxToPdf.CalcPagragraphPt(size, line); begin if ifnil(line) then line := 12; lines := self.FloatN(line / 12, 2); multi := Ceil(size / base_size_); return sect_.DocGrid.LinePitch * multi; end; function TSDocxToPdf.FloatN(r, n); begin return Round(r * IntPower(10, n)) / IntPower(10, n); end; function TSDocxToPdf.ReadFont(); begin return font_ware_; end;