PdfConverter/range/Advanced/TSPdfParagraphRange.tsf

361 lines
13 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 TSPdfParagraphRange = class(TSPdfBasicRange)
public
function Create(docx_to_pdf: TSDocxToPdf; pg: TSPage; components: TSDocxComponentsWare; 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: PPr);
function NewLineRange(): TSPdfLineRange;
private
[weakref]docx_to_pdf_: TSDocxToPdf;
[weakref]docx_components_ware_: TSDocxComponentsWare;
[weakref]sect_ware_: TSSectWare;
[weakref]paragraph_: P;
[weakref]page_: TSPage;
extra_style_id_: string;
range_array_: array of TSPdfBasicRange;
line_range_array_: array of TSPdfLineRange;
end;
function TSPdfParagraphRange.Create(docx_to_pdf: TSDocxToPdf; pg: TSPage; components: TSDocxComponentsWare; sect_ware: TSSectWare; paragraph: P);
begin
docx_to_pdf_ := docx_to_pdf;
page_ := pg;
docx_components_ware_ := components;
sect_ware_ := sect_ware;
paragraph_ := paragraph;
extra_style_id_ := "";
range_array_ := array();
line_range_array_ := array();
{self.}TSPage := page_;
end;
function TSPdfParagraphRange.Calc(): tableArray;
begin
{self.}SetPPr(paragraph_.PPr);
{self.}SetLvlText();
ppr := new PPrUnitDecorator(paragraph_.PPr);
{self.}ResetCoordinates(ppr);
{self.}EndX := {self.}StartX;
{self.}EndY := {self.}StartY;
{self.}CheckAndAddPage({self.}EndY, ppr.Spacing.Before);
{self.}EndY -= ppr.Spacing.Before;
{self.}DynamicHeight += ppr.Spacing.Before;
rs := paragraph_.Rs();
if length(rs) = 0 then
begin
line_space := {self.}GetParagraphLineSpace(ppr.RPr.Sz.Val, ppr.Spacing.Line);
{self.}DynamicHeight += ppr.Spacing.After + line_space;
{self.}EndY -= {self.}DynamicHeight;
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);
// hyperlinks := paragraph_.Hyperlinks(); // TOC
// for _,hyperlink in hyperlinks do
// begin
// rs := hyperlink.Rs();
// for _,r in rs do
// begin
// if r.FldChar.FldCharType = "begin" then break;
// {self.}RToTextRange(r, ppr);
// end
// end
end;
function TSPdfParagraphRange.Do();
begin
for _,line_range in line_range_array_ do
line_range.Do();
end;
function TSPdfParagraphRange.SetExtraStyleId(style_id: string);
begin
extra_style_id_ := style_id;
end;
function TSPdfParagraphRange.NewLineRange(): TSPdfLineRange;
begin
line_range := new TSPdfLineRange(page_);
line_range.StartX := {self.}EndX;
line_range.StartY := {self.}EndY;
line_range.Width := {self.}Width;
return line_range;
end;
function TSPdfParagraphRange.RangesToLines(ppr: PPr);
begin
line_range := {self.}NewLineRange();
i := 0;
max_size := 0;
max_y := 0;
while i <= length(range_array_)-1 do
begin
range := range_array_[i];
if i = 0 then {self.}EndX += ppr.Ind.FirstLine;
if range is class(TSPdfTextRange) and range.RPr.Sz.Val > max_size then
max_size := range.RPr.Sz.Val;
if range.DynamicHeight > max_y then max_y := range.DynamicHeight;
line_space := {self.}GetParagraphLineSpace(max_size, ppr.Spacing.Line);
diff := {self.}EndY - sect_ware_.SectPr.PgMar.Bottom;
if {self.}CheckAndAddPage({self.}EndY, max(line_space, range.DynamicHeight)) then
{self.}DynamicHeight += diff;
if_newline := {self.}EndX + range.Width - {self.}StartX > {self.}Width + 1e-6;
if if_newline and range.Width < {self.}Width then
begin
offset := line_space > max_y ? (line_space - max_size) / 2 + max_size - max_size / 5 : max_y;
max_value := max(line_space, max_y);
line_range.TSPage := page_;
line_range.EndY := {self.}EndY - max_value;
line_range.DynamicHeight := max_value;
line_range.SetAllRangeProp(pg: page_, ey: {self.}EndY - offset);
line_range_array_[length(line_range_array_)] := line_range;
line_range := {self.}NewLineRange();
max_size := 0;
max_y := 0;
{self.}DynamicHeight += max_value;
{self.}EndY -= max_value;
{self.}EndX := {self.}StartX;
// w:hanging
sz := range.RPr.SzCs.Val ? range.RPr.SzCs.Val : range.RPr.Sz.Val ? range.RPr.Sz.Val : docx_to_pdf_.Font.GetDefaultSz();
{self.}EndX -= ppr.Ind.HangingChars ? ppr.Ind.HangingChars * sz : ppr.Ind.Hanging;
continue;
end
range.EndX := {self.}EndX - range.StartX;
{self.}EndX += range.Width;
line_range.AddRange(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;
max_value := max(line_space, max_y) + ppr.Spacing.After;
line_range.TSPage := page_;
line_range.EndY := {self.}EndY - max_value;
line_range.DynamicHeight := max_value;
line_range.SetAllRangeProp(pg: page_, ey: {self.}EndY - offset);
line_range_array_[length(line_range_array_)] := line_range;
{self.}DynamicHeight += max_value;
{self.}EndY -= max_value;
end
end
{self.}SetLinesAlignment(ppr);
end;
function TSPdfParagraphRange.SetLinesAlignment(ppr: PPr);
begin
if length(line_range_array_) = 0 then return;
idx := length(line_range_array_)-1;
line_range := line_range_array_[idx];
line_range.Align(ppr.Jc.Val);
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();
{self.}EndY := 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();
// println("page_.PdfPage = {}, font_obj = {}, sz = {}", page_.PdfPage, font_obj, rpr.Sz.Val);
page_.PdfPage.SetFontAndSize(font_obj, rpr.Sz.Val);
word_width := page_.PdfPage.TextWidth(word);
text_range := new TSPdfTextRange();
text_range.RPr := rpr;
text_range.Text := word;
text_range.Font := font_obj;
text_range.Width := word_width;
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 := docx_components_ware_.GetDocumentRelsAdapter();
id := r.Drawing._Inline.Graphic.GraphicData.Pic.BlipFill.Blip.Embed;
rel := rels_adapter.Id(id);
image_path := "word/" + rel.Target;
image := docx_components_ware_.Zip().Get(image_path);
data := image.Data();
image_path := docx_to_pdf_.GetCachePath(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.StartX := xfrm.Off.X;
image_range.StartY := xfrm.Off.Y;
image_range.Width := xfrm.Ext.CX;
image_range.DynamicHeight := 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 := docx_components_ware_.GetStyles();
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 := docx_components_ware_.GetStylesAdapter();
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 := docx_components_ware_.GetStylesAdapter();
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 := docx_components_ware_.GetNumberingWare();
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: 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.}StartX += ppr.Ind.LeftChars ? ppr.Ind.LeftChars * sz : ppr.Ind.Left;
{self.}Width -= ppr.Ind.LeftChars ? ppr.Ind.LeftChars * sz : ppr.Ind.Left;
{self.}Width -= ppr.Ind.RightChars ? ppr.Ind.RightChars * sz : ppr.Ind.Right;
end;