PdfConverter/range/TSPdfParagraphRange.tsf

367 lines
12 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(TSPdfAbstractRange)
public
function Create(docx_to_pdf: TSDocxToPdf; page: PdfPage; 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(ppr: PPr);
function ResetCoordinates(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; page: PdfPage; components: Components; sect_ware: TSSectWare; paragraph: P);
begin
docx_to_pdf_ := docx_to_pdf;
page_ := page;
docx_components_ := Components;
sect_ware_ := sect_ware;
paragraph_ := paragraph;
extra_style_id_ := "";
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.ResetCoordinates(ppr);
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);
diff := point_.Y - sect_ware_.SectPr.PgMar.Bottom;
if self.CheckAndAddPage(point_.Y, max(line_space, range.H)) then
total_height += diff;
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;
// w:hanging
sz := range.RPr.SzCs.Val ? range.RPr.SzCs.Val : range.RPr.Sz.Val ? range.RPr.Sz.Val : docx_to_pdf_.Font.GetDefaultSz();
point_.X -= ppr.Ind.HangingChars ? ppr.Ind.HangingChars * sz : ppr.Ind.Hanging;
continue;
end
range.X := point_.X - range.X;
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) + ppr.Spacing.After;
total_height += max_value;
point_.Y -= max_value;
end
end
self.SetLinesAlignment(ppr);
if not self.H then self.H := total_height;
self.Y := point_.Y;
end;
function TSPdfParagraphRange.SetLinesAlignment(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 < 0 then return;
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_.GetNextPage(page_);
if ifnil(page_) then page_ := docx_to_pdf_.AddPage(sect_ware_);
point := docx_to_pdf_.GetCurrentPoint();
point_.Y := 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();
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_.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.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;
function TSPdfParagraphRange.ResetCoordinates(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.X += ppr.Ind.LeftChars ? ppr.Ind.LeftChars * sz : ppr.Ind.Left;
self.W -= ppr.Ind.LeftChars ? ppr.Ind.LeftChars * sz : ppr.Ind.Left;
self.W -= ppr.Ind.RightChars ? ppr.Ind.RightChars * sz : ppr.Ind.Right;
end;