converter.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. package core
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "strconv"
  6. "strings"
  7. "github.com/signintech/gopdf"
  8. )
  9. type FontMap struct {
  10. FontName string
  11. FileName string
  12. }
  13. // 对接 pdf
  14. type Converter struct {
  15. pdf *gopdf.GoPdf // 第三方转换
  16. atomicCells []string // 原子单元, 多个单元格最终汇总成PDF文件
  17. unit float64 // 单位像素
  18. fonts []*FontMap // 字体
  19. linew float64 // 线宽度(辅助)
  20. lastFont string // 最近字体(辅助)
  21. }
  22. // var convert.unit float64 = 2.834645669
  23. // 获取AtomicCells
  24. func (convert *Converter) GetAutomicCells() []string {
  25. cells := make([]string, len(convert.atomicCells))
  26. copy(cells, convert.atomicCells)
  27. return cells
  28. }
  29. // 设置AtomicCells(小心使用)
  30. func (convert *Converter) SetAutomicCells(cells []string) {
  31. convert.atomicCells = cells
  32. }
  33. // 添加AtomicCell
  34. func (convert *Converter) AddAtomicCell(cell string) {
  35. if strings.HasPrefix(cell, "F|") {
  36. if cell == convert.lastFont {
  37. return
  38. }
  39. convert.lastFont = cell
  40. }
  41. convert.atomicCells = append(convert.atomicCells, cell)
  42. }
  43. // 从保存的文件解析AtomicCell
  44. func (convert *Converter) ReadFile(fileName string) error {
  45. buf, err := ioutil.ReadFile(fileName)
  46. if err != nil {
  47. return err
  48. }
  49. text := strings.Replace(string(buf), "\r", "", -1)
  50. var UTF8_BOM = []byte{239, 187, 191}
  51. if text[0:3] == string(UTF8_BOM) {
  52. text = text[3:]
  53. }
  54. convert.atomicCells = strings.Split(text, "\n")
  55. return nil
  56. }
  57. // 将 Text(写入的, 而不是读入的) -> PDF文件
  58. func (convert *Converter) Execute() {
  59. lines := convert.atomicCells
  60. for _, line := range lines {
  61. elements := strings.Split(line, "|")
  62. switch elements[0] {
  63. case "P":
  64. convert.Page(line, elements) // PDF开始, P是标准的格式, P1属于自定义的格式
  65. case "NP":
  66. convert.NewPage(line, elements) // 新页面
  67. case "F":
  68. convert.Font(line, elements) // 字体
  69. case "TC":
  70. convert.TextColor(line, elements) // 颜色
  71. case "LC":
  72. convert.LineColor(line, elements) //
  73. case "BC":
  74. convert.BackgroundColor(line, elements) // 背景颜色
  75. case "GF", "GS":
  76. convert.Grey(line, elements)
  77. case "C", "CL", "CR":
  78. convert.Cell(line, elements) // 单元格
  79. case "L", "LV", "LH", "LT":
  80. convert.Line(line, elements) // 新行
  81. case "R":
  82. convert.Rect(line, elements) // 表
  83. case "O":
  84. convert.Oval(line, elements) //
  85. case "I":
  86. convert.Image(line, elements) // 图片
  87. case "M":
  88. convert.Margin(line, elements)
  89. default:
  90. if len(line) > 0 && line[0:1] != "v" {
  91. fmt.Println("skip:" + line + ":")
  92. }
  93. }
  94. }
  95. }
  96. // 添加字体
  97. func (convert *Converter) AddFont() {
  98. for _, font := range convert.fonts {
  99. err := convert.pdf.AddTTFFont(font.FontName, font.FileName)
  100. if err != nil {
  101. panic("font file:" + font.FileName + " not found")
  102. }
  103. }
  104. }
  105. // PDF文件页面的开始
  106. // [P, mm|pt|in, A4, P|L]
  107. // mm|pt|in 表示的尺寸单位, 毫米,像素,英尺
  108. // P|L 表示Portait, Landscape, 表示布局
  109. func (convert *Converter) Page(line string, elements []string) {
  110. convert.pdf = new(gopdf.GoPdf)
  111. CheckLength(line, elements, 4)
  112. switch elements[2] {
  113. /* A0 ~ A5 纸张像素表示
  114. 'A0': [2383.94, 3370.39],
  115. 'A1': [1683.78, 2383.94],
  116. 'A2': [1190.55, 1683.78],
  117. 'A3': [841.89, 1190.55],
  118. 'A4': [595.28, 841.89],
  119. 'A5': [419.53, 595.28],
  120. */
  121. case "A3":
  122. config := defaultConfigs["A3"]
  123. convert.setunit(elements[1])
  124. if elements[3] == "P" {
  125. convert.Start(config.width, config.height) // 像素
  126. } else if elements[3] == "L" {
  127. convert.Start(config.height, config.width)
  128. } else {
  129. panic("Page Orientation accept P or L")
  130. }
  131. case "A4":
  132. config := defaultConfigs["A4"]
  133. convert.setunit(elements[1])
  134. if elements[3] == "P" {
  135. convert.Start(config.width, config.height) // 像素
  136. } else if elements[3] == "L" {
  137. convert.Start(config.height, config.width)
  138. } else {
  139. panic("Page Orientation accept P or L")
  140. }
  141. case "LTR":
  142. config := defaultConfigs["LTR"]
  143. convert.setunit(elements[1])
  144. if elements[3] == "P" {
  145. convert.Start(config.width, config.height) // 像素
  146. } else if elements[3] == "L" {
  147. convert.Start(config.height, config.width)
  148. } else {
  149. panic("Page Orientation accept P or L")
  150. }
  151. default:
  152. panic("This size not supported yet:" + elements[2])
  153. }
  154. convert.AddFont()
  155. convert.pdf.AddPage()
  156. }
  157. // 单位转换率设置, 基准的像素Pt
  158. func (convert *Converter) setunit(unit string) {
  159. // 1mm ~ 2.8pt 1in ~ 72pt
  160. switch unit {
  161. case "mm":
  162. convert.unit = 2.834645669
  163. case "pt":
  164. convert.unit = 1
  165. case "in":
  166. convert.unit = 72
  167. default:
  168. panic("This unit is not specified :" + unit)
  169. }
  170. }
  171. // 构建新的页面
  172. func (convert *Converter) NewPage(line string, elements []string) {
  173. convert.pdf.AddPage()
  174. }
  175. // 设置PDF文件基本信息(单位,页面大小)
  176. func (convert *Converter) Start(w float64, h float64) {
  177. convert.pdf.Start(gopdf.Config{
  178. Unit: gopdf.Unit_PT,
  179. PageSize: gopdf.Rect{W: w, H: h},
  180. }) // 595.28, 841.89 = A4
  181. }
  182. // 设置当前文本使用的字体
  183. // ["", "family", "style", "size"]
  184. // style: "" or "U", ("B", "I")(需要字体本身支持)
  185. func (convert *Converter) Font(line string, elements []string) {
  186. CheckLength(line, elements, 4)
  187. err := convert.pdf.SetFont(elements[1], elements[2], AtoiPanic(elements[3], line))
  188. if err != nil {
  189. panic(err.Error() + " line;" + line)
  190. }
  191. }
  192. // 设置笔画的灰度 | 设置填充的灰度
  193. // ["GF|GS", grayScale]
  194. // grayScale: 0.0 到 1.0
  195. func (convert *Converter) Grey(line string, elements []string) {
  196. CheckLength(line, elements, 2)
  197. if elements[0] == "GF" {
  198. convert.pdf.SetGrayFill(ParseFloatPanic(elements[1], line))
  199. }
  200. if elements[0] == "GS" {
  201. convert.pdf.SetGrayStroke(ParseFloatPanic(elements[1], line))
  202. }
  203. }
  204. // 文本颜色
  205. // ["", R, G, B] // RGB文本颜色
  206. func (convert *Converter) TextColor(line string, elements []string) {
  207. CheckLength(line, elements, 4)
  208. convert.pdf.SetTextColor(uint8(AtoiPanic(elements[1], line)),
  209. uint8(AtoiPanic(elements[2], line)),
  210. uint8(AtoiPanic(elements[3], line)))
  211. }
  212. // 画笔颜色
  213. // ["", R, G, B]
  214. func (convert *Converter) LineColor(line string, elements []string) {
  215. CheckLength(line, elements, 4)
  216. convert.pdf.SetStrokeColor(uint8(AtoiPanic(elements[1], line)),
  217. uint8(AtoiPanic(elements[2], line)),
  218. uint8(AtoiPanic(elements[3], line)))
  219. }
  220. func (convert *Converter) BackgroundColor(line string, elements []string) {
  221. CheckLength(line, elements, 9)
  222. //convert.pdf.SetLineWidth(0) // 宽带最小
  223. convert.pdf.SetStrokeColor(255, 255, 255) // 白色线条
  224. convert.pdf.SetFillColor(uint8(AtoiPanic(elements[5], line)),
  225. uint8(AtoiPanic(elements[6], line)),
  226. uint8(AtoiPanic(elements[7], line))) // 设置填充颜色
  227. convert.pdf.RectFromUpperLeftWithStyle(ParseFloatPanic(elements[1], line)*convert.unit,
  228. ParseFloatPanic(elements[2], line)*convert.unit,
  229. ParseFloatPanic(elements[3], line)*convert.unit,
  230. ParseFloatPanic(elements[4], line)*convert.unit, "F")
  231. convert.pdf.SetFillColor(0, 0, 0) // 颜色恢复
  232. convert.pdf.SetStrokeColor(0, 0, 0)
  233. convert.pdf.SetLineType("solid")
  234. x := ParseFloatPanic(elements[1], line) * convert.unit
  235. y := ParseFloatPanic(elements[2], line) * convert.unit
  236. w := ParseFloatPanic(elements[3], line) * convert.unit
  237. h := ParseFloatPanic(elements[4], line) * convert.unit
  238. lines := elements[8] // LEFT,TOP,RIGHT,BOTTOM
  239. if lines[0] == '1' {
  240. convert.pdf.Line(x, y, x, y+h)
  241. }
  242. if lines[1] == '1' {
  243. convert.pdf.Line(x, y, x+w, y)
  244. }
  245. if lines[2] == '1' {
  246. convert.pdf.Line(x+w, y, x+w, y+h)
  247. }
  248. if lines[3] == '1' {
  249. convert.pdf.Line(x, y+h, x+w, y+h)
  250. }
  251. }
  252. // 椭圆
  253. // ["", x1, y1, x2, y2]
  254. func (convert *Converter) Oval(line string, elements []string) {
  255. CheckLength(line, elements, 5)
  256. convert.pdf.Oval(ParseFloatPanic(elements[1], line)*convert.unit,
  257. ParseFloatPanic(elements[2], line)*convert.unit,
  258. ParseFloatPanic(elements[3], line)*convert.unit,
  259. ParseFloatPanic(elements[4], line)*convert.unit)
  260. }
  261. // 长方形
  262. // ["R", x1, y1, x2, y2]
  263. func (convert *Converter) Rect(line string, eles []string) {
  264. CheckLength(line, eles, 5)
  265. adj := convert.linew * convert.unit * 0.5
  266. convert.pdf.Line(
  267. ParseFloatPanic(eles[1], line)*convert.unit,
  268. ParseFloatPanic(eles[2], line)*convert.unit+adj,
  269. ParseFloatPanic(eles[3], line)*convert.unit+adj*2,
  270. ParseFloatPanic(eles[2], line)*convert.unit+adj)
  271. convert.pdf.Line(
  272. ParseFloatPanic(eles[1], line)*convert.unit+adj,
  273. ParseFloatPanic(eles[2], line)*convert.unit,
  274. ParseFloatPanic(eles[1], line)*convert.unit+adj,
  275. ParseFloatPanic(eles[4], line)*convert.unit+adj*2)
  276. convert.pdf.Line(
  277. ParseFloatPanic(eles[1], line)*convert.unit,
  278. ParseFloatPanic(eles[4], line)*convert.unit+adj,
  279. ParseFloatPanic(eles[3], line)*convert.unit+adj*2,
  280. ParseFloatPanic(eles[4], line)*convert.unit+adj)
  281. convert.pdf.Line(
  282. ParseFloatPanic(eles[3], line)*convert.unit+adj,
  283. ParseFloatPanic(eles[2], line)*convert.unit,
  284. ParseFloatPanic(eles[3], line)*convert.unit+adj,
  285. ParseFloatPanic(eles[4], line)*convert.unit+adj*2)
  286. }
  287. // 图片
  288. // ["I", path, x, y, x1, y2]
  289. func (convert *Converter) Image(line string, elements []string) {
  290. CheckLength(line, elements, 6)
  291. r := new(gopdf.Rect)
  292. r.W = ParseFloatPanic(elements[4], line)*convert.unit - ParseFloatPanic(elements[2], line)*convert.unit
  293. r.H = ParseFloatPanic(elements[5], line)*convert.unit - ParseFloatPanic(elements[3], line)*convert.unit
  294. convert.pdf.Image(
  295. elements[1],
  296. ParseFloatPanic(elements[2], line)*convert.unit,
  297. ParseFloatPanic(elements[3], line)*convert.unit,
  298. r,
  299. )
  300. }
  301. // 线
  302. // ["L", x1, y1, x2, y2] 两点之间的线
  303. // ["LH", x1, y1, x2] 水平线
  304. // ["LV", x1, y2, y2] 垂直线
  305. // ["LT", "dashed|dotted|straight", w] 虚线,点,直线
  306. func (convert *Converter) Line(line string, elements []string) {
  307. switch elements[0] {
  308. case "L":
  309. CheckLength(line, elements, 5)
  310. convert.pdf.Line(
  311. ParseFloatPanic(elements[1], line)*convert.unit,
  312. ParseFloatPanic(elements[2], line)*convert.unit,
  313. ParseFloatPanic(elements[3], line)*convert.unit,
  314. ParseFloatPanic(elements[4], line)*convert.unit,
  315. )
  316. case "LH":
  317. CheckLength(line, elements, 4)
  318. convert.pdf.Line(
  319. ParseFloatPanic(elements[1], line)*convert.unit,
  320. ParseFloatPanic(elements[2], line)*convert.unit,
  321. ParseFloatPanic(elements[3], line)*convert.unit,
  322. ParseFloatPanic(elements[2], line)*convert.unit,
  323. )
  324. case "LV":
  325. CheckLength(line, elements, 4)
  326. convert.pdf.Line(
  327. ParseFloatPanic(elements[1], line)*convert.unit,
  328. ParseFloatPanic(elements[2], line)*convert.unit,
  329. ParseFloatPanic(elements[1], line)*convert.unit,
  330. ParseFloatPanic(elements[3], line)*convert.unit,
  331. )
  332. case "LT":
  333. CheckLength(line, elements, 3)
  334. lineType := elements[1]
  335. if lineType == "" {
  336. lineType = "straight"
  337. }
  338. convert.pdf.SetLineType(lineType)
  339. convert.linew = ParseFloatPanic(elements[2], line)
  340. convert.pdf.SetLineWidth(convert.linew * convert.unit)
  341. }
  342. }
  343. // 单元格
  344. // ["C", family, size, x, y, content] // 从(x,y) 位置开始写入content
  345. // ["CL", x, y, content] // 从(x,y) 位置开始写入content
  346. // ["CR", x, y, w, content] // 从右往左写入w长度的内容
  347. func (convert *Converter) Cell(line string, elements []string) {
  348. switch elements[0] {
  349. case "C":
  350. CheckLength(line, elements, 6)
  351. err := convert.pdf.SetFont(elements[1], "", AtoiPanic(elements[2], line))
  352. if err != nil {
  353. panic(err.Error() + " line;" + line)
  354. }
  355. convert.setPosition(elements[3], elements[4], line)
  356. convert.pdf.Cell(nil, elements[5])
  357. case "CL":
  358. CheckLength(line, elements, 4)
  359. convert.setPosition(elements[1], elements[2], line)
  360. convert.pdf.Cell(nil, elements[3])
  361. case "CR":
  362. CheckLength(line, elements, 5)
  363. tw, err := convert.pdf.MeasureTextWidth(elements[4])
  364. if err != nil {
  365. panic(err.Error() + " line;" + line)
  366. }
  367. x := ParseFloatPanic(elements[1], line) * convert.unit
  368. y := ParseFloatPanic(elements[2], line) * convert.unit
  369. w := ParseFloatPanic(elements[3], line) * convert.unit
  370. finalx := x + w - tw
  371. convert.pdf.SetX(finalx)
  372. convert.pdf.SetY(y)
  373. convert.pdf.Cell(nil, elements[4])
  374. }
  375. }
  376. func (convert *Converter) Margin(line string, eles []string) {
  377. CheckLength(line, eles, 3)
  378. top := ParseFloatPanic(eles[1], line)
  379. left := ParseFloatPanic(eles[2], line)
  380. if top != 0.0 {
  381. convert.pdf.SetTopMargin(top)
  382. }
  383. if left != 0.0 {
  384. convert.pdf.SetLeftMargin(left)
  385. }
  386. }
  387. func (convert *Converter) setPosition(x string, y string, line string) {
  388. convert.pdf.SetX(ParseFloatPanic(x, line) * convert.unit)
  389. convert.pdf.SetY(ParseFloatPanic(y, line) * convert.unit)
  390. }
  391. func CheckLength(line string, eles []string, no int) {
  392. if len(eles) < no {
  393. panic("Column short:" + line)
  394. }
  395. }
  396. func AtoiPanic(s string, line string) int {
  397. i, err := strconv.Atoi(s)
  398. if err != nil {
  399. panic(s + " not Integer :" + line)
  400. }
  401. return i
  402. }
  403. func ParseFloatPanic(num string, line string) float64 {
  404. if num == "" {
  405. return 0
  406. }
  407. f, err := strconv.ParseFloat(num, 64)
  408. if err != nil {
  409. panic(num + " not Numeric :" + line)
  410. }
  411. return f
  412. }