354 lines
12 KiB
Plaintext
354 lines
12 KiB
Plaintext
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;
|