PdfConverter/internal/DTPModules.tsf

745 lines
21 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.

unit DTPModules;
interface
uses DTPAdvancedRanges, DocxMLAdapter, SharedMLAdapter;
type DocxComponentsModule = class(DocxComponents)
public
function Create();
function GetStyles(): Styles;
function GetStylesAdapter(): StylesAdapter;
function GetDocumentRelsAdapter(): RelationShipsAdapter;
function GetNumberingModule(): NumberingModule;
function GetTblStylePrByType(style_id: string; type: string): TblStylePr;
function GetFtr(target: string): Ftr;
function GetHdr(target: string): Hdr;
function GetFtrRelsAdapter(target: string): RelationShipsAdapter;
function GetHdrRelsAdapter(target: string): RelationShipsAdapter;
function GetFootnotesAdapter(): FootnotesAdapter;
private
styles_deserialize_flag_: boolean;
styles_adapter_: StylesAdapter;
document_rels_adapter_: RelationShipsAdapter;
numbering_module_: NumberingModule;
tbl_style_pr_hash_: array of TblStylePr;
ftr_hash_: array of Ftr;
hdr_hash_: array of Hdr;
hdr_rel_hash_: array of RelationShipsAdapter;
ftr_rel_hash_: array of RelationShipsAdapter;
footnotes_adapter_: FootnotesAdapter;
end;
type FontModule = class
public
function Create(pdf: PdfFile);
function GetAsciiFont(name: string; bold: boolean; italic: boolean): PdfFont;
function GetCNSFont(name: string; bold: boolean; italic: boolean): PdfFont;
function GetSymbolFont(): PdfFont;
function GetZapfDingbatsFont(): PdfFont;
function UseExternalFont();
function SetSubstitutionRules(source: string; target: string);
function SetDefaultSz(value: real);
function GetDefaultSz(): real;
private
function GetExternalFont(name:string; bold: boolean; italic: boolean): PdfFont;
function GetBuiltInCNSFont(name:string; bold: boolean; italic: boolean): PdfFont;
function GetBuiltInAsciiFont(name:string; bold: boolean; italic: boolean): PdfFont;
private
[weakref]pdf_: PdfFile;
use_built_in_font_: boolean; // 是否使用内置字体
substitution_rules_: array of string; // 替换规则
external_reference_: array of string;
default_sz_: real;
end;
type NoteModule = class
public
function Create(docx_to_pdf: TSDocxToPdf);
function AddFootnoteId(id: string);
function GetFootnoteOrderNumber(): string;
function GetFootnoteIndex(): string;
private
function CalculateNumber(num_fmt: string; num: integer): string;
function ChineseCountingThousand(n: integer): string;
private
[weakref]docx_to_pdf_: TSDocxToPdf;
footnote_hash_: hash;
footnote_index_: integer;
endnote_hash_: hash;
endnote_index_: integer;
end;
type NumberingModule = class
public
function Create(number: NumberingAdapter);
function GetNumberLvl(ppr: PPr);
private
function CalculateNumber(num_fmt: string; num: integer): string;
function ChineseCountingThousand(n: integer): string;
private
numbering_adapter_: NumberingAdapter;
num_hash_: hash;
end;
type SectModule = class
public
function Create();
function AddElement(element: tslobj);
public
SectPr: SectPr;
Elements: array of tslobj;
BaseSize: real;
end;
type TocModule = class
public
function Create();
function UpdateDocxNumPages();
function CalculateRect(anchor: string);
function LinkToToc(anchor: string; range: BasicRange);overload;
function LinkToToc(anchor: string);overload;
function AddToc(anchor: string; toc: Toc);overload;
function AddDocxPage(anchor: string; r: R);
private
toc_hash_: hash;
toc_unmatched_hash_: hash;
update_docx_num_pages_: boolean; // 开放给docx文件是否更新docx文件的页码
docx_page_hash_: hash;
end;
// 坐标
type Point = class
public
function Create();
public
X: real;
Y: real;
end;
// Page延迟建立存基本信息即可
type Page = class
public
function Create(pdf_file: PdfFile);
function OriginalTextCoordinates(): array of real;
function PrintGrid();
property PdfPage read ReadPdfPage;
function ReadPdfPage(): PdfPage;
public
Index: integer;
Number: integer;
SectPr: SectPr;
TextPoint: Point; // 正文坐标
FtrPoint: Point; // 页脚坐标
HdrPoint: Point; // 页眉坐标
UpperBound: real; // 上界
LowerBound: real; // 下界
private
[weakref]pdf_file_: PdfFile;
pdf_page_: PdfPage;
end;
type PageManagerModule = class
public
function Create(pdf_file: PdfFile);
function Operator[](index: uinteger): Page;
function NewPage(): Page;
function NextPage(page: Page): Page;
function Count(): integer;
private
[weakref]pdf_file_: PdfFile;
page_array_: array of Page;
end;
implementation
// DocxComponentsModule
function DocxComponentsModule.Create();
begin
class(DocxComponents).Create();
tbl_style_pr_hash_ := array();
ftr_hash_ := array();
hdr_hash_ := array();
hdr_rel_hash_ := array();
ftr_rel_hash_ := array();
end;
function DocxComponentsModule.GetStyles(): Styles;
begin
if styles_deserialize_flag_ then return {self.}Styles;
{self.}Styles.Deserialize();
{self.}Styles.ConvertToPoint();
styles_deserialize_flag_ := true;
return {self.}Styles;
end;
function DocxComponentsModule.GetStylesAdapter(): StylesAdapter;
begin
if styles_adapter_ then return styles_adapter_;
styles_adapter_ := new StylesAdapter({self.}GetStyles());
return styles_adapter_;
end;
function DocxComponentsModule.GetDocumentRelsAdapter(): RelationShipsAdapter;
begin
if document_rels_adapter_ then return document_rels_adapter_;
{self.}DocumentRels.Deserialize();
document_rels_adapter_ := new RelationShipsAdapter({self.}DocumentRels);
return document_rels_adapter_;
end;
function DocxComponentsModule.GetNumberingModule(): NumberingModule;
begin
if numbering_module_ then return numbering_module_;
if not {self.}Numbering then return nil;
{self.}Numbering.Deserialize();
{self.}Numbering.ConvertToPoint();
numbering_module_ := new NumberingModule({self.}Numbering);
return numbering_module_;
end;
function DocxComponentsModule.GetTblStylePrByType(style_id: string; type: string): TblStylePr;
begin
if tbl_style_pr_hash_[style_id][type] then return tbl_style_pr_hash_[style_id][type];
styles_adapter := {self.}GetStylesAdapter();
style := styles_adapter.GetStyleByStyleId(style_id);
style := new StyleAdapter(style);
tbl_style_pr := style.GetTblStylePrByType(type);
tbl_style_pr_hash_[style_id][type] := tbl_style_pr;
return tbl_style_pr;
end;
function DocxComponentsModule.GetFtr(target: string): Ftr;
begin
if ftr_hash_[target] then return ftr_hash_[target];
index := replaceStr(replaceStr(target, "footer", ""), ".xml", "");
obj := {self.}Footers(strtoint(index));
obj.Deserialize();
obj.ConvertToPoint();
ftr_hash_[target] := obj;
return obj;
end;
function DocxComponentsModule.GetHdr(target: string): Hdr;
begin
if hdr_hash_[target] then return hdr_hash_[target];
index := replaceStr(replaceStr(target, "header", ""), ".xml", "");
obj := {self.}Headers(strtoint(index));
obj.Deserialize();
obj.ConvertToPoint();
hdr_hash_[target] := obj;
return obj;
end;
function DocxComponentsModule.GetHdrRelsAdapter(target: string): RelationShipsAdapter;
begin
if hdr_rel_hash_[target] then return hdr_rel_hash_[target];
index := replaceStr(replaceStr(target, "header", ""), ".xml", "");
obj := {self.}HeaderRels(strtoint(index));
obj.Deserialize();
rels_adapter := new RelationShipsAdapter(obj);
hdr_rel_hash_[target] := rels_adapter;
return rels_adapter;
end;
function DocxComponentsModule.GetFtrRelsAdapter(target: string): RelationShipsAdapter;
begin
if ftr_rel_hash_[target] then return ftr_rel_hash_[target];
index := replaceStr(replaceStr(target, "footer", ""), ".xml", "");
obj := {self.}FooterRels(strtoint(index));
obj.Deserialize();
rels_adapter := new RelationShipsAdapter(obj);
ftr_rel_hash_[target] := rels_adapter;
return rels_adapter;
end;
function DocxComponentsModule.GetFootnotesAdapter(): FootnotesAdapter;
begin
if footnotes_adapter_ then return footnotes_adapter_;
obj := {self.}Footnotes;
obj.Deserialize();
obj.ConvertToPoint();
footnotes_adapter_ := new FootnotesAdapter(obj);
return footnotes_adapter_;
end;
// FontModule
function FontModule.Create(pdf: PdfFile);
begin
pdf_ := pdf;
use_built_in_font_ := true;
substitution_rules_ := array("宋体": "SimSun",
"黑体": "SimHei",
"Courier New": "Courier",
"Helvetica": "Helvetica",
"Times New Roman": "Times-Roman",
);
external_reference_ := array();
default_sz_ := 10.5;
end;
function FontModule.SetDefaultSz(value: real);
begin
default_sz_ := value;
end;
function FontModule.GetDefaultSz();
begin
return default_sz_;
end;
function FontModule.UseExternalFont();
begin
use_built_in_font_ := false;
end;
function FontModule.GetExternalFont(name:string; bold: boolean; italic: boolean);
begin
if ifnil(name) or name = '' then name := "等线";
if not ifnil(external_font_cache_[name]) then return external_font_cache_[name];
value := external_reference_[name];
if ifnil(value) then return nil;
if value["ext"] = ".ttf" then
font_name := pdf_.LoadTTFontFromFile("", value["path"], true);
else if value["ext"] = ".ttc" then
font_name := pdf_.LoadTTFontFromFile2("", value["path"], 0, true);
if not ifString(font_name) then return nil;
font := pdf_.GetFont(font_name, "UTF-8");
external_font_cache_[name] := font;
return font;
end;
function FontModule.GetCNSFont(name: string; bold: boolean; italic: boolean): PdfFont;
begin
return use_built_in_font_ ? {self.}GetBuiltInCNSFont(name, bold, italic) : {self.}GetExternalFont(name, bold, italic);
end;
function FontModule.GetBuiltInCNSFont(name: string; bold: boolean; italic: boolean): PdfFont;
begin
font_name := substitution_rules_[name];
if ifnil(font_name) then font_name := "SimSun";
if bold and italic then
font_name += ",BoldItalic";
else if bold then
font_name += ",Bold";
else if italic then
font_name += ",Italic";
return pdf_.GetFont(font_name, "GBK-EUC-H");
end;
function FontModule.GetAsciiFont(name: string; bold: boolean; italic: boolean): PdfFont;
begin
return use_built_in_font_ ? {self.}GetBuiltInAsciiFont(name, bold, italic) : {self.}GetExternalFont(name, bold, italic);
end;
function FontModule.GetBuiltInAsciiFont(name:string; bold: boolean; italic: boolean): PdfFont;
begin
font_name := substitution_rules_[name];
if ifnil(font_name) then font_name := "Times-Roman";
if font_name = "Courier" or font_name = "Helvetica" then
begin
if bold and italic then
font_name += "-BoldOblique";
else if bold then
font_name += "-Bold";
else if italic then
font_name += "-Oblique";
end
else if font_name = "Times-Roman" then
begin
if bold and italic then
font_name := "Times-BoldItalic";
else if bold then
font_name := "Times-Bold";
else if italic then
font_name := "Times-Italic";
end
else begin
font_name := "Courier";
end
return pdf_.GetFont(font_name, "");
end;
function FontModule.SetSubstitutionRules(source: string; target: string);
begin
substitution_rules_[source] := target;
end;
function FontModule.GetSymbolFont(): PdfFont;
begin
return pdf_.GetFont("Symbol", "");
end;
function FontModule.GetZapfDingbatsFont(): PdfFont;
begin
return pdf_.GetFont("ZapfDingbats", "");
end;
// NoteModule
function NoteModule.Create(docx_to_pdf: TSDocxToPdf);
begin
docx_to_pdf_ := docx_to_pdf;
footnote_hash_ := array();
endnote_hash_ := array();
footnote_index_ := 0;
endnote_index_ := 0;
end;
function NoteModule.AddFootnoteId(id: string);
begin
footnotes_adapter := docx_to_pdf_.DocxComponents.GetFootnotesAdapter();
footnote := footnotes_adapter.GetFootnoteById(id);
sect_pr := docx_to_pdf_.CurrentSect.SectPr;
w := sect_pr.SectPr.PgSz.W - sect_pr.SectPr.PgMar.Right - sect_pr.SectPr.PgMar.Left;
lb := 0;
arr := array();
elements := footnote.Elements();
for _,element in elements do
begin
if element.LocalName = "p" then
begin
range := new PRange(docx_to_pdf_, element);
range.Width := w;
range.LowerBound := lb;
range.Parent := self;
range.Calc();
arr[length(arr)] := range;
end
end
footnote_hash_[id] := arr;
end;
// function NoteModule.RunFootnote(id: string; x: real; y: real; pg: Page);
// begin
// sx := x;
// sy := y;
// spg := pg;
// height := 0;
// arr := array();
// hash := footnote_hash_[id];
// for id,range in hash do
// begin
// range.StartX := x;
// range.StartY := y;
// range.StartPage := spg;
// range.LowerBound := 0;
// range.Run();
// sy := range.EndY;
// height += range.DynamicHeight;
// arr union= range.PLineRanges();
// footnote_hash_[id] := arr;
// end
// return height;
// end;
function NoteModule.GetFootnoteOrderNumber();
begin
num_fmt := docx_to_pdf_.CurrentSect.SectPr.FootnotePr.NumFmt.Val;
if ifnil(num_fmt) then num_fmt := "decimal";
return CalculateNumber(num_fmt, ++footnote_index_);
end;
function NoteModule.GetFootnoteIndex(): string;
begin
return inttostr(footnote_index_);
end;
function NoteModule.CalculateNumber(num_fmt: string; num: integer): string;
begin
if num_fmt = "decimal" then
return format("%d", num);
else if num_fmt = "chineseCountingThousand" then
return {self.}ChineseCountingThousand(num);
else
return format("%d", num);
end;
function NoteModule.ChineseCountingThousand(n: integer): string;
begin
chinese_digits := array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九");
chinese_units := array("", "十", "百", "千");
if n < 10 then return chinese_digits[n];
result := "";
num_str := inttostr(n);
len := length(num_str);
for i:=1 to len do
begin
digit_int := strtoint(num_str[i]);
if digit_int <> 0 then
result += chinese_digits[digit_int] + chinese_units[len - i];
end
if length(result) >= 6 and result[:6] = "一十" then result := result[4:];
return result;
end;
// SectModule
function SectModule.Create();
begin
{self.}Elements := array();
{self.}SectPr := nil;
end;
function SectModule.AddElement(element: tslobj);
begin
{self.}Elements[length({self.}Elements)] := element;
end;
// NumberingModule
function NumberingModule.Create(number: NumberingAdapter);
begin
numbering_adapter_ := new NumberingAdapter(number);
num_hash_ := array();
end;
function NumberingModule.GetNumberLvl(ppr: PPr);
begin
num_id := ppr.NumPr.NumId.Val;
if ifnil(num_id) then return array("", nil);
ilvl := ppr.NumPr.Ilvl.Val;
pstyle := ppr.PStyle.Val;
num := numbering_adapter_.GetNumByNumId(num_id);
if ifnil(num) then return array("", nil);
abstract_id := num.AbstractNumId.Val;
abstract_num := numbering_adapter_.GetAbstractNumByAbstractNumId(abstract_id);
lvls := abstract_num.Lvls();
if ifnil(ilvl) then ilvl := "0";
for k,v in lvls do
begin
if ilvl and v.Ilvl = ilvl then
begin
lvl_text := v.LvlText.Val;
if not ifarray(num_hash_[num_id]) then num_hash_[num_id] := array(0, 0, 0, 0, 0, 0, 0, 0, 0);
ilvl_int := strtoint(ilvl);
for i:=0 to ilvl_int do
begin
source_str := "%" $ (i + 1);
n := num_hash_[num_id][i];
if i = ilvl_int then
begin
n := num_hash_[num_id][i] + 1;
for j:=i+1 to length(num_hash_[num_id])-1 do
num_hash_[num_id][j] := 0;
end
n := i = ilvl_int ? num_hash_[num_id][i] + 1 : num_hash_[num_id][i];
dest_str := {self.}CalculateNumber(v.NumFmt.Val, n);
lvl_text := replaceStr(lvl_text, source_str, dest_str);
end
num_hash_[num_id][ilvl_int]++;
if v.Suff.Val = "space" then lvl_text += " ";
return array(lvl_text, v);
end
end
return array("", nil);
end
function NumberingModule.CalculateNumber(num_fmt: string; num: integer): string;
begin
if num_fmt = "decimal" then
return format("%d", num);
else if num_fmt = "chineseCountingThousand" then
return {self.}ChineseCountingThousand(num);
else
return format("%d", num);
end;
function NumberingModule.ChineseCountingThousand(n: integer): string;
begin
chinese_digits := array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九");
chinese_units := array("", "十", "百", "千");
if n < 10 then return chinese_digits[n];
result := "";
num_str := inttostr(n);
len := length(num_str);
for i:=1 to len do
begin
digit_int := strtoint(num_str[i]);
if digit_int <> 0 then
result += chinese_digits[digit_int] + chinese_units[len - i];
end
if length(result) >= 6 and result[:6] = "一十" then result := result[4:];
return result;
end;
// Page
function Page.Create(pdf_file: PdfFile);
begin
pdf_file_ := pdf_file;
{self.}TextPoint := new Point();
{self.}FtrPoint := new Point();
{self.}HdrPoint := new Point();
end;
function Page.OriginalTextCoordinates(): array of real;
begin
x := {self.}SectPr.PgMar.Left;
y := min({self.}SectPr.PgSz.H - max({self.}SectPr.PgMar.Top, {self.}SectPr.PgMar.Header), {self.}HdrPoint.Y);
return array(x, y);
end;
function Page.ReadPdfPage(): PdfPage;
begin
if ifnil(pdf_page_) then
begin
pdf_page_ := pdf_file_.AddPage();
pdf_page_.SetWidth({self.}SectPr.PgSz.W);
pdf_page_.SetHeight({self.}SectPr.PgSz.H);
if sysparams["_PDF_PAGE_GRID_DEBUG_"] then
{self.}PrintGrid();
end
return pdf_page_;
end;
function Page.PrintGrid();
begin
i := 0;
while true do
begin
y := {self.}SectPr.PgSz.H - max({self.}SectPr.PgMar.Top, {self.}SectPr.PgMar.Header);
y := min(y, {self.}HdrPoint.Y);
y := y - i * {self.}SectPr.DocGrid.LinePitch;
if y <= {self.}SectPr.PgMar.Bottom then break;
pdf_page_.SetLineWidth(0.05);
pdf_page_.SetGrayStroke(0.75);
pdf_page_.MoveTo({self.}SectPr.PgMar.Left, y);
pdf_page_.LineTo({self.}SectPr.PgSz.W- {self.}SectPr.PgMar.Right, y);
pdf_page_.Stroke();
i++;
end
x1 := {self.}SectPr.PgMar.Left;
y1 := {self.}SectPr.PgSz.H - {self.}SectPr.PgMar.Top;
x2 := {self.}SectPr.PgSz.W - {self.}SectPr.PgMar.Right;
y2 := y1;
x3 := x1;
y3 := {self.}SectPr.PgMar.Bottom;
x4 := x2;
y4 := y3;
pdf_page_.SetLineWidth(0.05);
pdf_page_.SetGrayStroke(0.70);
pdf_page_.MoveTo(x1, y1);
pdf_page_.LineTo(x2, y2);
pdf_page_.Stroke();
pdf_page_.MoveTo(x1, y1);
pdf_page_.LineTo(x3, y3);
pdf_page_.Stroke();
pdf_page_.MoveTo(x2, y2);
pdf_page_.LineTo(x4, y4);
pdf_page_.Stroke();
pdf_page_.MoveTo(x3, y3);
pdf_page_.LineTo(x4, y4);
pdf_page_.Stroke();
end;
// PageManagerModule
function PageManagerModule.Create(pdf_file: PdfFile);
begin
pdf_file_ := pdf_file;
page_array_ := array();
end;
function Operator PageManagerModule.[](index: uinteger): Page;
begin
return page_array_[index];
end;
function PageManagerModule.NewPage(): Page;
begin
len := length(page_array_);
page := new Page(pdf_file_);
page.Index := len;
page_array_[len] := page;
return page;
end;
function PageManagerModule.Count(): integer;
begin
return length(page_array_);
end;
// TocModule
function TocModule.Create();
begin
toc_hash_ := array();
toc_unmatched_hash_ := array();
update_docx_num_pages_ := false;
docx_page_hash_ := array();
end;
function TocModule.UpdateDocxNumPages();
begin
update_docx_num_pages_ := true;
end;
function TocModule.CalculateRect(anchor: string);
begin
arr := toc_hash_[anchor];
for _,toc in arr do
toc.CalculateRect();
end;
function TocModule.LinkToToc(anchor: string; range: BasicRange);overload;
begin
arr := toc_hash_[anchor];
if ifnil(arr) then // 未匹配到链接,说明链接可能在后面
begin
toc_unmatched_hash_[anchor] := range;
return;
end
pg := range.EndPage;
dst := pg.PdfPage.CreateDestination();
dst.SetXYZ(range.EndX, range.EndY + range.RPr.Sz.Val, 1);
for _,toc in arr do
toc.LinkAnnot(dst);
toc.AddPageNumber(pg);
if update_docx_num_pages_ then
begin
r := docx_page_hash_[anchor];
r.T.Text := pg.Number;
r.Serialize();
end
end;
function TocModule.LinkToToc(anchor: string);overload;
begin
range := toc_unmatched_hash_[anchor];
if range then
begin
{self.}LinkToToc(anchor, range);
toc_unmatched_hash_[anchor] := nil;
end
end;
function TocModule.AddToc(anchor: string; toc: Toc);
begin
if ifarray(toc_hash_[anchor]) then toc_hash_[anchor] union= array(toc);
else toc_hash_[anchor] := array(toc);
end;
function TocModule.AddDocxPage(anchor: string; r: R);
begin
docx_page_hash_[anchor] := r;
end;
end.