重构代码,抽象成range,完成段落的重构

This commit is contained in:
csh
2024-06-21 10:28:47 +08:00
parent 61001c9a46
commit 007da049a3
15 changed files with 1074 additions and 601 deletions
+10
View File
@@ -0,0 +1,10 @@
type TSPdfAbstractRange = class
public
function Do();virtual;
public
X: real;
Y: real; // range的起始坐标(x,y)
W: real; // range的宽度
H: real; // range的高度
end;
+26
View File
@@ -0,0 +1,26 @@
type TSPdfImageRange = class(TSPdfAbstractRange)
public
function Do();override;
public
Page: PdfPage;
Image: PdfImage;
end;
function TSPdfImageRange.Do();
begin
println("image = {}, x = {}, y = {}, w = {}, h = {}", image, x, y, w, h);
self.Page.DrawImage(self.Image, self.X, self.Y, self.W, self.H);
{$DEFINE WordToPdfTEST}
{$IFDEF WordToPdfTEST}
// Page.SetLineWidth(0.5);
// Page.SetGrayStroke(0.5);
// Page.MoveTo(0, self.Y);
// Page.LineTo(90, self.Y);
// Page.Stroke();
self.Page.SetLineWidth(0.1);
self.Page.SetGrayStroke(0.5);
self.Page.Rectangle(0, Y, 900, H);
self.Page.Stroke();
{$ENDIF}
end;
+353
View File
@@ -0,0 +1,353 @@
type TSPdfParagraphRange = class(TSPdfAbstractRange)
public
function Create(docx_to_pdf: TSDocxToPdf; 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(lines_range_array: tableArray; ppr: 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; components: Components; sect_ware: TSSectWare; paragraph: P);
begin
docx_to_pdf_ := docx_to_pdf;
docx_components_ := Components;
sect_ware_ := sect_ware;
paragraph_ := paragraph;
extra_style_id_ := "";
page_ := docx_to_pdf_.GetCurrentPage();
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.X += ppr.Ind.Left;
self.W := self.W - ppr.Ind.Left - ppr.Ind.Right;
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);
if self.CheckAndAddPage(point_.Y, max(line_space, range.H)) then
total_height += point_.Y - sect_ware_.SectPr.PgMar.Bottom;
range.X := point_.X - range.X;
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;
continue;
end
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);
total_height += max_value;
point_.Y -= max_value;
end
end
// self.SetLinesAlignment(lines_range_array, ppr);
if not self.H then self.H := total_height;
self.Y := point_.Y;
end;
function TSPdfParagraphRange.SetLinesAlignment(lines_range_array: tableArray; 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 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_.AddPage(sect_ware_);
point := docx_to_pdf_.GetCurrentPoint();
point_.Y := point.Y;
page_array_ := array(page_);
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, 0, 0);
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;
+13
View File
@@ -0,0 +1,13 @@
type TSPdfRectRange = class(TSPdfAbstractRange)
public
function Do();override;
public
Page: PdfPage;
end;
function TSPdfRectRange.Do();override;
begin
self.Page.SetRGBStroke(1.0, 0.0, 0.0);
self.Page.Rectangle(self.X, self.Y - self.H, self.W, self.H);
self.Page.Stroke();
end;
+177
View File
@@ -0,0 +1,177 @@
type TSPdfTableRange = class(TSPdfAbstractRange)
public
function Create(docx_to_pdf: TSDocxToPdf; components: Components; sect_ware: TSSectWare; paragraph: P);
function Calc(): tableArray;
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 SetTblPr(tbl_pr: TblPr);
function SetTblPrByStyleId(var tbl_pr: TblPr; style_id: string);
function SetTrPr(var tr_pr: TrPr);
function SetTrPrByStyleId(var tr_pr: TrPr; style_id: string);
function SetTcPr(var tc_pr: TcPr);
function SetTcPrByStyleId(var tc_pr: TcPr; style_id: string);
private
docx_to_pdf_: TSDocxToPdf;
docx_components_: Components;
sect_ware_: TSSectWare;
table_: Tbl;
range_array_: array of TSPdfAbstractRange;
page_: PdfPage;
point_: TSPoint;
end;
function TSPdfTableRange.Create(docx_to_pdf: TSDocxToPdf; components: Components; sect_ware: TSSectWare; table: Tbl);
begin
docx_to_pdf_ := docx_to_pdf;
docx_components_ := Components;
sect_ware_ := sect_ware;
table_ := table;
page_ := docx_to_pdf_.GetCurrentPage();
range_array_ := array();
point_ := new TSPoint(); // 动态记录段落的坐标位置
end;
function TSPdfTableRange.Calc(): tableArray;
begin
self.SetTblPr(table_.TblPr);
tbl_pr := new TblPrUnitDecorator(table_.TblPr);
grid_cols := table_.TblGrid.GridCols();
for i:=0 to length(grid_cols)-1 do
grid_cols[i] := new GridColUnitDecorator(grid_cols[i]);
self.ResetCoordinates(tbl_pr, grid_cols);
point_.X := self.X;
point_.Y := self.Y;
// 如果是根据内容自适应,应该计算并调整grid_cols的值
trs := table_.Trs();
matrix := array();
for i,tr in trs do
matrix[i] := self.ProcessTrData(grid_cols, tr, tbl_pr);
return matrix;
end;
function TSPdfTableRange.ProcessTrData(grid_cols: array of GridCol; tr: Tr; tbl_pr: TblPr): array of TSPdfAbstractRange;
begin
self.SetTrPr(tr.TrPr);
tr_pr := new TrPrUnitDecorator(tr.TrPr);
tcs := tr.Tcs();
rows_range_array := array();
for _,tc in tcs do
begin
elements := tc.Elements();
rect_range := new TSPdfRectRange();
rect_range.X := point_.X;
rect_range.Y := point_.Y;
rect_range.W := grid_col.W;
rect_range.H := 100;
rows_range_array[length(rows_range_array)] := rect_range;
for _,element in elements do
begin
range := nil;
if element.LocalName = "p" then
begin
range := new TSPdfParagraphRange(docx_to_pdf_, docx_components_, sect_ware_, element);
range.SetExtraStyleId(tbl_pr.TblStyle.Val);
end
else if element.LocalName = "tbl" then
begin
range := new TSPdfTableRange(docx_to_pdf_, docx_components_, sect_ware_, element);
end
if ifObj(range) then
begin
range.X := point_.X - tbl_pr.TblCellMar.Left.W;
range.Y := point_.Y - tbl_pr.TblCellMar.Top.W;
range.W := grid_col.W - tbl_pr.TblCellMar.Right.W;
range.H := nil;
ret := range.Calc();
point_.Y := range.Y;
point_.X += grid_col.W;
rows_range_array[length(rows_range_array)] := range;
end
end
end
return rows_range_array;
end;
function TSPdfTableRange.ResetCoordinates(tbl_pr: TblPr; grid_cols: array of GridCol);
begin
total_width := 0;
for _,grid_col in grid_cols do
total_width += grid_col.W;
diff := total_width - self.W;
case tbl_pr.jc.Val of
"center":
begin
offset := diff/2;
self.X -= offset;
end
"right":
begin
self.X -= diff;
end
end;
self.W := total_width;
end;
function TSPdfTableRange.SetTblPr(var tbl_pr: TblPr);
begin
new_tbl_pr := new TblPr();
tbl_pr.TblStyle.XmlNode.print;
self.SetTblPrByStyleId(new_tbl_pr, tbl_pr.TblStyle.Val);
new_tbl_pr.Copy(tbl_pr);
tbl_pr.Copy(new_tbl_pr);
end;
function TSPdfTableRange.SetTblPrByStyleId(var tbl_pr: TblPr; 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.SetTblPrByStyleId(tbl_pr, based_on);
tbl_pr.Copy(style.TblPr);
end
end;
function TSPdfTableRange.SetTrPr(var tr_pr: TrPr);
begin
new_tr_pr := new TrPr();
self.SetTrPrByStyleId(new_tr_pr, table_.TblPr.TblStyle.Val);
new_tr_pr.Copy(tr_pr);
tr_pr.Copy(new_tr_pr);
end;
function TSPdfTableRange.SetTrPrByStyleId(var tr_pr: TrPr; 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.SetTrPrByStyleId(tr_pr, based_on);
tr_pr.Copy(style.TrPr);
end
end;
function TSPdfTableRange.SetTcPr(var tc_pr: TcPr);
begin
new_tc_pr := new TcPr();
self.SetTcPrByStyleId(new_tc_pr, table_.TblPr.TblStyle.Val);
new_tc_pr.Copy(tc_pr);
tc_pr.Copy(new_tc_pr);
end;
function TSPdfTableRange.SetTcPrByStyleId(var tc_pr: TcPr; 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.SetTcPrByStyleId(tc_pr, based_on);
tc_pr.Copy(style.TcPr);
end
end;
+33
View File
@@ -0,0 +1,33 @@
type TSPdfTextRange = class(TSPdfAbstractRange)
uses TSColorToolKit;
public
function Do();override;
public
RPr: RPr;
Text: string;
Font: PdfFont;
Page: PdfPage;
end;
function TSPdfTextRange.Do();
begin
// println("text = {}, x = {}, y = {}, w = {}, sz = {}", ansiToUtf8(text), x, y, w);
[r, g, b] := array(0, 0, 0);
if self.RPr.Color.Val then [r, g, b] := TSColorToolKit.HexToRGB(self.RPr.Color.Val);
self.Page.SetRGBFill(r / 255, g / 255, b / 255);
self.Page.SetFontAndSize(self.Font, self.RPr.Sz.Val);
self.Page.BeginText();
self.Page.TextOut(self.X, self.Y, self.Text);
self.Page.EndText();
self.Page.SetRGBFill(0, 0, 0);
{$DEFINE WordToPdfTEST}
{$IFDEF WordToPdfTEST}
self.Page.SetLineWidth(0.5);
self.Page.SetGrayStroke(0.5);
self.Page.MoveTo(0, self.Y);
self.Page.LineTo(90, self.Y);
self.Page.Stroke();
{$ENDIF}
end;