report.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. package core
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "strconv"
  6. "strings"
  7. "github.com/tiechui1994/gopdf/util"
  8. )
  9. // 需要解决的问题: currY的控制权, 用户 -> 程序 -> 自动化操作
  10. // 页面的三部分: Header Page Footer
  11. const (
  12. Header = "Header"
  13. Footer = "Footer"
  14. Detail = "Detail"
  15. // flags
  16. Flag_AutoAddNewPage = "AutoAddNewPage"
  17. Flag_ResetPageNo = "ResetPageNo"
  18. )
  19. type pageMark struct {
  20. lineNo int
  21. pageNo int
  22. }
  23. type Executor func(report *Report)
  24. type CallBack func(report *Report)
  25. type Report struct {
  26. FisrtPageNeedHeader bool // 首页需要执行页眉
  27. FisrtPageNeedFooter bool // 首页需要执行页脚
  28. Vars map[string]string
  29. converter *Converter // 转换引擎(对接第三方库)
  30. config *Config // 当前PDF的页面配置
  31. currX, currY float64 // 当前位置
  32. executors map[string]*Executor // 执行器
  33. flags map[string]bool // 标记(自动分页和重置页号码)
  34. unit float64 // 转换单位
  35. pageNo int // 记录当前的 Page 的页数
  36. linew float64 // 线宽
  37. // 下面是页面的信息
  38. pageWidth, pageHeight float64
  39. contentWidth, contentHeight float64
  40. pageStartX, pageStartY float64
  41. pageEndX, pageEndY float64
  42. callbacks []CallBack // 回调函数,在PDF生成之后执行
  43. }
  44. func CreateReport() *Report {
  45. report := new(Report)
  46. report.converter = new(Converter)
  47. report.Vars = make(map[string]string)
  48. report.executors = make(map[string]*Executor)
  49. report.callbacks = make([]CallBack, 0)
  50. report.flags = make(map[string]bool)
  51. report.flags[Flag_AutoAddNewPage] = false
  52. report.flags[Flag_ResetPageNo] = false
  53. return report
  54. }
  55. func (report *Report) NoCompression() {
  56. report.converter.pdf.SetNoCompression()
  57. }
  58. /****************************************************************
  59. 压缩级别:
  60. -2 只使用哈夫曼压缩,
  61. -1 默认值, 压缩级别的6
  62. 0 不进行压缩,
  63. 1 最快的压缩, 但是压缩比率不是最好的
  64. 9 最大限度的压缩, 但是执行效率也是最慢的
  65. ****************************************************************/
  66. func (report *Report) CompressLevel(level int) {
  67. report.converter.pdf.SetCompressLevel(level)
  68. }
  69. // 写入PDF文件
  70. func (report *Report) Execute(filepath string) {
  71. if report.config == nil {
  72. panic("please set page config")
  73. }
  74. report.execute(true)
  75. report.converter.pdf.WritePdf(filepath)
  76. for i := range report.callbacks {
  77. report.callbacks[i](report)
  78. }
  79. }
  80. // 获取PDF内容
  81. func (report *Report) GetBytesPdf() (ret []byte) {
  82. if report.config == nil {
  83. panic("please set page config")
  84. }
  85. report.execute(true)
  86. ret = report.converter.pdf.GetBytesPdf()
  87. return
  88. }
  89. func (report *Report) LoadCellsFromText(filepath string) error {
  90. return report.converter.ReadFile(filepath)
  91. }
  92. // 转换, 内容 -> PDF文件
  93. func (report *Report) execute(exec bool) {
  94. if exec {
  95. report.ExecutePageHeader() // 首页的页眉
  96. report.pageNo = 1
  97. report.currX, report.currY = report.GetPageStartXY()
  98. report.addAtomicCell("v|PAGE|" + strconv.Itoa(report.pageNo))
  99. report.ExecuteDetail()
  100. report.pagination() // 分页
  101. report.ExecutePageFooter() // 最后一页的页脚
  102. }
  103. report.converter.Execute()
  104. }
  105. // 分页, 只有一个页面的PDF没有此操作
  106. func (report *Report) pagination() {
  107. lines := report.converter.GetAutomicCells()
  108. list := new(List)
  109. // 第一次遍历单元格, 确定需要创建的PDF页
  110. for i, line := range lines {
  111. if len(line) < 8 {
  112. continue
  113. }
  114. if line[0:7] == "v|PAGE|" {
  115. h := new(pageMark)
  116. h.lineNo = i
  117. h.pageNo = AtoiPanic(line[7:], line)
  118. list.Add(h)
  119. //fmt.Printf("hist %v \n", h)
  120. }
  121. }
  122. // 第二次遍历单元格, 检查 TotalPage
  123. for i, line := range lines {
  124. if strings.Index(line, "{#TotalPage#}") > -1 {
  125. total := report.getpageNoBylineNo(i, list)
  126. //fmt.Printf("total :%v\n", total)
  127. lines[i] = strings.Replace(lines[i], "{#TotalPage#}", strconv.Itoa(total), -1)
  128. }
  129. }
  130. cells := make([]string, 0)
  131. for _, line := range lines {
  132. cells = append(cells, line)
  133. }
  134. report.converter.SetAutomicCells(cells)
  135. }
  136. // 获取 lineNo 对应的 pageNo
  137. func (report *Report) getpageNoBylineNo(lineNo int, list *List) int {
  138. count := 0
  139. page := 0
  140. // 遍历到当前的lineNo, 当前的count记录的是list的索引
  141. for i, l := range list.GetAsArray() {
  142. if l.(*pageMark).lineNo >= lineNo {
  143. count = i
  144. break
  145. }
  146. }
  147. // 从新的页面开始, 得到页面号码
  148. for i := count; i < list.Size(); i++ {
  149. pageNo := list.Get(i).(*pageMark).pageNo // 当前item的页号
  150. if pageNo <= page {
  151. return page
  152. }
  153. page = pageNo
  154. //fmt.Printf("page :%v\n", page)
  155. }
  156. return page
  157. }
  158. // 设置可用字体
  159. func (report *Report) SetFonts(fmap []*FontMap) {
  160. report.converter.fonts = fmap
  161. }
  162. // 获取当前页面编号
  163. func (report *Report) GetCurrentPageNo() int {
  164. return report.pageNo
  165. }
  166. // 添加新的页面
  167. func (report *Report) AddNewPage(resetpageNo bool) {
  168. report.ExecutePageFooter()
  169. report.addAtomicCell("NP") // 构建新的页面
  170. if resetpageNo {
  171. report.pageNo = 1
  172. } else {
  173. report.pageNo++
  174. }
  175. report.addAtomicCell("v|PAGE|" + strconv.Itoa(report.pageNo))
  176. report.SetXY(report.GetPageStartXY())
  177. report.ExecutePageHeader()
  178. }
  179. func (report *Report) ExecutePageFooter() {
  180. if !report.FisrtPageNeedFooter {
  181. report.FisrtPageNeedFooter = true
  182. return
  183. }
  184. curX, curY := report.GetXY()
  185. report.currY = report.config.endY / report.unit
  186. report.currX = report.config.startX / report.unit
  187. h := report.executors[Footer]
  188. if h != nil {
  189. (*h)(report)
  190. }
  191. report.SetXY(curX, curY)
  192. }
  193. func (report *Report) ExecutePageHeader() {
  194. if !report.FisrtPageNeedHeader {
  195. report.FisrtPageNeedHeader = true
  196. return
  197. }
  198. curX, curY := report.GetXY()
  199. report.currY = 0
  200. report.currX = report.config.startX / report.unit
  201. h := report.executors[Header]
  202. if h != nil {
  203. (*h)(report)
  204. }
  205. report.SetXY(curX, curY)
  206. }
  207. func (report *Report) ExecuteDetail() {
  208. h := report.executors[Detail]
  209. if h != nil {
  210. if report.flags[Flag_AutoAddNewPage] {
  211. report.AddNewPage(report.flags[Flag_ResetPageNo])
  212. report.flags[Flag_AutoAddNewPage] = false
  213. report.flags[Flag_ResetPageNo] = false
  214. }
  215. (*h)(report)
  216. }
  217. }
  218. func (report *Report) RegisterExecutor(execuror Executor, name string) {
  219. report.executors[name] = &execuror
  220. }
  221. // 换页坐标
  222. func (report *Report) GetPageEndY() float64 {
  223. return report.pageEndY / report.unit
  224. }
  225. func (report *Report) GetPageEndX() float64 {
  226. return report.pageEndX / report.unit
  227. }
  228. // 页面开始坐标
  229. func (report *Report) GetPageStartXY() (x, y float64) {
  230. return report.pageStartX / report.unit, report.pageStartY / report.unit
  231. }
  232. func (report *Report) GetContentWidthAndHeight() (width, height float64) {
  233. return report.contentWidth / report.unit, report.contentHeight / report.unit
  234. }
  235. // currX, currY, 坐标
  236. func (report *Report) SetXY(currX, currY float64) {
  237. if currX > 0 {
  238. report.currX = currX
  239. }
  240. if currY > 0 {
  241. report.currY = currY
  242. }
  243. }
  244. func (report *Report) GetXY() (x, y float64) {
  245. return report.currX, report.currY
  246. }
  247. func (report *Report) SetMargin(dx, dy float64) {
  248. x, y := report.GetXY()
  249. report.SetXY(x+dx, y+dy)
  250. }
  251. // 设置页面的尺寸, unit: mm pt in size: A4 LTR, 目前支持常用的两种方式
  252. func (report *Report) SetPage(size string, unit string, orientation string) {
  253. report.setUnit(unit)
  254. config, ok := defaultConfigs[size]
  255. if !ok {
  256. panic("the config not exists, please add config")
  257. }
  258. switch size {
  259. case "A4":
  260. switch orientation {
  261. case "P":
  262. report.addAtomicCell("P|" + unit + "|A4|P")
  263. report.pageWidth = config.width / report.unit
  264. report.pageHeight = config.height / report.unit
  265. case "L":
  266. report.addAtomicCell("P|" + unit + "|A4|L")
  267. report.pageWidth = config.height / report.unit
  268. report.pageHeight = config.width / report.unit
  269. }
  270. case "LTR":
  271. switch orientation {
  272. case "P":
  273. report.pageWidth = config.width / report.unit
  274. report.pageHeight = config.height / report.unit
  275. report.addAtomicCell("P|" + unit + "|" + strconv.FormatFloat(report.pageWidth, 'f', 4, 64) +
  276. "|" + strconv.FormatFloat(report.pageHeight, 'f', 4, 64))
  277. case "L":
  278. report.pageWidth = config.height / report.unit
  279. report.pageHeight = config.width / report.unit
  280. report.addAtomicCell("P |" + unit + "|" + strconv.FormatFloat(report.pageWidth, 'f', 4, 64) +
  281. "|" + strconv.FormatFloat(report.pageHeight, 'f', 4, 64))
  282. }
  283. }
  284. report.contentWidth = config.contentWidth
  285. report.contentHeight = config.contentHeight
  286. report.pageStartX = config.startX
  287. report.pageStartY = config.startY
  288. report.pageEndX = config.endX
  289. report.pageEndY = config.endY
  290. report.config = config
  291. report.execute(false)
  292. }
  293. func (report *Report) setUnit(unit string) {
  294. switch unit {
  295. case "mm":
  296. report.unit = 2.834645669
  297. case "pt":
  298. report.unit = 1
  299. case "in":
  300. report.unit = 72
  301. default:
  302. panic("This unit is not specified :" + unit)
  303. }
  304. }
  305. func (report *Report) GetUnit() float64 {
  306. if report.unit == 0.0 {
  307. panic("does not set unit")
  308. }
  309. return report.unit
  310. }
  311. // 获取底层的所有的原子单元内容
  312. func (report *Report) GetAtomicCells() *[]string {
  313. cells := report.converter.GetAutomicCells()
  314. return &cells
  315. }
  316. // 保存原子操作单元
  317. func (report *Report) SaveAtomicCellText(filepath string) {
  318. cells := report.converter.GetAutomicCells()
  319. text := strings.Join(cells, "\n")
  320. ioutil.WriteFile(filepath, []byte(text), os.ModePerm)
  321. }
  322. // 计算文本宽度, 必须先调用 SetFontWithStyle() 或者 SetFont()
  323. func (report *Report) MeasureTextWidth(text string) float64 {
  324. w, err := report.converter.pdf.MeasureTextWidth(text)
  325. if err != nil {
  326. panic(err)
  327. }
  328. return w
  329. }
  330. // 设置当前文本字体, 先注册,后设置
  331. func (report *Report) SetFontWithStyle(family, style string, size int) {
  332. report.converter.pdf.SetFont(family, style, size)
  333. }
  334. func (report *Report) SetFont(family string, size int) {
  335. report.SetFontWithStyle(family, "", size)
  336. }
  337. func (report *Report) AddCallBack(callback CallBack) {
  338. report.callbacks = append(report.callbacks, callback)
  339. }
  340. /********************************************
  341. 将特定的字符串转换成底层可以识别的原子操作符
  342. *********************************************/
  343. func (report *Report) addAtomicCell(s string) {
  344. report.converter.AddAtomicCell(s)
  345. }
  346. // 注册当前字体
  347. func (report *Report) Font(fontName string, size int, style string) {
  348. report.addAtomicCell("F|" + fontName + "|" + style + "|" + strconv.Itoa(size))
  349. }
  350. // 写入字符串内容
  351. func (report *Report) Cell(x float64, y float64, content string) {
  352. report.addAtomicCell("CL|" + util.Ftoa(x) + "|" + util.Ftoa(y) + "|" + content)
  353. }
  354. func (report *Report) CellRight(x float64, y float64, w float64, content string) {
  355. report.addAtomicCell("CR|" + util.Ftoa(x) + "|" + util.Ftoa(y) + "|" +
  356. util.Ftoa(w) + "|" + content)
  357. }
  358. // 划线
  359. func (report *Report) LineType(ltype string, width float64) {
  360. report.linew = width
  361. report.addAtomicCell("LT|" + ltype + "|" + util.Ftoa(width))
  362. }
  363. func (report *Report) Line(x1 float64, y1 float64, x2 float64, y2 float64) {
  364. report.addAtomicCell("L|" + util.Ftoa(x1) + "|" + util.Ftoa(y1) + "|" + util.Ftoa(x2) +
  365. "|" + util.Ftoa(y2))
  366. }
  367. func (report *Report) LineH(x1 float64, y float64, x2 float64) {
  368. adj := report.linew * 0.5
  369. report.addAtomicCell("LH|" + util.Ftoa(x1) + "|" + util.Ftoa(y+adj) + "|" + util.Ftoa(x2))
  370. }
  371. func (report *Report) LineV(x float64, y1 float64, y2 float64) {
  372. adj := report.linew * 0.5
  373. report.addAtomicCell("LV|" + util.Ftoa(x+adj) + "|" + util.Ftoa(y1) + "|" + util.Ftoa(y2))
  374. }
  375. // 画特定的图形, 目前支持: 长方形, 椭圆两大类
  376. func (report *Report) Rect(x1 float64, y1 float64, x2 float64, y2 float64) {
  377. report.addAtomicCell("R|" + util.Ftoa(x1) + "|" + util.Ftoa(y1) + "|" + util.Ftoa(x2) +
  378. "|" + util.Ftoa(y2))
  379. }
  380. func (report *Report) Oval(x1 float64, y1 float64, x2 float64, y2 float64) {
  381. report.addAtomicCell("O|" + util.Ftoa(x1) + "|" + util.Ftoa(y1) + "|" + util.Ftoa(x2) +
  382. "|" + util.Ftoa(y2))
  383. }
  384. // 设置当前的字体颜色, 线条颜色
  385. func (report *Report) TextDefaultColor() {
  386. report.addAtomicCell("TC|" + strconv.Itoa(1) + "|" + strconv.Itoa(1) +
  387. "|" + strconv.Itoa(1))
  388. }
  389. func (report *Report) LineDefaultColor() {
  390. report.addAtomicCell("LC|" + strconv.Itoa(1) + "|" + strconv.Itoa(1) +
  391. "|" + strconv.Itoa(1))
  392. }
  393. func (report *Report) TextColor(red int, green int, blue int) {
  394. report.addAtomicCell("TC|" + strconv.Itoa(red) + "|" + strconv.Itoa(green) +
  395. "|" + strconv.Itoa(blue))
  396. }
  397. func (report *Report) LineColor(red int, green int, blue int) {
  398. report.addAtomicCell("LC|" + strconv.Itoa(red) + "|" + strconv.Itoa(green) +
  399. "|" + strconv.Itoa(blue))
  400. }
  401. // color: 背景颜色
  402. // line: 是否需要边框线条, "0000"不需要, "1111"需要, "0110" 是需要 TOP,RIGHT 线条
  403. func (report *Report) BackgroundColor(x, y, w, h float64, color string, line string) {
  404. red, green, blue := util.GetColorRGB(color)
  405. report.addAtomicCell("BC|" + util.Ftoa(x) + "|" + util.Ftoa(y) + "|" + util.Ftoa(w) + "|" +
  406. util.Ftoa(h) + "|" + strconv.Itoa(red) + "|" + strconv.Itoa(green) + "|" + strconv.Itoa(blue) + "|" + line)
  407. }
  408. func (report *Report) GrayColor(x, y float64, w, h float64, gray float64) {
  409. if gray < 0 || gray > 1 {
  410. gray = 0.85
  411. }
  412. report.LineType("straight", h)
  413. report.GrayStroke(gray)
  414. report.LineH(x, y, x+w)
  415. report.LineType("straight", 0.01)
  416. report.GrayStroke(0)
  417. }
  418. func (report *Report) GrayFill(grayScale float64) {
  419. report.addAtomicCell("GF|" + util.Ftoa(grayScale))
  420. }
  421. func (report *Report) GrayStroke(grayScale float64) {
  422. report.addAtomicCell("GS|" + util.Ftoa(grayScale))
  423. }
  424. // 图片
  425. func (report *Report) Image(path string, x1 float64, y1 float64, x2 float64, y2 float64) {
  426. report.addAtomicCell("I|" + path + "|" + util.Ftoa(x1) + "|" + util.Ftoa(y1) + "|" +
  427. util.Ftoa(x2) + "|" + util.Ftoa(y2))
  428. }
  429. // 添加变量
  430. func (report *Report) Var(name string, val string) {
  431. report.addAtomicCell("V|" + name + "|" + val)
  432. }