支持目录

This commit is contained in:
csh 2024-07-19 14:49:22 +08:00
parent cfc2d74ab8
commit d14de26d58
7 changed files with 237 additions and 73 deletions

View File

@ -10,6 +10,9 @@ public
function GetPdf(): PdfFile; function GetPdf(): PdfFile;
function GetNextPage(page: TSPage): TSPage; function GetNextPage(page: TSPage): TSPage;
function AddPage(sect_ware: TSSectWare): TSPage; function AddPage(sect_ware: TSSectWare): TSPage;
function AdjustPageNumber(page: TSPage; num: integer);
function LinkToToc(anchor: string; page: TSPage);
function AddToc(anchor: string; toc: TSToc);
property Font read ReadFont; property Font read ReadFont;
function ReadFont(); function ReadFont();
@ -37,6 +40,17 @@ private
current_page_: TSPage; current_page_: TSPage;
point_: TSPoint; // 定位坐标点 point_: TSPoint; // 定位坐标点
page_array_: array of TSPage; page_array_: array of TSPage;
toc_array_: tableArray;
end;
type TSPoint = class
function Create();
begin
{self.}X := 0;
{self.}Y := 0;
end
X: real;
Y: real;
end; end;
function TSDocxToPdf.Create(alias: string; file: string); function TSDocxToPdf.Create(alias: string; file: string);
@ -50,7 +64,7 @@ begin
current_page_ := nil; current_page_ := nil;
point_ := new TSPoint(); point_ := new TSPoint();
page_array_ := array(); page_array_ := array();
page_index_ := array(); toc_array_ := array();
end; end;
function TSDocxToPdf.Destroy(); function TSDocxToPdf.Destroy();
@ -76,8 +90,10 @@ begin
elements := sect_ware.Elements; elements := sect_ware.Elements;
for _,element in elements do for _,element in elements do
begin begin
// println("_ = {}", _);
if element.LocalName = "p" then {self.}TransformP(sect_ware, element); if element.LocalName = "p" then {self.}TransformP(sect_ware, element);
else if element.LocalName = "tbl" then {self.}TransformTbl(sect_ware, element); else if element.LocalName = "tbl" then {self.}TransformTbl(sect_ware, element);
else if element.LocalName = "sdt" then {self.}TransformSdt(sect_ware, element);
end end
end end
end; end;
@ -114,7 +130,7 @@ begin
namespace "DOCX"; namespace "DOCX";
docx_components_ := new TSDocxComponentsWare(); docx_components_ := new TSDocxComponentsWare();
[err, msg] := docx_components_.OpenFile(alias, file, nil); [err, msg] := docx_components_.OpenFile(alias, file, nil);
if err then raise "Create obejct 'TSDocxFile' failed."; if err then raise "Open file error.";
end; end;
function TSDocxToPdf.InitCachePath(file: string); function TSDocxToPdf.InitCachePath(file: string);
@ -172,9 +188,11 @@ begin
{self.}ResetCoordinates(sect_ware); {self.}ResetCoordinates(sect_ware);
len := length(page_array_); len := length(page_array_);
start := ifString(sect_ware.SectPr.PgNumType.Start) and trystrtoint(sect_ware.SectPr.PgNumType.Start, r) ? r : 0;
current_page_ := new TSPage(); current_page_ := new TSPage();
current_page_.Index := len; current_page_.Index := len;
current_page_.PdfPage := page; current_page_.PdfPage := page;
current_page_.Number := len = 0 ? start : page_array_[len-1].Number + 1;
page_array_[len] := current_page_; page_array_[len] := current_page_;
if sysparams["_PDF_PAGE_GRID_DEBUG_"] then if sysparams["_PDF_PAGE_GRID_DEBUG_"] then
@ -183,6 +201,12 @@ if sysparams["_PDF_PAGE_GRID_DEBUG_"] then
return current_page_; return current_page_;
end; end;
function TSDocxToPdf.AdjustPageNumber(page: TSPage; num: integer);
begin
for i:=page.Index to length(page_array_)-1 do
page_array_[i].Number := num++;
end;
function TSDocxToPdf.GetNextPage(page: TSPage); function TSDocxToPdf.GetNextPage(page: TSPage);
begin begin
return page_array_[page.Index + 1]; return page_array_[page.Index + 1];
@ -259,5 +283,23 @@ end;
function TSDocxToPdf.TransformSdt(sect_ware: TSSectWare; sdt: Sdt); function TSDocxToPdf.TransformSdt(sect_ware: TSSectWare; sdt: Sdt);
begin begin
w := sect_ware.SectPr.PgSz.W - sect_ware.SectPr.PgMar.Right - sect_ware.SectPr.PgMar.Left; ps := sdt.SdtContent.Ps();
for _,p in ps do
{self.}TransformP(sect_ware, p);
end;
function TSDocxToPdf.AddToc(anchor: string; toc: TSToc);
begin
if ifarray(toc_array_[anchor]) then toc_array_[anchor] union= array(toc);
else toc_array_[anchor] := array(toc);
end;
function TSDocxToPdf.LinkToToc(anchor: string; page: TSPage);
begin
arr := toc_array_[anchor];
if ifnil(arr) then return;
dst := page.PdfPage.CreateDestination();
for _,toc in arr do
toc.LinkAnnot(dst);
toc.AddPageNumber(page);
end; end;

