diff --git a/TSDocxToPdf.tsf b/TSDocxToPdf.tsf index 5b6e686..f5752bd 100644 --- a/TSDocxToPdf.tsf +++ b/TSDocxToPdf.tsf @@ -1,418 +1,255 @@ type TSDocxToPdf = class -uses TSColorToolKit, TSPdfEnumerations; - public - function Create(alias, file); - function SaveToFile(alias, file); - function Transform(); + function Create(alias: string; file: string); + function Destroy(); + function SaveToFile(alias: string; file: string): integer; + function Transform(); - property Font read ReadFont; - function ReadFont(); + function GetCurrentPage(): PdfPage; + function GetCurrentPoint(): Point; + function GetTempPath(image_path: string): string; + function GetPdf(): PdfFile; + function AddPage(sect_ware: TSSectWare): PdfPage; + + property Font read ReadFont; + function ReadFont(); private - function Init(); - function InitSectPr(); - function InitEncoder(); - function InitPoint(); - function InitStyles(); + function InitDocxComponents(alias: string; file: string); + function InitTempPath(file: string); + function InitPdfEncoder(); + function InitSectWare(); + function AllocateElementsToSectWare(); - function TransformParagraph(paragraph); - function TransformDrawing(drawing); - function TransformTable(table); + function ResetCoordinates(sect_ware: TSSectWare); + function TransformParagraph(sect_ware: TSSectWare; paragraph: P); + function TransformTable(sect_ware: TSSectWare; table: Tbl); - function AddPage(); - function CheckAndAddPage(offset); - function ResetCoordinates(); - function CalcPagragraphPt(size, line); - function FloatN(R, N); - function ParagraphWordsToLine(ware); - function GetElementType(element); - function SetPageItalic(page, x, y); // 模拟倾斜[废弃] - - // test用 - function PrintGrid(); + function PrintGrid(page: PdfPage; sect_ware: TSSectWare); // test private - docx_components_; // Components@DOCX - styles_adapter_; // StylesAdapter@Docx - - pdf_; // PdfFile - point_; // Point - sect_; - base_size_; // 基准字体大小 - font_ware_; - current_page_; // 当前page + pdf_: PdfFile; + docx_components_: Components; // Components@DOCX + temp_path_: string; // 临时目录,用来存放临时文件 + sect_ware_array_: array of TSSectWare; // 页面布局组件数组 + font_ware_: TSFontWare; // 字体部件 + current_page_: PdfPage; + page_array_: array of PdfPage; + point_: TSPoint; // 定位坐标点 end; -type Point = class // 定位当前的位置 -public - X; - Y; -end; - -function TSDocxToPdf.Create(alias, file); +function TSDocxToPdf.Create(alias: string; file: string); begin - NameSpace "DOCX"; - docx_components_ := new Components(); - [err, msg] := docx_components_.OpenFile(alias, file, nil); - if err then raise "Create obejct 'TSDocxFile' failed."; - self.Init(); + pdf_ := new PdfFile(); + self.InitPdfEncoder(); + self.InitDocxComponents(alias, file); + self.InitTempPath(file); + self.InitSectWare(); + font_ware_ := new TSFontWare(pdf_); + current_page_ := nil; + page_array_ := array(); + point_ := new TSPoint(); end; -function TSDocxToPdf.Init(); +function TSDocxToPdf.Destroy(); begin - pdf_ := new PdfFile(); - self.InitStyles(); - self.InitEncoder(); - self.InitSectPr(); - self.InitPoint(); - self.AddPage(); - base_size_ := integer(sect_.DocGrid.LinePitch / 1.38, 2); - font_ware_ := new TSFontWare(pdf_); + removeDir("", temp_path_); end; -function TSDocxToPdf.InitEncoder(); +function TSDocxToPdf.SaveToFile(alias: string; file: string): integer; begin - pdf_.UseCNSFonts(); - pdf_.UseCNSEncodings(); - // pdf_.UseUTFEncodings(); -end; - -function TSDocxToPdf.InitSectPr(); -begin - document := docx_components_.Document; - document.Deserialize(); - sect_ := document.Body.SectPr; - // 装饰器进行转换 - sect_ := new SectPrUnitDecorator(sect_); - sect_.PgSz.Orient := sect_.PgSz.Orient ? "portrait" : "landscape"; - - // println("LinePitch = {}, Type = {}", sect_.DocGrid.LinePitch, sect_.DocGrid.Type); - // println("Width = {}, Height = {}", sect_.PgSz.W, sect_.PgSz.H); - // println("Top = {}, Right = {}, Bottom = {}, Left = {}, Header = {}, Footer = {}\n", - // sect_.PgMar.Top, sect_.PgMar.Right, sect_.PgMar.Bottom, sect_.PgMar.Left, sect_.PgMar.Header, sect_.PgMar.Footer); -end; - -function TSDocxToPdf.InitPoint(); -begin - // 起始y的位置应为max(top, header) + 行距 - point_ := new Point(); - self.ResetCoordinates(); -end; - -function TSDocxToPdf.InitStyles(); -begin - styles := docx_components_.Styles; - styles.Deserialize(); - styles_adapter_ := new StylesAdapter(styles); -end; - -function TSDocxToPdf.AddPage(); -begin - current_page_ := pdf_.AddPage(); - current_page_.SetWidth(sect_.PgSz.W); - current_page_.SetHeight(sect_.PgSz.H); - self.PrintGrid(); -end; - -function TSDocxToPdf.ResetCoordinates(); -begin - point_.X := sect_.PgMar.Left; - point_.Y := sect_.PgSz.H - sect_.PgMar.Top; -end; - -///返回:err -function TSDocxToPdf.SaveToFile(alias, file); -begin - return pdf_.SaveToFile(alias, file); -end; - -function TSDocxToPdf.PrintGrid(); // test用 -begin - page := current_page_; - - i := 0; - while true do - begin - y := point_.Y - i * sect_.DocGrid.LinePitch; - if y <= sect_.PgMar.Bottom then break; - page.SetLineWidth(0.05); - page.SetGrayStroke(0.75); - page.MoveTo(sect_.PgMar.Left, y); - page.LineTo(sect_.PgSz.W- sect_.PgMar.Right, y); - page.Stroke(); - i++; - end - - x1 := sect_.PgMar.Left; - y1 := sect_.PgSz.H - sect_.PgMar.Top; - x2 := sect_.PgSz.W - sect_.PgMar.Right; - y2 := y1; - x3 := x1; - y3 := sect_.PgMar.Bottom; - x4 := x2; - y4 := y3; - page.SetLineWidth(0.05); - page.SetGrayStroke(0.5); - page.MoveTo(x1, y1); - page.LineTo(x2, y2); - page.Stroke(); - page.MoveTo(x1, y1); - page.LineTo(x3, y3); - page.Stroke(); - page.MoveTo(x2, y2); - page.LineTo(x4, y4); - page.Stroke(); - page.MoveTo(x3, y3); - page.LineTo(x4, y4); - page.Stroke(); - -end; - -function TSDocxToPdf.GetElementType(element); -begin - if element.LocalName = 'p' then return 1; - if element.LocalName = 'tbl' then return 3; - return 0; + return pdf_.SaveToFile(alias, file); end; function TSDocxToPdf.Transform(); begin - elements := docx_components_.Document.Body.Elements(); - for i:=0 to length(elements)-1 do - // for i:=0 to 2 do - begin - println("i = {}", i); - case self.GetElementType(elements[i]) of - 1: self.TransformParagraph(elements[i]); // 普通段落 - 2: self.TransformDrawing(elements[i]); // 图片段落 - 3: self.TransformTable(elements[i]); // 表格 - end; - end + prev := nil; + for _,sect_ware in sect_ware_array_ do + begin + if prev <> sect_ware then + begin + self.AddPage(sect_ware); + prev := sect_ware; + end + elements := sect_ware.Elements(); + for _,element in elements do + begin + if element.LocalName = "p" then self.TransformParagraph(sect_ware, element); + else if element.LocalName = "tbl" then self.TransformTable(sect_ware, element); + end + end end; -function TSDocxToPdf.CheckAndAddPage(offset); +function TSDocxToPdf.GetCurrentPage(): PdfPage; begin - offset := ifnil(offset) ? 0 : offset; - if point_.Y - offset <= sect_.PgMar.Bottom then - begin - self.AddPage(); - self.ResetCoordinates(); - self.PrintGrid(); - return true; - end - return false; + return current_page_; end; -function TSDocxToPdf.ParagraphWordsToLine(ware); +function TSDocxToPdf.GetCurrentPoint(): Point; begin - self.CheckAndAddPage(); - page := current_page_; - lines := array(); - x := point_.X + ware.Paragraph.PPr.Ind.FirstLine; - y := point_.Y; - max_size := 0; - - i := 0; - begin_index := 0; - words := ware.GetWords(); - while i <= length(words)-1 do - begin - [word, rpr] := words[i]; - if rpr.Sz.Val > max_size then - max_size := rpr.Sz.Val; - - font_obj := font_ware_.GetFont(rpr.RFonts.EastAsia, rpr.B, rpr.I); - lines[i]["page"] := page; - lines[i]["font"] := font_obj; - lines[i]["word"] := word; - lines[i]["rpr"] := rpr; - lines[i]["x"] := x; - page.SetFontAndSize(font_obj, rpr.Sz.Val); - w := page.TextWidth(word); - // println("word = {}, x = {}, w = {}, sz = {}", word, x, w, rpr.Sz.Val); - x += w; - - if ware.Paragraph.PPr.AutoSpaceDN and i < length(words)-1 then - begin - current_len := length(word); - next_len := length(words[i+1][0]); - if (current_len = 1 and next_len >= 2) or (current_len >= 2 and next_len = 1) then - begin - cord := current_len = 1 ? ord(word) : ord(words[i+1][0]); - if cord >= 48 and cord <= 57 then - x += rpr.Sz.Val * 0.27; - end - end - - if ware.Paragraph.PPr.AutoSpaceDE and i < length(words)-1 then - begin - current_len := length(word); - next_len := length(words[i+1][0]); - if (current_len = 1 and next_len >= 2) or (current_len >= 2 and next_len = 1) then - begin - cord := current_len = 1 ? ord(word) : ord(words[i+1][0]); - if (cord >= 97 and cord <= 122) or (cord >= 65 and cord <= 90) then - x += rpr.Sz.Val * 0.27; - end - end - - line_pt := self.CalcPagragraphPt(max_size, ware.Paragraph.PPr.Spacing.Line); - offset := (line_pt - max_size) / 2; - if self.CheckAndAddPage(line_pt) then // 换页,x不变,y变 - begin - page := current_page_; - x := point_.X; - y := point_.Y; - i := begin_index; - max_size := 0; - continue; - end - if x >= sect_.PgSz.W - sect_.PgMar.Right then // 换行 - begin - y := y - offset - max_size + max_size / 5; - for j:=begin_index to i-1 do - lines[j]["y"] := y; - - // 重置参数 - begin_index := i; - max_size := 0; - x := point_.X; - y := point_.Y - line_pt; - point_.Y := y; - - // 换页 - if self.CheckAndAddPage() then - begin - page := current_page_; - x := point_.X; - y := point_.Y; - end - end - else begin - i++; - if i > length(words)-1 then // 到了末尾仍未换行 - begin - y := y - offset - max_size + max_size / 5; - for j:=begin_index to length(words)-1 do - lines[j]["y"] := y; - point_.Y -= line_pt; - end - end - end - return lines; - + return point_; end; -function TSDocxToPdf.SetPageItalic(page, x, y); +function TSDocxToPdf.GetTempPath(image_path: string): string; begin - angle := 130; - rad := angle / 180 / Pi(); - page.SetTextMatrix(1, 0, tan(rad), 1, x, y); + return temp_path_ + extractFileName(image_path); end; -function TSDocxToPdf.TransformParagraph(paragraph); +function TSDocxToPdf.GetPdf(): PdfFile; begin - // 1. 字体大小 - // 2. 字体颜色 - // 3. 字体斜体 - // 4. 字体粗体 - // 5. 首行间距 - // 6. 字符间距 - - // 7. 下划线 - // 8. 删除线 - // 9. 上下标 - // 10. 对齐方式 - paragraph_ware := new TSParagraphWare(docx_components_, styles_adapter_, paragraph); - paragraph_ware.Do(); - - // 将段落中间件的每一个字符序列化成每一行 - lines := self.ParagraphWordsToLine(paragraph_ware); - // 开始写入pdf - for i:=0 to length(lines)-1 do - begin - line := lines[i]; - word := line["word"]; - rpr := line["rpr"]; - [r, g, b] := array(0, 0, 0); - if rpr.Color.Val then [r, g, b] := TSColorToolKit.HexToRGB(rpr.Color.Val); - page := line["page"]; - page.SetRGBFill(r / 255, g / 255, b / 255); - page.SetFontAndSize(line["font"], rpr.Sz.Val); - - x := line["x"]; - y := line["y"]; - if draw_bold then - begin - // 1. 调整偏移位置重复绘制 -- 当前参数的效果不太好 - // z := 0.0005 * sqrt(x*x + y*y) * sqrt(2) / 2; - // left_x := x - z; // 左下角x位置 - // left_y := y - z; // 左下角y位置 - // right_x := x + z; // 右上角x位置 - // right_y := y + z; // 右上角y位置 - // offset := word.WordProperty.Size / 1000; // 每次偏移千分之一 - // println("z = {}", z); - // println("x = {}, y = {}, offset = {}", x, y, offset); - // println("left_x = {}, left_y = {}, right_x = {}, left_y = {}\n", left_x, left_y, right_x, right_x); - // while left_x <= right_x and left_y <= right_y do - // begin - // if draw_italic then self.SetPageItalic(page, left_x, left_y); - // page.TextOut(left_x, left_y, word.Word); - // left_x += offset; - // left_y += right_y; - // end - - // 2. 调整字体大小,x,y位置重复绘制 - multi := 1.030; - size := word.WordProperty.Size; - target_size := size * multi; - offset := size * 0.01; - size /= multi; - while size <= target_size do - begin - page.SetFontAndSize(font, size); - page.beginText(); - if draw_italic then self.SetPageItalic(page, x, y); - page.TextOut(x, y, word.Word); - page.endText(); - size += offset; - end - end - else begin - page.beginText(); - page.TextOut(x, y, word); - page.endText(); - end - - page.SetRGBFill(0, 0, 0); - end - -end; - -function TSDocxToPdf.TransformDrawing(drawing); -begin -end; - -function TSDocxToPdf.TransformTable(table); -begin -end; - -function TSDocxToPdf.CalcPagragraphPt(size, line); -begin - if ifnil(line) then line := 12; - lines := self.FloatN(line / 12, 2); - multi := Ceil(size / base_size_); - return sect_.DocGrid.LinePitch * multi; -end; - -function TSDocxToPdf.FloatN(r, n); -begin - return Round(r * IntPower(10, n)) / IntPower(10, n); + return pdf_; end; function TSDocxToPdf.ReadFont(); begin - return font_ware_; + return font_ware_; +end; + +function TSDocxToPdf.InitPdfEncoder(); +begin + pdf_.UseCNSFonts(); + pdf_.UseCNSEncodings(); + // pdf_.UseUTFEncodings(); +end; + +function TSDocxToPdf.InitDocxComponents(alias: string; file: string); +begin + namespace "DOCX"; + docx_components_ := new Components(); + [err, msg] := docx_components_.OpenFile(alias, file, nil); + if err then raise "Create obejct 'TSDocxFile' failed."; +end; + +function TSDocxToPdf.InitTempPath(file: string); +begin + path := format("%s_%s", extractFileName(file), formatDatetime("YYYYMMDDHHNNSSZZZ", now())); + temp_path_ := format("%s/funcext/WordToPdf/temp/%s/", extractFileDir(sysExecName()), path); + createDir("", temp_path_); +end; + +function TSDocxToPdf.InitSectWare(); +begin + document := docx_components_.Document; + document.Deserialize(); + sect_ware_array_ := array(); + self.AllocateElementsToSectWare(); +end; + +function TSDocxToPdf.AllocateElementsToSectWare(); +begin + elements := docx_components_.Document.Body.Elements(); + ware := new TSSectWare(); + fp := function(ware, sect); + begin + sect := new SectPrUnitDecorator(sect); + sect.PgSz.Orient := sect.PgSz.Orient ? "portrait" : "landscape"; + ware.SectPr := sect; + ware.Do(); + end + for i:=0 to length(elements)-1 do + begin + element := elements[i]; + ware.Elements[length(ware.Elements)] := element; + if element.LocalName = "p" and ifObj(element.PPr.SectPr.XmlNode) then + begin + ##fp(ware, element.PPr.SectPr); + sect_ware_array_[length(sect_ware_array_)] := ware; + ware := new TSSectWare(); + end + else if element.LocalName = "sectPr" and i = length(elements)-1 then + begin + ##fp(ware, element); + sect_ware_array_[length(sect_ware_array_)] := ware; + end + end + println("sect_ware_array_ = {}", sect_ware_array_); +end; + +function TSDocxToPdf.AddPage(sect_ware: TSSectWare): PdfPage; +begin + current_page_ := pdf_.AddPage(); + current_page_.SetWidth(sect_ware.SectPr.PgSz.W); + current_page_.SetHeight(sect_ware.SectPr.PgSz.H); + self.ResetCoordinates(sect_ware); + page_array_[length(page_array_)] := current_page_; + +{$DEFINE WordToPdfTEST} +{$IFDEF WordToPdfTEST} + self.PrintGrid(current_page_, sect_ware); +{$ENDIF} + + return current_page_; +end; + +function TSDocxToPdf.ResetCoordinates(sect_ware: TSSectWare); +begin + point_.X := sect_ware.SectPr.PgMar.Left; + point_.Y := sect_ware.SectPr.PgSz.H - max(sect_ware.SectPr.PgMar.Top, sect_ware.SectPr.PgMar.Header); +end; + +function TSDocxToPdf.PrintGrid(page: PdfPage; sect_ware: TSSectWare); +begin + i := 0; + while true do + begin + y := point_.Y - i * sect_ware.SectPr.DocGrid.LinePitch; + if y <= sect_ware.SectPr.PgMar.Bottom then break; + page.SetLineWidth(0.05); + page.SetGrayStroke(0.75); + page.MoveTo(sect_ware.SectPr.PgMar.Left, y); + page.LineTo(sect_ware.SectPr.PgSz.W- sect_ware.SectPr.PgMar.Right, y); + page.Stroke(); + i++; + end + + x1 := sect_ware.SectPr.PgMar.Left; + y1 := sect_ware.SectPr.PgSz.H - sect_ware.SectPr.PgMar.Top; + x2 := sect_ware.SectPr.PgSz.W - sect_ware.SectPr.PgMar.Right; + y2 := y1; + x3 := x1; + y3 := sect_ware.SectPr.PgMar.Bottom; + x4 := x2; + y4 := y3; + page.SetLineWidth(0.05); + page.SetGrayStroke(0.5); + page.MoveTo(x1, y1); + page.LineTo(x2, y2); + page.Stroke(); + page.MoveTo(x1, y1); + page.LineTo(x3, y3); + page.Stroke(); + page.MoveTo(x2, y2); + page.LineTo(x4, y4); + page.Stroke(); + page.MoveTo(x3, y3); + page.LineTo(x4, y4); + page.Stroke(); +end; + +function TSDocxToPdf.TransformParagraph(sect_ware: TSSectWare; paragraph: P); +begin + w := sect_ware.SectPr.PgSz.W - sect_ware.SectPr.PgMar.Right - sect_ware.SectPr.PgMar.Left; + range := new TSPdfParagraphRange(self, docx_components_, sect_ware, paragraph); + range.X := point_.X; + range.Y := point_.Y; + range.W := w; + range.H := 0; + range.Calc(); + range.Do(); + point_.Y := range.Y; +end; + +function TSDocxToPdf.TransformTable(sect_ware: TSSectWare; table: Tbl); +begin + return; + w := sect_ware.SectPr.PgSz.W - sect_ware.SectPr.PgMar.Right - sect_ware.SectPr.PgMar.Left; + range := new TSPdfTableRange(self, docx_components_, sect_ware, table); + range.X := point_.X; + range.Y := point_.Y; + range.W := w; + range.H := 0; + range.Calc(); + range.Do(); + point_.Y := range.Y; end; diff --git a/range/TSPdfAbstractRange.tsf b/range/TSPdfAbstractRange.tsf new file mode 100644 index 0000000..164e365 --- /dev/null +++ b/range/TSPdfAbstractRange.tsf @@ -0,0 +1,10 @@ +type TSPdfAbstractRange = class +public + function Do();virtual; +public + X: real; + Y: real; // range的起始坐标(x,y) + W: real; // range的宽度 + H: real; // range的高度 + +end; diff --git a/range/TSPdfImageRange.tsf b/range/TSPdfImageRange.tsf new file mode 100644 index 0000000..3104964 --- /dev/null +++ b/range/TSPdfImageRange.tsf @@ -0,0 +1,26 @@ +type TSPdfImageRange = class(TSPdfAbstractRange) +public + function Do();override; +public + Page: PdfPage; + Image: PdfImage; +end; + +function TSPdfImageRange.Do(); +begin + println("image = {}, x = {}, y = {}, w = {}, h = {}", image, x, y, w, h); + self.Page.DrawImage(self.Image, self.X, self.Y, self.W, self.H); + +{$DEFINE WordToPdfTEST} +{$IFDEF WordToPdfTEST} + // Page.SetLineWidth(0.5); + // Page.SetGrayStroke(0.5); + // Page.MoveTo(0, self.Y); + // Page.LineTo(90, self.Y); + // Page.Stroke(); + self.Page.SetLineWidth(0.1); + self.Page.SetGrayStroke(0.5); + self.Page.Rectangle(0, Y, 900, H); + self.Page.Stroke(); +{$ENDIF} +end; diff --git a/range/TSPdfParagraphRange.tsf b/range/TSPdfParagraphRange.tsf new file mode 100644 index 0000000..729a4c1 --- /dev/null +++ b/range/TSPdfParagraphRange.tsf @@ -0,0 +1,353 @@ +type TSPdfParagraphRange = class(TSPdfAbstractRange) +public + function Create(docx_to_pdf: TSDocxToPdf; 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(lines_range_array: tableArray; ppr: 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; components: Components; sect_ware: TSSectWare; paragraph: P); +begin + docx_to_pdf_ := docx_to_pdf; + docx_components_ := Components; + sect_ware_ := sect_ware; + paragraph_ := paragraph; + extra_style_id_ := ""; + page_ := docx_to_pdf_.GetCurrentPage(); + 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.X += ppr.Ind.Left; + self.W := self.W - ppr.Ind.Left - ppr.Ind.Right; + 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); + if self.CheckAndAddPage(point_.Y, max(line_space, range.H)) then + total_height += point_.Y - sect_ware_.SectPr.PgMar.Bottom; + range.X := point_.X - range.X; + 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; + continue; + end + 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); + total_height += max_value; + point_.Y -= max_value; + end + end + // self.SetLinesAlignment(lines_range_array, ppr); + if not self.H then self.H := total_height; + self.Y := point_.Y; +end; + +function TSPdfParagraphRange.SetLinesAlignment(lines_range_array: tableArray; 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 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_.AddPage(sect_ware_); + point := docx_to_pdf_.GetCurrentPoint(); + point_.Y := point.Y; + page_array_ := array(page_); + 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, 0, 0); + 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_.GetTempPath(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; diff --git a/range/TSPdfRectRange.tsf b/range/TSPdfRectRange.tsf new file mode 100644 index 0000000..ce7c8ac --- /dev/null +++ b/range/TSPdfRectRange.tsf @@ -0,0 +1,13 @@ +type TSPdfRectRange = class(TSPdfAbstractRange) +public + function Do();override; +public + Page: PdfPage; +end; + +function TSPdfRectRange.Do();override; +begin + self.Page.SetRGBStroke(1.0, 0.0, 0.0); + self.Page.Rectangle(self.X, self.Y - self.H, self.W, self.H); + self.Page.Stroke(); +end; diff --git a/range/TSPdfTableRange.tsf b/range/TSPdfTableRange.tsf new file mode 100644 index 0000000..fb3e5ee --- /dev/null +++ b/range/TSPdfTableRange.tsf @@ -0,0 +1,177 @@ +type TSPdfTableRange = class(TSPdfAbstractRange) +public + function Create(docx_to_pdf: TSDocxToPdf; components: Components; sect_ware: TSSectWare; paragraph: P); + function Calc(): tableArray; + +private + 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 SetTblPr(tbl_pr: TblPr); + function SetTblPrByStyleId(var tbl_pr: TblPr; style_id: string); + function SetTrPr(var tr_pr: TrPr); + function SetTrPrByStyleId(var tr_pr: TrPr; style_id: string); + function SetTcPr(var tc_pr: TcPr); + function SetTcPrByStyleId(var tc_pr: TcPr; style_id: string); + +private + docx_to_pdf_: TSDocxToPdf; + docx_components_: Components; + sect_ware_: TSSectWare; + table_: Tbl; + range_array_: array of TSPdfAbstractRange; + page_: PdfPage; + point_: TSPoint; +end; + +function TSPdfTableRange.Create(docx_to_pdf: TSDocxToPdf; components: Components; sect_ware: TSSectWare; table: Tbl); +begin + docx_to_pdf_ := docx_to_pdf; + docx_components_ := Components; + sect_ware_ := sect_ware; + table_ := table; + page_ := docx_to_pdf_.GetCurrentPage(); + range_array_ := array(); + point_ := new TSPoint(); // 动态记录段落的坐标位置 +end; + +function TSPdfTableRange.Calc(): tableArray; +begin + self.SetTblPr(table_.TblPr); + tbl_pr := new TblPrUnitDecorator(table_.TblPr); + grid_cols := table_.TblGrid.GridCols(); + for i:=0 to length(grid_cols)-1 do + grid_cols[i] := new GridColUnitDecorator(grid_cols[i]); + self.ResetCoordinates(tbl_pr, grid_cols); + point_.X := self.X; + point_.Y := self.Y; + // 如果是根据内容自适应,应该计算并调整grid_cols的值 + trs := table_.Trs(); + matrix := array(); + for i,tr in trs do + matrix[i] := self.ProcessTrData(grid_cols, tr, tbl_pr); + return matrix; +end; + +function TSPdfTableRange.ProcessTrData(grid_cols: array of GridCol; tr: Tr; tbl_pr: TblPr): array of TSPdfAbstractRange; +begin + self.SetTrPr(tr.TrPr); + tr_pr := new TrPrUnitDecorator(tr.TrPr); + tcs := tr.Tcs(); + rows_range_array := array(); + for _,tc in tcs do + begin + elements := tc.Elements(); + rect_range := new TSPdfRectRange(); + rect_range.X := point_.X; + rect_range.Y := point_.Y; + rect_range.W := grid_col.W; + rect_range.H := 100; + rows_range_array[length(rows_range_array)] := rect_range; + for _,element in elements do + begin + range := nil; + if element.LocalName = "p" then + begin + range := new TSPdfParagraphRange(docx_to_pdf_, docx_components_, sect_ware_, element); + range.SetExtraStyleId(tbl_pr.TblStyle.Val); + end + else if element.LocalName = "tbl" then + begin + range := new TSPdfTableRange(docx_to_pdf_, docx_components_, sect_ware_, element); + end + if ifObj(range) then + begin + range.X := point_.X - tbl_pr.TblCellMar.Left.W; + range.Y := point_.Y - tbl_pr.TblCellMar.Top.W; + range.W := grid_col.W - tbl_pr.TblCellMar.Right.W; + range.H := nil; + ret := range.Calc(); + point_.Y := range.Y; + point_.X += grid_col.W; + rows_range_array[length(rows_range_array)] := range; + end + end + end + return rows_range_array; +end; + +function TSPdfTableRange.ResetCoordinates(tbl_pr: TblPr; grid_cols: array of GridCol); +begin + total_width := 0; + for _,grid_col in grid_cols do + total_width += grid_col.W; + diff := total_width - self.W; + case tbl_pr.jc.Val of + "center": + begin + offset := diff/2; + self.X -= offset; + end + "right": + begin + self.X -= diff; + end + end; + self.W := total_width; +end; + +function TSPdfTableRange.SetTblPr(var tbl_pr: TblPr); +begin + new_tbl_pr := new TblPr(); + tbl_pr.TblStyle.XmlNode.print; + self.SetTblPrByStyleId(new_tbl_pr, tbl_pr.TblStyle.Val); + new_tbl_pr.Copy(tbl_pr); + tbl_pr.Copy(new_tbl_pr); +end; + +function TSPdfTableRange.SetTblPrByStyleId(var tbl_pr: TblPr; 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.SetTblPrByStyleId(tbl_pr, based_on); + tbl_pr.Copy(style.TblPr); + end +end; + +function TSPdfTableRange.SetTrPr(var tr_pr: TrPr); +begin + new_tr_pr := new TrPr(); + self.SetTrPrByStyleId(new_tr_pr, table_.TblPr.TblStyle.Val); + new_tr_pr.Copy(tr_pr); + tr_pr.Copy(new_tr_pr); +end; + +function TSPdfTableRange.SetTrPrByStyleId(var tr_pr: TrPr; 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.SetTrPrByStyleId(tr_pr, based_on); + tr_pr.Copy(style.TrPr); + end +end; + +function TSPdfTableRange.SetTcPr(var tc_pr: TcPr); +begin + new_tc_pr := new TcPr(); + self.SetTcPrByStyleId(new_tc_pr, table_.TblPr.TblStyle.Val); + new_tc_pr.Copy(tc_pr); + tc_pr.Copy(new_tc_pr); +end; + +function TSPdfTableRange.SetTcPrByStyleId(var tc_pr: TcPr; 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.SetTcPrByStyleId(tc_pr, based_on); + tc_pr.Copy(style.TcPr); + end +end; diff --git a/range/TSPdfTextRange.tsf b/range/TSPdfTextRange.tsf new file mode 100644 index 0000000..4c4d79c --- /dev/null +++ b/range/TSPdfTextRange.tsf @@ -0,0 +1,33 @@ +type TSPdfTextRange = class(TSPdfAbstractRange) +uses TSColorToolKit; +public + function Do();override; +public + RPr: RPr; + Text: string; + Font: PdfFont; + Page: PdfPage; +end; + +function TSPdfTextRange.Do(); +begin + // println("text = {}, x = {}, y = {}, w = {}, sz = {}", ansiToUtf8(text), x, y, w); + [r, g, b] := array(0, 0, 0); + if self.RPr.Color.Val then [r, g, b] := TSColorToolKit.HexToRGB(self.RPr.Color.Val); + self.Page.SetRGBFill(r / 255, g / 255, b / 255); + self.Page.SetFontAndSize(self.Font, self.RPr.Sz.Val); + self.Page.BeginText(); + self.Page.TextOut(self.X, self.Y, self.Text); + self.Page.EndText(); + self.Page.SetRGBFill(0, 0, 0); + +{$DEFINE WordToPdfTEST} +{$IFDEF WordToPdfTEST} + self.Page.SetLineWidth(0.5); + self.Page.SetGrayStroke(0.5); + self.Page.MoveTo(0, self.Y); + self.Page.LineTo(90, self.Y); + self.Page.Stroke(); +{$ENDIF} +end; + diff --git a/utils/TSColorToolKit.tsf b/utils/TSColorToolKit.tsf index ade7143..65c2bd4 100644 --- a/utils/TSColorToolKit.tsf +++ b/utils/TSColorToolKit.tsf @@ -1,23 +1,23 @@ unit TSColorToolKit; interface - function HexToRGB(hex); + function HexToRGB(hex); implementation - function HexToRGB(hex); - begin - hex_string := ifnumber(hex) ? format("%x", hex) : hex; - if length(hex_string) = 7 then - begin - if hex_string[1] <> "#" then raise "Invalid hexadecimal parameter."; - hex_string := hex_string[1:]; - end - if length(hex_string) <> 6 then raise "Invalid hexadecimal parameter"; - r := eval(&"return 0x" + hex_string[1:2]); - g := eval(&"return 0x" + hex_string[3:4]); - b := eval(&"return 0x" + hex_string[5:6]); - return array(r, g, b); - end; + function HexToRGB(hex); + begin + hex_string := ifnumber(hex) ? format("%x", hex) : hex; + if length(hex_string) = 7 then + begin + if hex_string[1] <> "#" then raise "Invalid hexadecimal parameter."; + hex_string := hex_string[1:]; + end + if length(hex_string) <> 6 then raise "Invalid hexadecimal parameter"; + r := eval(&"return 0x" + hex_string[1:2]); + g := eval(&"return 0x" + hex_string[3:4]); + b := eval(&"return 0x" + hex_string[5:6]); + return array(r, g, b); + end; end. diff --git a/utils/TSFontWare.tsf b/utils/TSFontWare.tsf deleted file mode 100644 index 204ce32..0000000 --- a/utils/TSFontWare.tsf +++ /dev/null @@ -1,100 +0,0 @@ -type TSFontWare = class -public - function Create(pdf); - function Init(); - function UseBuiltInFont(); - function SetSubstitutionRules(source, target); - function GetFont(name, bold, italic); - -private - function GetExternalFont(name, bold, italic); - function GetBuiltInFont(name, bold, italic); - -private - pdf_; - is_linux_; // 是否是linux - - use_built_in_font_; // 是否使用内置字体 - substitution_rules_; // 替换规则 - external_reference_; - external_font_cache_; -end; - - -function TSFontWare.Create(pdf); -begin - pdf_ := pdf; - is_linux_ := true; - use_built_in_font_ := false; - substitution_rules_ := array("宋体": "SimSun", "黑体": "SimHei"); - external_reference_ := array(); - external_font_cache_ := array(); - // self.Init(); -end; - -function TSFontWare.UseBuiltInFont(); -begin - use_built_in_font_ := true; -end; - -function TSFontWare.Init(); -begin -{$IFDEF LINUX} - is_linux_ := true; -{$ELSE} - is_linux_ := false; -{$ENDIF} - separator := is_linux_ ? "/" : "\\"; - path := extractFileDir(sysExecName()) + separator + "funcext" + separator + "WordToPdf" + separator + "fonts" + separator; - files := fileList("", path + "*.tt*"); - for i:=0 to length(files)-1 do - begin - filename := files[i]["FileName"]; - ext := extractFileExt(filename); - pos := pos(ext, filename); - name := is_linux_ ? filename[:pos-1] : ansiToUTF8(filename[:pos-1]); - external_reference_[name] := array("ext": ext, "path": path + filename); - end -end; - -function TSFontWare.GetExternalFont(name, bold, italic); -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 ifnil(value) then raise name + " is unsupported font."; - 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 raise "Load font error : " + format("%x", font_name); - if not ifString(font_name) then return nil; - font := pdf_.GetFont(font_name, "UTF-8"); - external_font_cache_[name] := font; - return font; -end; - -function TSFontWare.GetFont(name, bold, italic); -begin - return use_built_in_font_ ? self.GetBuiltInFont(name, bold, italic) : self.GetExternalFont(name, bold, italic); -end; - -function TSFontWare.GetBuiltInFont(name, bold, italic); -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"; - font := pdf_.GetFont(font_name, "GBK-EUC-H"); - return font; -end; - -function TSFontWare.SetSubstitutionRules(source, target); -begin - substitution_rules_[source] := target; -end; diff --git a/utils/TSParagraphWare.tsf b/utils/TSParagraphWare.tsf deleted file mode 100644 index 0842d26..0000000 --- a/utils/TSParagraphWare.tsf +++ /dev/null @@ -1,108 +0,0 @@ -type TSParagraphWare = class -public - function Create(components, styles, paragraph); - function Do(); - function GetWords(); - - property Paragraph read ReadParagraph; - function ReadParagraph(); - -private - function SetPPr(ppr); - function SetRPr(rpr, ppr); - function SetRPrByStyleId(rpr, style_id); - function SetPPrByStyleId(ppr, style_id); - -private - docx_components_; - styles_; - paragraph_; - words_; -end; - -function TSParagraphWare.Create(components, styles, paragraph); -begin - docx_components_ := components; - styles_ := styles; - paragraph_ := paragraph; - words_ := array(); -end; - -function TSParagraphWare.Do(); -begin - self.SetPPr(paragraph_.PPr); // styleid与ppr样式合并 - rs := paragraph_.Rs(); - for i:=0 to length(rs)-1 do - begin - r := rs[i]; - self.SetRPr(r.RPr, paragraph_.PPr); // rpr样式与ppr与styleid样式合并 - rpr := new RPrUnitDecorator(r.RPr); - pos := 1; - text := r.T.Text; - while pos <= length(text) do - begin - c := text[pos]; - pos ++; - if ord(c) > 127 then - begin - c := text[pos-1 : pos+1]; - pos += 2; - end - words_[length(words_)] := array(utf8ToAnsi(c), rpr); - end - end - paragraph_.PPr := new PPrUnitDecorator(paragraph_.PPr); -end; - -function TSParagraphWare.SetPPrByStyleId(ppr, style_id); -begin - 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 TSParagraphWare.SetPPr(ppr); -begin - new_ppr := new PPr(); - style_id := ppr.PStyle.Val; - self.SetPPrByStyleId(new_ppr, style_id); - new_ppr.Copy(ppr); - ppr.Copy(new_ppr); -end; - -function TSParagraphWare.SetRPrByStyleId(rpr, style_id); -begin - 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 TSParagraphWare.SetRPr(rpr, 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 TSParagraphWare.GetWords(); -begin - return words_; -end; - -function TSParagraphWare.ReadParagraph(); -begin - return paragraph_; -end diff --git a/utils/TSPdfSingletonKit.tsf b/utils/TSPdfSingletonKit.tsf new file mode 100644 index 0000000..e0dfe7e --- /dev/null +++ b/utils/TSPdfSingletonKit.tsf @@ -0,0 +1,59 @@ +type TSPdfSingletonKit = class +public + function Create(); + class function GetComponent(components: Components; type: string); +public + hash_: array of tslobj; + +private + static singleton_: Singleton; +end; + +function TSPdfSingletonKit.Create(); +begin + hash_ := array(); +end; + +class function TSPdfSingletonKit.GetComponent(components: Components; type: string); +begin + if not ifObj(singleton_) then singleton_ := new TSPdfSingletonKit(); + if ifnil(singleton_.hash_[components]) then singleton_.hash_[components] := array(); + if ifObj(singleton_.hash_[components][type]) then return singleton_.hash_[components][type]; + case type of + "styles": + begin + styles := components.Styles; + styles.Deserialize(); + singleton_.hash_[components][type] := styles; + return styles; + end + "styles_adapter": + begin + styles := class(TSPdfSingletonKit).GetComponent(components, "styles"); + styles_adapter := new StylesAdapter(styles); + singleton_.hash_[components][type] := styles_adapter; + return styles_adapter; + end + "numbering_ware": + begin + numbering := components.Numbering; + numbering_ware := nil; + if ifObj(numbering) then + begin + numbering.Deserialize(); + numbering_ware := new TSNumberingWare(numbering); + end + singleton_.hash_[components][type] := numbering_ware; + return numbering_ware; + end + "document_rels_adapter": + begin + document_rels := components.DocumentRels; + document_rels.Deserialize(); + rels_adapter := new RelationShipsAdapter(document_rels); + singleton_.hash_[components][type] := rels_adapter; + return rels_adapter; + end + end; +end; + diff --git a/utils/TSPoint.tsf b/utils/TSPoint.tsf new file mode 100644 index 0000000..3cadb1c --- /dev/null +++ b/utils/TSPoint.tsf @@ -0,0 +1,10 @@ +type TSPoint = class + function Create(); + begin + self.X := 0; + self.Y := 0; + end + X: real; + Y: real; +end; + diff --git a/ware/TSFontWare.tsf b/ware/TSFontWare.tsf new file mode 100644 index 0000000..215eb8a --- /dev/null +++ b/ware/TSFontWare.tsf @@ -0,0 +1,88 @@ +type TSFontWare = class +public + function Create(pdf: PdfFile); + function GetFont(name: string; bold: boolean; italic: boolean); + function UseExternalFont(); + function SetSubstitutionRules(source: string; target: string); + function SetDefaultSz(value: real); + function GetDefaultSz(); + +private + function GetExternalFont(name:string; bold: boolean; italic: boolean); + function GetBuiltInFont(name:string; bold: boolean; italic: boolean); + +private + [weakref]pdf_: PdfFile; + + is_linux_: boolean; // 是否是linux + use_built_in_font_: boolean; // 是否使用内置字体 + substitution_rules_: array of string; // 替换规则 + external_reference_: array of string; + default_sz_: real; +end; + +function TSFontWare.Create(pdf: PdfFile); +begin + pdf_ := pdf; + use_built_in_font_ := true; + substitution_rules_ := array("宋体": "SimSun", "黑体": "SimHei"); + external_reference_ := array(); + default_sz_ := 10.5; +end; + +function TSFontWare.SetDefaultSz(value: real); +begin + default_sz_ := value; +end; + +function TSFontWare.GetDefaultSz(); +begin + return default_sz_; +end; + +function TSFontWare.UseExternalFont(); +begin + use_built_in_font_ := false; +end; + +function TSFontWare.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 ifnil(value) then raise name + " is unsupported font."; + 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 raise "Load font error : " + format("%x", font_name); + if not ifString(font_name) then return nil; + font := pdf_.GetFont(font_name, "UTF-8"); + external_font_cache_[name] := font; + return font; +end; + +function TSFontWare.GetFont(name: string; bold: boolean; italic: boolean); +begin + return use_built_in_font_ ? self.GetBuiltInFont(name, bold, italic) : self.GetExternalFont(name, bold, italic); +end; + +function TSFontWare.GetBuiltInFont(name: string; bold: boolean; italic: boolean); +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"; + font := pdf_.GetFont(font_name, "GBK-EUC-H"); + return font; +end; + +function TSFontWare.SetSubstitutionRules(source: string; target: string); +begin + substitution_rules_[source] := target; +end; diff --git a/ware/TSNumberingWare.tsf b/ware/TSNumberingWare.tsf new file mode 100644 index 0000000..14b9263 --- /dev/null +++ b/ware/TSNumberingWare.tsf @@ -0,0 +1,48 @@ +type TSNumberingWare = class +public + function Create(number: NumberingAdapter); + function GetNumberLvl(ppr: PPr); + +private + numbering_adapter_: NumberingAdapter; + num_hash_: array of tslobj; +end; + +function TSNumberingWare.Create(number: NumberingAdapter); +begin + numbering_adapter_ := new NumberingAdapter(number); + num_hash_ := array(); +end; + +function TSNumberingWare.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_.NumId(num_id); + abstract_id := num.AbstractNumId.Val; + abstract_num := numbering_adapter_.AbstractNumId(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 := i = ilvl_int ? num_hash_[num_id][i] + 1 : num_hash_[num_id][i]; + dest_str := format("%d", 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 diff --git a/ware/TSSectWare.tsf b/ware/TSSectWare.tsf new file mode 100644 index 0000000..85f089f --- /dev/null +++ b/ware/TSSectWare.tsf @@ -0,0 +1,27 @@ +type TSSectWare = class +public + function Create(); + function Do(); + +public + Elements: array of tslobj; + SectPr: SectPr; + BaseSize: integer; +end; + +function TSSectWare.Create(); +begin + self.Elements := array(); + self.SectPr := nil; + self.BaseSize := 0; +end; + +function TSSectWare.Do(); +begin + if ifObj(self.SectPr) then + self.BaseSize := round(self.SectPr.DocGrid.LinePitch * 0.75); + // println("LinePitch = {}, Type = {}", self.SectPr.DocGrid.LinePitch, self.SectPr.DocGrid.Type); + // println("Width = {}, Height = {}", self.SectPr.PgSz.W, self.SectPr.PgSz.H); + // println("Top = {}, Right = {}, Bottom = {}, Left = {}, Header = {}, Footer = {}\n", + // self.SectPr.PgMar.Top, self.SectPr.PgMar.Right, self.SectPr.PgMar.Bottom, self.SectPr.PgMar.Left, self.SectPr.PgMar.Header, self.SectPr.PgMar.Footer); +end;