PdfConverter/TSDocxToPdf.tsf

419 lines
9.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;