View File

@ -13,15 +13,18 @@ private
function SetLvlText(); function SetLvlText();
function GetImageFileType(data: binary): string; function GetImageFileType(data: binary): string;
function GetParagraphLineSpace(size: real; line: integer): real; function GetParagraphLineSpace(size: real; line: integer): real;
function RangesToLines(ppr: PPr): tableArray; function RangesToLines(ppr: PPrUnitDecorator): tableArray;
function CheckAndAddPage(y: real; offset: real): boolean; function CheckAndAddPage(y: real; offset: real): boolean;
function GetUtf8CharLength(byte: string): integer; function GetUtf8CharLength(byte: string): integer;
function RToTextRange(r: R; ppr: PPr); function RToTextRange(r: R; ppr: PPrUnitDecorator; link: string);
function SplitTextToTextRange(text: string; rpr: RPr); function SplitTextToTextRange(text: string; rpr: RPrUnitDecorator; link: string);
function RToDrawingRange(r: R; ppr: PPr); function RToDrawingRange(r: R; ppr: PPrUnitDecorator);
function SetLinesAlignment(ppr: PPr); function SetLinesAlignment(ppr: PPrUnitDecorator);
function ResetCoordinates(ppr: PPr); function ResetCoordinates(ppr: PPrUnitDecorator);
function NewLineRange(): TSPdfLineRange; function NewLineRange(): TSPdfLineRange;
function BookMarkLinkToc();
function HyperlinkToToc(ppr: PPrUnitDecorator);
function HyperlinkToTextRange(hyperlink: Hyperlink; ppr: PPrUnitDecorator);
private private
[weakref]docx_to_pdf_: TSDocxToPdf; [weakref]docx_to_pdf_: TSDocxToPdf;
@ -32,6 +35,8 @@ private
extra_style_id_: string; extra_style_id_: string;
range_array_: array of TSPdfBasicRange; range_array_: array of TSPdfBasicRange;
line_range_array_: array of TSPdfLineRange; line_range_array_: array of TSPdfLineRange;
hyperlink_array_: tableArray;
bookmark_array_: tableArray;
end; end;
function TSPdfParagraphRange.Create(docx_to_pdf: TSDocxToPdf; pg: TSPage; components: TSDocxComponentsWare; sect_ware: TSSectWare; paragraph: P); function TSPdfParagraphRange.Create(docx_to_pdf: TSDocxToPdf; pg: TSPage; components: TSDocxComponentsWare; sect_ware: TSSectWare; paragraph: P);
@ -44,11 +49,14 @@ begin
extra_style_id_ := ""; extra_style_id_ := "";
range_array_ := array(); range_array_ := array();
line_range_array_ := array(); line_range_array_ := array();
hyperlink_array_ := array();
bookmark_array_ := array();
{self.}TSPage := page_; {self.}TSPage := page_;
end; end;
function TSPdfParagraphRange.Calc(): tableArray; function TSPdfParagraphRange.Calc(): tableArray;
begin begin
// ppr.rpr是无效的应该以ppr.pStyle为准
{self.}SetPPr(paragraph_.PPr); {self.}SetPPr(paragraph_.PPr);
{self.}SetLvlText(); {self.}SetLvlText();
ppr := new PPrUnitDecorator(paragraph_.PPr); ppr := new PPrUnitDecorator(paragraph_.PPr);
@ -58,37 +66,115 @@ begin
{self.}CheckAndAddPage({self.}EndY, ppr.Spacing.Before); {self.}CheckAndAddPage({self.}EndY, ppr.Spacing.Before);
{self.}EndY -= ppr.Spacing.Before; {self.}EndY -= ppr.Spacing.Before;
{self.}DynamicHeight += ppr.Spacing.Before; {self.}DynamicHeight += ppr.Spacing.Before;
rs := paragraph_.Rs();
if length(rs) = 0 then elements := paragraph_.Elements();
empty_flag := true;
bookmark_id := '';
bookmark_name := '';
bookmark_flag := false;
for _,element in elements do
begin
if not ifObj(element.XmlNode) then continue;
if element.LocalName = "r" then
begin
empty_flag := false;
{self.}SetRPr(element.RPr, ppr);
if element.Br.Type = "page" then
{self.}CheckAndAddPage(sect_ware_.SectPr.PgMar.Bottom, 1);
else if ifObj(element.Drawing.XmlNode) then {self.}RToDrawingRange(element, ppr);
else {self.}RToTextRange(element, ppr, bookmark_name);
end
else if element.LocalName = "hyperlink" then
begin
empty_flag := false;
{self.}HyperlinkToTextRange(element, ppr);
end
else if element.LocalName = "bookmarkStart" then
begin
bookmark_id := element.Id;
bookmark_name := element.Name;
bookmark_array_[bookmark_name] := array();
end
else if element.LocalName = "bookmarkEnd" then
begin
bookmark_id := '';
bookmark_name := '';
end
end
if empty_flag then
begin begin
line_space := {self.}GetParagraphLineSpace(ppr.RPr.Sz.Val, ppr.Spacing.Line); line_space := {self.}GetParagraphLineSpace(ppr.RPr.Sz.Val, ppr.Spacing.Line);
{self.}DynamicHeight += ppr.Spacing.After + line_space; {self.}DynamicHeight += ppr.Spacing.After + line_space;
{self.}EndY -= {self.}DynamicHeight; {self.}EndY -= {self.}DynamicHeight;
end 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); {self.}RangesToLines(ppr);
// hyperlinks := paragraph_.Hyperlinks(); // TOC if hyperlink_array_ then {self.}HyperlinkToToc(ppr);
// for _,hyperlink in hyperlinks do if bookmark_array_ then {self.}BookMarkLinkToc();
// begin
// rs := hyperlink.Rs();
// for _,r in rs do
// begin
// if r.FldChar.FldCharType = "begin" then break;
// {self.}RToTextRange(r, ppr);
// end
// end
end; end;
function TSPdfParagraphRange.Do(); function TSPdfParagraphRange.BookMarkLinkToc();
begin
for name,arr in bookmark_array_ do
if arr[0] then docx_to_pdf_.LinkToToc(name, arr[0].TSPage);
end;
function TSPdfParagraphRange.HyperlinkToToc(ppr: PPrUnitDecorator);
begin
for anchor,arr in hyperlink_array_ do // 整理hyperlink发送到docx_to_pdf_
begin
pg := arr[0].TSPage;
left := {self.}StartX;
bottom := arr[0].EndY;
right := left + {self.}Width;
top := bottom + arr[0].RPr.Sz.Val;
x := arr[0].EndX + arr[0].Width;
y := arr[0].EndY;
for _,range in arr do
begin
if range.TSPage <> pg then // 说明目录文字换页
begin
rect := array(left, bottom, right, top);
toc := new TSToc(ppr, rect, pg, x, y);
docx_to_pdf_.AddToc(anchor, toc);
pg := range.TSPage;
left := range.EndX;
top := range.EndY + range.RPr.Sz.Val;
right := left;
bottom := range.EndY;
continue;
end
top := range.EndY + arr[0].RPr.Sz.Val;
x := range.EndX + range.Width;
end
rect := array(left, bottom, right, top);
font_name := ppr.RPr.RFonts.EastAsia ? ppr.RPr.RFonts.EastAsia : ppr.RPr.RFonts.Ascii;
font_obj := docx_to_pdf_.Font.GetFont(font_name, ppr.RPr.B, ppr.RPr.I);
toc := new TSToc(ppr, rect, pg, x, y, font_obj);
docx_to_pdf_.AddToc(anchor, toc);
end
end;
function TSPdfParagraphRange.HyperlinkToTextRange(hyperlink: Hyperlink; ppr: PPrUnitDecorator);
begin
i := length(range_array_);
rs := hyperlink.Rs();
for _,r in rs do
begin
if r.FldChar.FldCharType = "begin" then break;
r.RPr := new RPr();
{self.}SetRPr(r.RPr, ppr);
{self.}RToTextRange(r, ppr, hyperlink.Anchor);
end
arr := array();
while i < length(range_array_) do
begin
arr[length(arr)] := range_array_[i];
i++;
end
hyperlink_array_[hyperlink.Anchor] := arr;
end;
function TSPdfParagraphRange.Do();override;
begin begin
for _,line_range in line_range_array_ do for _,line_range in line_range_array_ do
line_range.Do(); line_range.Do();
@ -108,7 +194,7 @@ begin
return line_range; return line_range;
end; end;
function TSPdfParagraphRange.RangesToLines(ppr: PPr); function TSPdfParagraphRange.RangesToLines(ppr: PPrUnitDecorator);
begin begin
line_range := {self.}NewLineRange(); line_range := {self.}NewLineRange();
i := 0; i := 0;
@ -167,7 +253,7 @@ begin
{self.}SetLinesAlignment(ppr); {self.}SetLinesAlignment(ppr);
end; end;
function TSPdfParagraphRange.SetLinesAlignment(ppr: PPr); function TSPdfParagraphRange.SetLinesAlignment(ppr: PPrUnitDecorator);
begin begin
if length(line_range_array_) = 0 then return; if length(line_range_array_) = 0 then return;
idx := length(line_range_array_)-1; idx := length(line_range_array_)-1;
@ -200,14 +286,20 @@ begin
return 4; return 4;
end; end;
function TSPdfParagraphRange.RToTextRange(r: R; ppr: PPr); function TSPdfParagraphRange.RToTextRange(r: R; ppr: PPrUnitDecorator; link: string);
begin begin
rpr := new RPrUnitDecorator(r.RPr); if r.Anchor then
text := r.T.Text; begin
if ifString(text) then {self.}SplitTextToTextRange(text, rpr); {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; end;
function TSPdfParagraphRange.SplitTextToTextRange(text: string; rpr: RPr); function TSPdfParagraphRange.SplitTextToTextRange(text: string; rpr: RPrUnitDecorator; link: string);
begin begin
pos := 1; pos := 1;
while pos <= length(text) do while pos <= length(text) do
@ -219,7 +311,6 @@ begin
font_name := rpr.RFonts.EastAsia ? rpr.RFonts.EastAsia : rpr.RFonts.Ascii; font_name := rpr.RFonts.EastAsia ? rpr.RFonts.EastAsia : rpr.RFonts.Ascii;
font_obj := docx_to_pdf_.Font.GetFont(font_name, rpr.B, rpr.I); 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(); 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); page_.PdfPage.SetFontAndSize(font_obj, rpr.Sz.Val);
word_width := page_.PdfPage.TextWidth(word); word_width := page_.PdfPage.TextWidth(word);
text_range := new TSPdfTextRange(); text_range := new TSPdfTextRange();
@ -228,10 +319,11 @@ begin
text_range.Font := font_obj; text_range.Font := font_obj;
text_range.Width := word_width; text_range.Width := word_width;
range_array_[length(range_array_)] := text_range; range_array_[length(range_array_)] := text_range;
if ifarray(bookmark_array_[link]) then bookmark_array_[link] union= array(text_range);
end end
end; end;
function TSPdfParagraphRange.RToDrawingRange(r: R; ppr: PPr); function TSPdfParagraphRange.RToDrawingRange(r: R; ppr: PPrUnitDecorator);
begin begin
xfrm := new XfrmUnitDecorator(r.Drawing._Inline.Graphic.GraphicData.Pic.SpPr.Xfrm); xfrm := new XfrmUnitDecorator(r.Drawing._Inline.Graphic.GraphicData.Pic.SpPr.Xfrm);
rels_adapter := docx_components_ware_.GetDocumentRelsAdapter(); rels_adapter := docx_components_ware_.GetDocumentRelsAdapter();
@ -318,11 +410,11 @@ end;
function TSPdfParagraphRange.SetRPr(var rpr; ppr: PPr); function TSPdfParagraphRange.SetRPr(var rpr; ppr: PPr);
begin begin
// rpr先继承ppr再继承样式最后继承自己
new_rpr := new RPr(); new_rpr := new RPr();
style_id := rpr.RStyle.Val; styles := docx_components_ware_.GetStyles();
new_rpr.Copy(ppr.RPr); new_rpr.Copy(styles.DocDefaults.RPrDefault.RPr);
{self.}SetRPrByStyleId(new_rpr, style_id); {self.}SetRPrByStyleId(new_rpr, ppr.PStyle.Val);
{self.}SetRPrByStyleId(new_rpr, rpr.RStyle.Val);
new_rpr.Copy(rpr); new_rpr.Copy(rpr);
rpr.Copy(new_rpr); rpr.Copy(new_rpr);
end; end;
@ -350,7 +442,7 @@ begin
{self.}SplitTextToTextRange(lvl_text, rpr); {self.}SplitTextToTextRange(lvl_text, rpr);
end; end;
function TSPdfParagraphRange.ResetCoordinates(ppr: PPr); function TSPdfParagraphRange.ResetCoordinates(ppr: PPrUnitDecorator);
begin begin
// 根据段落的间距确定新的坐标 // 根据段落的间距确定新的坐标
sz := ppr.RPr.SzCs.Val ? ppr.RPr.SzCs.Val : ppr.RPr.Sz.Val ? ppr.RPr.Sz.Val : docx_to_pdf_.Font.GetDefaultSz(); sz := ppr.RPr.SzCs.Val ? ppr.RPr.SzCs.Val : ppr.RPr.Sz.Val ? ppr.RPr.Sz.Val : docx_to_pdf_.Font.GetDefaultSz();

