util.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*
  2. * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. package gofpdf
  17. import (
  18. "bufio"
  19. "bytes"
  20. "compress/zlib"
  21. "fmt"
  22. "io"
  23. "math"
  24. "os"
  25. "path/filepath"
  26. "strings"
  27. )
  28. func round(f float64) int {
  29. if f < 0 {
  30. return -int(math.Floor(-f + 0.5))
  31. }
  32. return int(math.Floor(f + 0.5))
  33. }
  34. func sprintf(fmtStr string, args ...interface{}) string {
  35. return fmt.Sprintf(fmtStr, args...)
  36. }
  37. // fileExist returns true if the specified normal file exists
  38. func fileExist(filename string) (ok bool) {
  39. info, err := os.Stat(filename)
  40. if err == nil {
  41. if ^os.ModePerm&info.Mode() == 0 {
  42. ok = true
  43. }
  44. }
  45. return ok
  46. }
  47. // fileSize returns the size of the specified file; ok will be false
  48. // if the file does not exist or is not an ordinary file
  49. func fileSize(filename string) (size int64, ok bool) {
  50. info, err := os.Stat(filename)
  51. ok = err == nil
  52. if ok {
  53. size = info.Size()
  54. }
  55. return
  56. }
  57. // bufferFromReader returns a new buffer populated with the contents of the specified Reader
  58. func bufferFromReader(r io.Reader) (b *bytes.Buffer, err error) {
  59. b = new(bytes.Buffer)
  60. _, err = b.ReadFrom(r)
  61. return
  62. }
  63. // slicesEqual returns true if the two specified float slices are equal
  64. func slicesEqual(a, b []float64) bool {
  65. if len(a) != len(b) {
  66. return false
  67. }
  68. for i := range a {
  69. if a[i] != b[i] {
  70. return false
  71. }
  72. }
  73. return true
  74. }
  75. // sliceCompress returns a zlib-compressed copy of the specified byte array
  76. func sliceCompress(data []byte) []byte {
  77. var buf bytes.Buffer
  78. cmp, _ := zlib.NewWriterLevel(&buf, zlib.BestSpeed)
  79. cmp.Write(data)
  80. cmp.Close()
  81. return buf.Bytes()
  82. }
  83. // sliceUncompress returns an uncompressed copy of the specified zlib-compressed byte array
  84. func sliceUncompress(data []byte) (outData []byte, err error) {
  85. inBuf := bytes.NewReader(data)
  86. r, err := zlib.NewReader(inBuf)
  87. defer r.Close()
  88. if err == nil {
  89. var outBuf bytes.Buffer
  90. _, err = outBuf.ReadFrom(r)
  91. if err == nil {
  92. outData = outBuf.Bytes()
  93. }
  94. }
  95. return
  96. }
  97. // utf8toutf16 converts UTF-8 to UTF-16BE; from http://www.fpdf.org/
  98. func utf8toutf16(s string, withBOM ...bool) string {
  99. bom := true
  100. if len(withBOM) > 0 {
  101. bom = withBOM[0]
  102. }
  103. res := make([]byte, 0, 8)
  104. if bom {
  105. res = append(res, 0xFE, 0xFF)
  106. }
  107. nb := len(s)
  108. i := 0
  109. for i < nb {
  110. c1 := byte(s[i])
  111. i++
  112. switch {
  113. case c1 >= 224:
  114. // 3-byte character
  115. c2 := byte(s[i])
  116. i++
  117. c3 := byte(s[i])
  118. i++
  119. res = append(res, ((c1&0x0F)<<4)+((c2&0x3C)>>2),
  120. ((c2&0x03)<<6)+(c3&0x3F))
  121. case c1 >= 192:
  122. // 2-byte character
  123. c2 := byte(s[i])
  124. i++
  125. res = append(res, ((c1 & 0x1C) >> 2),
  126. ((c1&0x03)<<6)+(c2&0x3F))
  127. default:
  128. // Single-byte character
  129. res = append(res, 0, c1)
  130. }
  131. }
  132. return string(res)
  133. }
  134. // intIf returns a if cnd is true, otherwise b
  135. func intIf(cnd bool, a, b int) int {
  136. if cnd {
  137. return a
  138. }
  139. return b
  140. }
  141. // strIf returns aStr if cnd is true, otherwise bStr
  142. func strIf(cnd bool, aStr, bStr string) string {
  143. if cnd {
  144. return aStr
  145. }
  146. return bStr
  147. }
  148. // doNothing returns the passed string with no translation.
  149. func doNothing(s string) string {
  150. return s
  151. }
  152. // Dump the internals of the specified values
  153. // func dump(fileStr string, a ...interface{}) {
  154. // fl, err := os.OpenFile(fileStr, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
  155. // if err == nil {
  156. // fmt.Fprintf(fl, "----------------\n")
  157. // spew.Fdump(fl, a...)
  158. // fl.Close()
  159. // }
  160. // }
  161. func repClosure(m map[rune]byte) func(string) string {
  162. var buf bytes.Buffer
  163. return func(str string) string {
  164. var ch byte
  165. var ok bool
  166. buf.Truncate(0)
  167. for _, r := range str {
  168. if r < 0x80 {
  169. ch = byte(r)
  170. } else {
  171. ch, ok = m[r]
  172. if !ok {
  173. ch = byte('.')
  174. }
  175. }
  176. buf.WriteByte(ch)
  177. }
  178. return buf.String()
  179. }
  180. }
  181. // UnicodeTranslator returns a function that can be used to translate, where
  182. // possible, utf-8 strings to a form that is compatible with the specified code
  183. // page. The returned function accepts a string and returns a string.
  184. //
  185. // r is a reader that should read a buffer made up of content lines that
  186. // pertain to the code page of interest. Each line is made up of three
  187. // whitespace separated fields. The first begins with "!" and is followed by
  188. // two hexadecimal digits that identify the glyph position in the code page of
  189. // interest. The second field begins with "U+" and is followed by the unicode
  190. // code point value. The third is the glyph name. A number of these code page
  191. // map files are packaged with the gfpdf library in the font directory.
  192. //
  193. // An error occurs only if a line is read that does not conform to the expected
  194. // format. In this case, the returned function is valid but does not perform
  195. // any rune translation.
  196. func UnicodeTranslator(r io.Reader) (f func(string) string, err error) {
  197. m := make(map[rune]byte)
  198. var uPos, cPos uint32
  199. var lineStr, nameStr string
  200. sc := bufio.NewScanner(r)
  201. for sc.Scan() {
  202. lineStr = sc.Text()
  203. lineStr = strings.TrimSpace(lineStr)
  204. if len(lineStr) > 0 {
  205. _, err = fmt.Sscanf(lineStr, "!%2X U+%4X %s", &cPos, &uPos, &nameStr)
  206. if err == nil {
  207. if cPos >= 0x80 {
  208. m[rune(uPos)] = byte(cPos)
  209. }
  210. }
  211. }
  212. }
  213. if err == nil {
  214. f = repClosure(m)
  215. } else {
  216. f = doNothing
  217. }
  218. return
  219. }
  220. // UnicodeTranslatorFromFile returns a function that can be used to translate,
  221. // where possible, utf-8 strings to a form that is compatible with the
  222. // specified code page. See UnicodeTranslator for more details.
  223. //
  224. // fileStr identifies a font descriptor file that maps glyph positions to names.
  225. //
  226. // If an error occurs reading the file, the returned function is valid but does
  227. // not perform any rune translation.
  228. func UnicodeTranslatorFromFile(fileStr string) (f func(string) string, err error) {
  229. var fl *os.File
  230. fl, err = os.Open(fileStr)
  231. if err == nil {
  232. f, err = UnicodeTranslator(fl)
  233. fl.Close()
  234. } else {
  235. f = doNothing
  236. }
  237. return
  238. }
  239. // UnicodeTranslatorFromDescriptor returns a function that can be used to
  240. // translate, where possible, utf-8 strings to a form that is compatible with
  241. // the specified code page. See UnicodeTranslator for more details.
  242. //
  243. // cpStr identifies a code page. A descriptor file in the font directory, set
  244. // with the fontDirStr argument in the call to New(), should have this name
  245. // plus the extension ".map". If cpStr is empty, it will be replaced with
  246. // "cp1252", the gofpdf code page default.
  247. //
  248. // If an error occurs reading the descriptor, the returned function is valid
  249. // but does not perform any rune translation.
  250. //
  251. // The CellFormat_codepage example demonstrates this method.
  252. func (f *Fpdf) UnicodeTranslatorFromDescriptor(cpStr string) (rep func(string) string) {
  253. var str string
  254. var ok bool
  255. if f.err == nil {
  256. if len(cpStr) == 0 {
  257. cpStr = "cp1252"
  258. }
  259. str, ok = embeddedMapList[cpStr]
  260. if ok {
  261. rep, f.err = UnicodeTranslator(strings.NewReader(str))
  262. } else {
  263. rep, f.err = UnicodeTranslatorFromFile(filepath.Join(f.fontpath, cpStr) + ".map")
  264. }
  265. } else {
  266. rep = doNothing
  267. }
  268. return
  269. }
  270. // Transform moves a point by given X, Y offset
  271. func (p *PointType) Transform(x, y float64) PointType {
  272. return PointType{p.X + x, p.Y + y}
  273. }
  274. // Orientation returns the orientation of a given size:
  275. // "P" for portrait, "L" for landscape
  276. func (s *SizeType) Orientation() string {
  277. if s == nil || s.Ht == s.Wd {
  278. return ""
  279. }
  280. if s.Wd > s.Ht {
  281. return "L"
  282. }
  283. return "P"
  284. }
  285. // ScaleBy expands a size by a certain factor
  286. func (s *SizeType) ScaleBy(factor float64) SizeType {
  287. return SizeType{s.Wd * factor, s.Ht * factor}
  288. }
  289. // ScaleToWidth adjusts the height of a size to match the given width
  290. func (s *SizeType) ScaleToWidth(width float64) SizeType {
  291. height := s.Ht * width / s.Wd
  292. return SizeType{width, height}
  293. }
  294. // ScaleToHeight adjusts the width of a size to match the given height
  295. func (s *SizeType) ScaleToHeight(height float64) SizeType {
  296. width := s.Wd * height / s.Ht
  297. return SizeType{width, height}
  298. }
  299. //The untypedKeyMap structure and its methods are copyrighted 2019 by Arteom Korotkiy (Gmail: arteomkorotkiy).
  300. //Imitation of untyped Map Array
  301. type untypedKeyMap struct {
  302. keySet []interface{}
  303. valueSet []int
  304. }
  305. //Get position of key=>value in PHP Array
  306. func (pa *untypedKeyMap) getIndex(key interface{}) int {
  307. if key != nil {
  308. for i, mKey := range pa.keySet {
  309. if mKey == key {
  310. return i
  311. }
  312. }
  313. return -1
  314. }
  315. return -1
  316. }
  317. //Put key=>value in PHP Array
  318. func (pa *untypedKeyMap) put(key interface{}, value int) {
  319. if key == nil {
  320. var i int
  321. for n := 0; ; n++ {
  322. i = pa.getIndex(n)
  323. if i < 0 {
  324. key = n
  325. break
  326. }
  327. }
  328. pa.keySet = append(pa.keySet, key)
  329. pa.valueSet = append(pa.valueSet, value)
  330. } else {
  331. i := pa.getIndex(key)
  332. if i < 0 {
  333. pa.keySet = append(pa.keySet, key)
  334. pa.valueSet = append(pa.valueSet, value)
  335. } else {
  336. pa.valueSet[i] = value
  337. }
  338. }
  339. }
  340. //Delete value in PHP Array
  341. func (pa *untypedKeyMap) delete(key interface{}) {
  342. if pa == nil || pa.keySet == nil || pa.valueSet == nil {
  343. return
  344. }
  345. i := pa.getIndex(key)
  346. if i >= 0 {
  347. if i == 0 {
  348. pa.keySet = pa.keySet[1:]
  349. pa.valueSet = pa.valueSet[1:]
  350. } else if i == len(pa.keySet)-1 {
  351. pa.keySet = pa.keySet[:len(pa.keySet)-1]
  352. pa.valueSet = pa.valueSet[:len(pa.valueSet)-1]
  353. } else {
  354. pa.keySet = append(pa.keySet[:i], pa.keySet[i+1:]...)
  355. pa.valueSet = append(pa.valueSet[:i], pa.valueSet[i+1:]...)
  356. }
  357. }
  358. }
  359. //Get value from PHP Array
  360. func (pa *untypedKeyMap) get(key interface{}) int {
  361. i := pa.getIndex(key)
  362. if i >= 0 {
  363. return pa.valueSet[i]
  364. }
  365. return 0
  366. }
  367. //Imitation of PHP function pop()
  368. func (pa *untypedKeyMap) pop() {
  369. pa.keySet = pa.keySet[:len(pa.keySet)-1]
  370. pa.valueSet = pa.valueSet[:len(pa.valueSet)-1]
  371. }
  372. //Imitation of PHP function array_merge()
  373. func arrayMerge(arr1, arr2 *untypedKeyMap) *untypedKeyMap {
  374. answer := untypedKeyMap{}
  375. if arr1 == nil && arr2 == nil {
  376. answer = untypedKeyMap{
  377. make([]interface{}, 0),
  378. make([]int, 0),
  379. }
  380. } else if arr2 == nil {
  381. answer.keySet = arr1.keySet[:]
  382. answer.valueSet = arr1.valueSet[:]
  383. } else if arr1 == nil {
  384. answer.keySet = arr2.keySet[:]
  385. answer.valueSet = arr2.valueSet[:]
  386. } else {
  387. answer.keySet = arr1.keySet[:]
  388. answer.valueSet = arr1.valueSet[:]
  389. for i := 0; i < len(arr2.keySet); i++ {
  390. if arr2.keySet[i] == "interval" {
  391. if arr1.getIndex("interval") < 0 {
  392. answer.put("interval", arr2.valueSet[i])
  393. }
  394. } else {
  395. answer.put(nil, arr2.valueSet[i])
  396. }
  397. }
  398. }
  399. return &answer
  400. }
  401. func remove(arr []int, key int) []int {
  402. n := 0
  403. for i, mKey := range arr {
  404. if mKey == key {
  405. n = i
  406. }
  407. }
  408. if n == 0 {
  409. return arr[1:]
  410. } else if n == len(arr)-1 {
  411. return arr[:len(arr)-1]
  412. }
  413. return append(arr[:n], arr[n+1:]...)
  414. }
  415. func isChinese(rune2 rune) bool {
  416. // chinese unicode: 4e00-9fa5
  417. if rune2 >= rune(0x4e00) && rune2 <= rune(0x9fa5) {
  418. return true
  419. }
  420. return false
  421. }
  422. // Condition font family string to PDF name compliance. See section 5.3 (Names)
  423. // in https://resources.infosecinstitute.com/pdf-file-format-basic-structure/
  424. func fontFamilyEscape(familyStr string) (escStr string) {
  425. escStr = strings.Replace(familyStr, " ", "#20", -1)
  426. // Additional replacements can take place here
  427. return
  428. }