PdfConverter/range/Advanced/TSPdfParagraphRange2.tsf

412 lines
15 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 TSPdfParagraphRange2 = 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);
function RangesToLines();overload;
function SetNumPages(num: integer);
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 CheckAndAddPage(y: real; offset: real): boolean;
function GetUtf8CharLength(byte: string): integer;
function RToTextRange(r: R; ppr: PPrUnitDecorator; link: string);
function SplitTextToTextRange(text: string; rpr: RPrUnitDecorator; link: string);
function RToDrawingRange(r: R; ppr: PPrUnitDecorator);
function SetLinesAlignment(ppr: PPrUnitDecorator);
function ResetCoordinates(ppr: PPrUnitDecorator);
function NewLineRange(): TSPdfLineRange;
function RangesToLines(ppr: PPrUnitDecorator);overload;
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;
hyperlink_array_: tableArray;
bookmark_array_: tableArray;
ppr_unit_decorator_: PPrUnitDecorator;
placeholder_array_: tableArray;
end;
function TSPdfParagraphRange2.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;
paragraph_ := paragraph;
sect_ware_ := sect_ware;
extra_style_id_ := "";
range_array_ := array();
line_range_array_ := array();
hyperlink_array_ := array();
bookmark_array_ := array();
placeholder_array_ := array();
{self.}TSPage := page_;
end;
function TSPdfParagraphRange2.Calc(): tableArray;
begin
// ppr.rpr是无效的应该以ppr.pStyle为准
{self.}SetPPr(paragraph_.PPr);
{self.}SetLvlText();
ppr_unit_decorator_ := new PPrUnitDecorator(paragraph_.PPr);
ppr := ppr_unit_decorator_;
{self.}ResetCoordinates(ppr);
{self.}EndX := {self.}StartX;
{self.}EndY := {self.}StartY;
{self.}EndY -= ppr.Spacing.Before;
{self.}DynamicHeight += ppr.Spacing.Before;
elements := paragraph_.Elements();
for _,element in elements do
begin
if not ifObj(element.XmlNode) then continue;
if element.LocalName = "r" then
begin
{self.}SetRPr(element.RPr, ppr);
if element.FldChar.FldCharType = "separate" then
fld := true;
else if element.FldChar.FldCharType = "end" then
fld := false;
else if element.InstrText.Text = "PAGE \\* Arabic \\* MERGEFORMAT" then
page_number := true;
else if element.InstrText.Text = " NUMPAGES " then
numpages := true;
else if fld and page_number then
begin
element.T.Text := tostring(page_.Number);
{self.}RToTextRange(element, ppr, nil);
page_number := false;
end
else if fld and numpages then
begin
rpr := new RPrUnitDecorator(element.RPr);
numpages_index := length(range_array_);
placeholder_array_ := array(numpages_index, rpr);
numpages := false;
end
else if not fld then {self.}RToTextRange(element, ppr, bookmark_name);
end
end
if placeholder_array_ then return false;
{self.}RangesToLines();
return true;
end;
function TSPdfParagraphRange2.SetNumPages(num: integer);
begin
text := tostring(num);
last_index := length(range_array_);
{self.}SplitTextToTextRange(text, placeholder_array_[1], link);
arr := array();
len := length(range_array_);
for i:=last_index to len-1 do
arr[length(arr)] := range_array_[i];
diff := len - last_index;
for i:=len-1 downto placeholder_array_[0] do
range_array_[i] := range_array_[i-diff];
k := 0;
for i:=placeholder_array_[0] to placeholder_array_[0]+diff-1 do
range_array_[i] := arr[k++];
end;
function TSPdfParagraphRange2.Do();override;
begin
for _,line_range in line_range_array_ do
line_range.Do();
end;
function TSPdfParagraphRange2.SetExtraStyleId(style_id: string);
begin
extra_style_id_ := style_id;
end;
function TSPdfParagraphRange2.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 TSPdfParagraphRange2.RangesToLines();overload;
begin
{self.}RangesToLines(ppr_unit_decorator_);
{self.}DynamicHeight += ppr_unit_decorator_.Spacing.After;
{self.}EndY -= ppr_unit_decorator_.Spacing.After;
if ppr_unit_decorator_.PBdr.Bottom.Val = "single" then
begin
page_.PdfPage.SetLineWidth(0.05);
page_.PdfPage.SetGrayStroke(0.25);
page_.PdfPage.MoveTo(sect_ware_.SectPr.PgMar.Left, {self.}EndY);
page_.PdfPage.LineTo(sect_ware_.SectPr.PgSz.W - sect_ware_.SectPr.PgMar.Right, {self.}EndY);
page_.PdfPage.Stroke();
end
end
function TSPdfParagraphRange2.RangesToLines(ppr: PPrUnitDecorator);overload;
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);
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);
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 TSPdfParagraphRange2.SetLinesAlignment(ppr: PPrUnitDecorator);
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 TSPdfParagraphRange2.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 TSPdfParagraphRange2.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 TSPdfParagraphRange2.RToTextRange(r: R; ppr: PPrUnitDecorator; link: string);
begin
if r.Anchor then
begin
{self.}HyperlinkToTextRange(r, ppr);
end
else begin
rpr := new RPrUnitDecorator(r.RPr);
text := r.T.Text;
if ifString(text) then {self.}SplitTextToTextRange(text, rpr, link);
end
end;
function TSPdfParagraphRange2.SplitTextToTextRange(text: string; rpr: RPrUnitDecorator; link: string);
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();
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 TSPdfParagraphRange2.RToDrawingRange(r: R; ppr: PPrUnitDecorator);
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.GetRelationshipById(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 TSPdfParagraphRange2.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 TSPdfParagraphRange2.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 TSPdfParagraphRange2.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 TSPdfParagraphRange2.SetPPrByStyleId(var ppr: PPr; style_id: string);
begin
styles := docx_components_ware_.GetStylesAdapter();
style := styles.GetStyleByStyleId(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 TSPdfParagraphRange2.SetRPr(var rpr; ppr: PPr);
begin
new_rpr := new RPr();
styles := docx_components_ware_.GetStyles();
new_rpr.Copy(styles.DocDefaults.RPrDefault.RPr);
{self.}SetRPrByStyleId(new_rpr, ppr.PStyle.Val);
{self.}SetRPrByStyleId(new_rpr, rpr.RStyle.Val);
new_rpr.Copy(rpr);
rpr.Copy(new_rpr);
end;
function TSPdfParagraphRange2.SetRPrByStyleId(var rpr: RPr; style_id: string);
begin
styles := docx_components_ware_.GetStylesAdapter();
style := styles.GetStyleByStyleId(style_id);
if ifObj(style) then
begin
based_on := style.BasedOn.Val;
{self.}SetRPrByStyleId(rpr, based_on);
rpr.Copy(style.RPr);
end
end;
function TSPdfParagraphRange2.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 TSPdfParagraphRange2.ResetCoordinates(ppr: PPrUnitDecorator);
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;