View File

@ -7,7 +7,6 @@ public
private private
function ProcessTrData(grid_cols: array of GridCol; tr: Tr; tbl_pr: TblPr): array of TSPdfAbstractRange; 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 ResetCoordinates(tbl_pr: TblPr; grid_cols: array of GridCol);
function CheckAndAddPage(y: real; offset: real): boolean;
function SetTblPr(tbl_pr: TblPr); function SetTblPr(tbl_pr: TblPr);
function SetTblPrByStyleId(var tbl_pr: TblPr; style_id: string); function SetTblPrByStyleId(var tbl_pr: TblPr; style_id: string);
function SetTrPr(var tr_pr: TrPr); function SetTrPr(var tr_pr: TrPr);
@ -22,7 +21,6 @@ private
[weakref]table_: Tbl; [weakref]table_: Tbl;
[weakref]page_: TSPage; [weakref]page_: TSPage;
[weakref]range_array_: tableArray; [weakref]range_array_: tableArray;
point_: TSPoint;
end; end;
function TSPdfTableRange.Create(docx_to_pdf: TSDocxToPdf; pg: PdfPage; components: Components; sect_ware: TSSectWare; table: Tbl); function TSPdfTableRange.Create(docx_to_pdf: TSDocxToPdf; pg: PdfPage; components: Components; sect_ware: TSSectWare; table: Tbl);
@ -60,19 +58,6 @@ begin
range.Do(); range.Do();
end; end;
function TSPdfTableRange.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 TSPdfTableRange.ProcessTrData(grid_cols: array of GridCol; tr: Tr; tbl_pr: TblPr): array of TSPdfAbstractRange; function TSPdfTableRange.ProcessTrData(grid_cols: array of GridCol; tr: Tr; tbl_pr: TblPr): array of TSPdfAbstractRange;
begin begin
{self.}SetTrPr(tr.TrPr); {self.}SetTrPr(tr.TrPr);

View File

@ -1,6 +1,9 @@
type TSPdfTocRange = class(TSPdfBasicRange) type TSPdfTocRange = class(TSPdfBasicRange)
public public
function Create(); function Create();
public
TSPage: TSPage;
end; end;
function TSPdfTocRange.Create(); function TSPdfTocRange.Create();

View File

@ -1,5 +1,6 @@
type TSPage = class type TSPage = class
public public
PdfPage; PdfPage: PdfPage;
Index; Index: integer;
Number: integer; // 页码
end; end;

View File

@ -1,10 +0,0 @@
type TSPoint = class
function Create();
begin
{self.}X := 0;
{self.}Y := 0;
end
X: real;
Y: real;
end;

51
utils/TSToc.tsf Normal file
View File

@ -0,0 +1,51 @@
type TSToc = class
uses TSPdfEnumerations;
public
function Create(ppr: PPrUnitDecorator; rect: array of real; page: TSPage; x: real; y: real; font: PdfFont);
function LinkAnnot(dst: PdfDestination);
function AddPageNumber(page: TSPage);
private
ppr_: PPrUnitDecorator;
rect_: array of real;
font_: PdfFont;
[weakref]tspage_: TSPage;
x_;
y_;
end;
function TSToc.Create(ppr: PPrUnitDecorator; rect: array of real; page: TSPage; x: real; y: real; font: PdfFont);
begin
ppr_ := ppr;
rect_ := rect;
tspage_ := page;
x_ := x;
y_ := y;
font_ := font;
end;
function TSToc.LinkAnnot(dst: PdfDestination);
begin
annot := tspage_.PdfPage.CreateLinkAnnot(rect_, dst);
annot.LinkAnnotSetHighlightMode(TSPdfEnumerations.ANNOT_NO_HIGHTLIGHT);
end;
function TSToc.AddPageNumber(page: TSPage);
begin
number := tostring(page.Number);
symbol := ".";
tspage_.PdfPage.SetFontAndSize(font_, ppr_.RPr.Sz.Val);
w := tspage_.PdfPage.TextWidth(symbol);
number_sz := tspage_.PdfPage.TextWidth(number);
x := rect_[2] - number_sz;
tspage_.PdfPage.BeginText();
tspage_.PdfPage.TextOut(x, y_, number);
tspage_.PdfPage.EndText();
while x_ < x - w do
begin
tspage_.PdfPage.BeginText();
tspage_.PdfPage.TextOut(x_, y_, symbol);
tspage_.PdfPage.EndText();
x_ += w;
end
end;