github.com/signintech/pdft@v0.5.0/pdft.go (about) 1 package pdft 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "strconv" 12 "strings" 13 14 gopdf "github.com/signintech/pdft/minigopdf" 15 ) 16 17 // ErrAddSameFontName add same font name 18 var ErrAddSameFontName = errors.New("add same font name") 19 20 // ErrFontNameNotFound font name not found 21 var ErrFontNameNotFound = errors.New("font name not found") 22 23 // Left left 24 const Left = gopdf.Left //001000 25 // Top top 26 const Top = gopdf.Top //000100 27 // Right right 28 const Right = gopdf.Right //000010 29 // Bottom bottom 30 const Bottom = gopdf.Bottom //000001 31 // Center center 32 const Center = gopdf.Center //010000 33 // Middle middle 34 const Middle = gopdf.Middle //100000 35 36 // PDFt inject text to pdf 37 type PDFt struct { 38 pdf PDFData 39 fontDatas map[string]*PDFFontData 40 pdfImgs []*PDFImageData 41 pdfImgsMd5Map map[string]*PDFImageData 42 curr current 43 contenters []Contenter 44 pdfProtection *gopdf.PDFProtection 45 } 46 47 type current struct { 48 fontName string 49 fontStyle int 50 fontSize int 51 lineWidth float64 52 } 53 54 func pageHeight() float64 { 55 return 841.89 56 } 57 58 func (i *PDFt) protection() *gopdf.PDFProtection { 59 return i.pdfProtection 60 } 61 62 // ShowCellBorder show cell of border 63 func (i *PDFt) ShowCellBorder(isShow bool) { 64 var clw ContentLineStyle 65 if isShow { 66 clw.width = 0.1 67 clw.lineType = "dotted" 68 i.curr.lineWidth = 0.1 69 } else { 70 clw.width = 0.0 71 clw.lineType = "" 72 i.curr.lineWidth = 0.0 73 } 74 i.contenters = append(i.contenters, &clw) 75 } 76 77 // Open open pdf file 78 func (i *PDFt) Open(filepath string) error { 79 f, err := os.Open(filepath) 80 if err != nil { 81 return err 82 } 83 defer f.Close() 84 85 return i.OpenFrom(f) 86 } 87 88 // OpenFrom open pdf from io.Reader 89 func (i *PDFt) OpenFrom(r io.Reader) error { 90 //init 91 i.fontDatas = make(map[string]*PDFFontData) 92 i.curr.lineWidth = 1.0 93 i.pdfImgsMd5Map = make(map[string]*PDFImageData) 94 //open 95 err := PDFParse(r, &i.pdf) 96 if err != nil { 97 return err 98 } 99 100 i.ShowCellBorder(false) 101 102 return nil 103 } 104 105 // DuplicatePageAfter ... 106 func (i *PDFt) DuplicatePageAfter(targetPageNumber, position int) error { 107 pageObjIds, err := i.pdf.getPageObjIDs() 108 if err != nil { 109 return err 110 } 111 if targetPageNumber > 0 && len(pageObjIds) < targetPageNumber { 112 return errors.New("No desired page to copy") 113 } 114 115 pageObj := *(i.pdf.getObjByID(pageObjIds[targetPageNumber-1])) //copy object value 116 117 props, err := pageObj.readProperties() 118 if err != nil { 119 return err 120 } 121 pageContent := props.getPropByKey("Contents") 122 if pageContent == nil { 123 return errors.New("No Contents property in this object") 124 } 125 contentID, _, err := pageContent.asDictionary() 126 if err != nil { 127 return err 128 } 129 contentID = i.pdf.putNewObject(*(i.pdf.getObjByID(contentID))) //copy object value 130 pageContent.setAsDictionary(contentID, 0) 131 pageObj.setProperties(props) 132 133 pageID := i.pdf.putNewObject(pageObj) 134 135 if position < 0 { 136 position = len(pageObjIds) + position // like python 137 } 138 pageObjIds = append(pageObjIds, 0) 139 copy(pageObjIds[position+1:], pageObjIds[position:]) 140 pageObjIds[position+1] = pageID 141 142 return i.setPages(pageObjIds) 143 } 144 145 // RemovePage remove page at targetPageNumber 146 func (i *PDFt) RemovePage(targetPageNumber int) error { 147 pageObjIds, err := i.pdf.getPageObjIDs() 148 if err != nil { 149 return err 150 } 151 if targetPageNumber > 0 && len(pageObjIds) < targetPageNumber { 152 return errors.New("No desired page to remove") 153 } 154 copy(pageObjIds[targetPageNumber-1:], pageObjIds[targetPageNumber:]) 155 pageObjIds = pageObjIds[:len(pageObjIds)-1] 156 157 return i.setPages(pageObjIds) 158 } 159 160 // RemoveOtherPages remove all pages, but not targetPageNumber 161 func (i *PDFt) RemoveOtherPages(targetPageNumber int) error { 162 pageObjIds, err := i.pdf.getPageObjIDs() 163 if err != nil { 164 return err 165 } 166 if targetPageNumber > 0 && len(pageObjIds) < targetPageNumber { 167 return errors.New("No desired page to keep") 168 } 169 170 return i.setPages([]int{pageObjIds[targetPageNumber]}) 171 } 172 173 func (i *PDFt) setPages(pageObjIds []int) error { 174 props, err := i.pdf.pagesObj.readProperties() 175 if err != nil { 176 return err 177 } 178 nPage := len(pageObjIds) 179 props.getPropByKey("Count").rawVal = strconv.Itoa(nPage) 180 props.getPropByKey("Kids").setAsDictionaryArr(pageObjIds, nil) 181 i.pdf.pagesObj.setProperties(props) 182 return nil 183 } 184 185 // GetNumberOfPage get number of page 186 func (i *PDFt) GetNumberOfPage() int { 187 pageObjIds, err := i.pdf.getPageObjIDs() 188 if err != nil { 189 return 0 190 } 191 return len(pageObjIds) 192 } 193 194 // Insert insert text in to pdf 195 func (i *PDFt) Insert(text string, pageNum int, x float64, y float64, w float64, h float64, align int, fontColor *FontColor) error { 196 var ct ContentText 197 ct.text = text 198 ct.fontColor = fontColor 199 ct.fontName = i.curr.fontName 200 ct.fontStyle = i.curr.fontStyle 201 ct.fontSize = i.curr.fontSize 202 ct.pageNum = pageNum 203 ct.x = x 204 ct.y = y 205 ct.w = w 206 ct.h = h 207 ct.align = align 208 ct.lineWidth = i.curr.lineWidth 209 ct.setProtection(i.protection()) 210 if _, have := i.fontDatas[ct.fontName]; !have { 211 return ErrFontNameNotFound 212 } 213 ct.pdfFontData = i.fontDatas[ct.fontName] 214 i.contenters = append(i.contenters, &ct) 215 return i.fontDatas[ct.fontName].addChars(text) 216 } 217 218 // MeasureTextWidth measure text width 219 func (i *PDFt) MeasureTextWidth(text string) (float64, error) { 220 i.fontDatas[i.curr.fontName].addChars(text) 221 var ct ContentText 222 ct.text = text 223 ct.fontName = i.curr.fontName 224 ct.fontStyle = i.curr.fontStyle 225 ct.fontSize = i.curr.fontSize 226 ct.lineWidth = i.curr.lineWidth 227 if _, have := i.fontDatas[ct.fontName]; !have { 228 return 0, ErrFontNameNotFound 229 } 230 ct.pdfFontData = i.fontDatas[ct.fontName] 231 width, err := ct.measureTextWidth() 232 return width, err 233 } 234 235 // InsertImgBase64 insert img base 64 236 func (i *PDFt) InsertImgBase64(base64str string, pageNum int, x float64, y float64, w float64, h float64) error { 237 238 var pdfimg PDFImageData 239 imgObj, smask, err := createImgObjFromImgBase64(base64str) 240 if err != nil { 241 return err 242 } 243 244 if smask != nil { 245 buff, err := smask.BytesBuffer(0) //ใส่ id ไปมั่วๆทำให้ไม่ support password protect 246 if err != nil { 247 return err 248 } 249 var pdfObj PDFObjData 250 pdfObj.data = buff.Bytes() 251 smaskObjID := i.pdf.putNewObject(pdfObj) 252 imgObj.SetSMaskObjID(smaskObjID) 253 } 254 255 err = pdfimg.setImgObj(imgObj) 256 if err != nil { 257 return err 258 } 259 /*err := pdfimg.setImgBase64(base64str) 260 if err != nil { 261 return err 262 }*/ 263 i.pdfImgs = append(i.pdfImgs, &pdfimg) 264 //fmt.Printf("-->%d\n", len(i.pdfImgs)) 265 266 var ct contentImgBase64 267 ct.pageNum = pageNum 268 ct.x = x 269 ct.y = y 270 ct.h = h 271 ct.w = w 272 ct.refPdfimg = &pdfimg //i.pdfImgs[len(i.pdfImgs)-1] 273 i.contenters = append(i.contenters, &ct) 274 return nil 275 } 276 277 // InsertImg insert img 278 func (i *PDFt) InsertImg(img []byte, pageNum int, x float64, y float64, w float64, h float64) error { 279 280 var pdfimg PDFImageData 281 /*err := pdfimg.setImg(img) 282 if err != nil { 283 return err 284 }*/ 285 imgObj, smask, err := createImgObjFromBytes(img) 286 if err != nil { 287 return err 288 } 289 if smask != nil { 290 buff, err := smask.BytesBuffer(0) //ใส่ id ไปมั่วๆทำให้ไม่ support password protect 291 if err != nil { 292 return err 293 } 294 var pdfObj PDFObjData 295 pdfObj.data = buff.Bytes() 296 smaskObjID := i.pdf.putNewObject(pdfObj) 297 imgObj.SetSMaskObjID(smaskObjID) 298 } 299 300 pdfimg.setImgObj(imgObj) 301 302 i.pdfImgs = append(i.pdfImgs, &pdfimg) 303 //fmt.Printf("-->%d\n", len(i.pdfImgs)) 304 305 var ct contentImgBase64 306 ct.pageNum = pageNum 307 ct.x = x 308 ct.y = y 309 ct.h = h 310 ct.w = w 311 ct.refPdfimg = &pdfimg //i.pdfImgs[len(i.pdfImgs)-1] 312 i.contenters = append(i.contenters, &ct) 313 //fmt.Printf("append(i.contenters, &ct) %d\n", len(i.contenters)) 314 //i.insertContenters(0, &ct) 315 return nil 316 } 317 318 // InsertImgWithCache insert img with cache 319 func (i *PDFt) InsertImgWithCache(img []byte, pageNum int, x float64, y float64, w float64, h float64) error { 320 md5Str := fmt.Sprintf("%x", md5.Sum(img)) 321 var pdfimg *PDFImageData 322 if val, ok := i.pdfImgsMd5Map[md5Str]; ok { 323 pdfimg = val 324 } else { 325 pdfimg = &PDFImageData{} 326 /*err := pdfimg.setImg(img) 327 if err != nil { 328 return err 329 }*/ 330 imgObj, smask, err := createImgObjFromBytes(img) 331 if err != nil { 332 return err 333 } 334 if smask != nil { 335 buff, err := smask.BytesBuffer(0) //ใส่ id ไปมั่วๆทำให้ไม่ support password protect 336 if err != nil { 337 return err 338 } 339 var pdfObj PDFObjData 340 pdfObj.data = buff.Bytes() 341 smaskObjID := i.pdf.putNewObject(pdfObj) 342 imgObj.SetSMaskObjID(smaskObjID) 343 } 344 pdfimg.setImgObj(imgObj) 345 346 i.pdfImgs = append(i.pdfImgs, pdfimg) 347 i.pdfImgsMd5Map[md5Str] = pdfimg 348 } 349 var ct contentImgBase64 350 ct.pageNum = pageNum 351 ct.x = x 352 ct.y = y 353 ct.h = h 354 ct.w = w 355 ct.refPdfimg = pdfimg 356 i.contenters = append(i.contenters, &ct) 357 return nil 358 } 359 360 /* 361 func (i *PDFt) insertContenters(index int, src Contenter) { 362 i.contenters = append(i.contenters, nil) 363 copy(i.contenters[index+1:], i.contenters[index:]) 364 i.contenters[index] = src 365 }*/ 366 367 // AddFont add ttf font 368 func (i *PDFt) AddFont(name string, ttfpath string) error { 369 370 if _, have := i.fontDatas[name]; have { 371 return ErrAddSameFontName 372 } 373 374 fontData, err := PDFParseFont(ttfpath, name) 375 if err != nil { 376 return err 377 } 378 379 i.fontDatas[name] = fontData 380 return nil 381 } 382 383 func (i *PDFt) AddFontFrom(name string, reader io.Reader) error { 384 385 if _, have := i.fontDatas[name]; have { 386 return ErrAddSameFontName 387 } 388 389 fontData, err := PDFParseFontReader(reader, name) 390 if err != nil { 391 return err 392 } 393 394 i.fontDatas[name] = fontData 395 return nil 396 } 397 398 // TextriseOverride override text rise 399 // Text rise, Trise , specifies the distance, in unscaled text space units, 400 // to move the baseline up or down from its default location. 401 // Positive values of text rise move the baseline up. 402 // Adjustments to the baseline are useful for drawing superscripts or subscripts. 403 // The default location of the baseline can be restored by setting the text rise to 0. 404 func (i *PDFt) TextriseOverride(name string, fn FuncTextriseOverride) error { 405 if _, have := i.fontDatas[name]; !have { 406 return ErrFontNameNotFound 407 } 408 i.fontDatas[name].font.SetFuncTextriseOverride(func( 409 leftRune rune, 410 rightRune rune, 411 fontsize int, 412 allText string, 413 currTextIndex int, 414 ) float32 { 415 return fn(leftRune, rightRune, fontsize, allText, currTextIndex) 416 }) 417 return nil 418 } 419 420 // KernOverride override kerning 421 func (i *PDFt) KernOverride(name string, fn FuncKernOverride) error { 422 if _, have := i.fontDatas[name]; !have { 423 return ErrFontNameNotFound 424 } 425 i.fontDatas[name].font.SetFuncKernOverride(func( 426 leftRune rune, 427 rightRune rune, 428 leftPair uint, 429 rightPair uint, 430 pairVal int16, 431 ) int16 { 432 return fn(leftRune, rightRune, leftPair, rightPair, pairVal) 433 }) 434 return nil 435 } 436 437 // SetFont set font 438 func (i *PDFt) SetFont(name string, style string, size int) error { 439 440 if _, have := i.fontDatas[name]; !have { 441 return ErrFontNameNotFound 442 } 443 i.curr.fontName = name 444 i.curr.fontStyle = getConvertedStyle(style) 445 i.curr.fontSize = size 446 return nil 447 } 448 449 // Save save output pdf 450 func (i *PDFt) Save(filepath string) error { 451 var buff bytes.Buffer 452 err := i.SaveTo(&buff) 453 if err != nil { 454 return err 455 } 456 err = ioutil.WriteFile(filepath, buff.Bytes(), 0644) 457 if err != nil { 458 return err 459 } 460 return nil 461 } 462 463 // SaveTo save pdf to io.Writer 464 func (i *PDFt) SaveTo(w io.Writer) error { 465 466 newpdf, lastID, err := i.build() 467 if err != nil { 468 return err 469 } 470 471 buff, err := i.toStream(newpdf, lastID) 472 if err != nil { 473 return err 474 } 475 _, err = buff.WriteTo(w) 476 if err != nil { 477 return err 478 } 479 return nil 480 } 481 482 func (i *PDFt) build() (*PDFData, int, error) { 483 484 var err error 485 nextID := i.pdf.maxID() 486 for _, fontData := range i.fontDatas { 487 nextID++ 488 fontData.setStartID(nextID) 489 nextID, err = fontData.build() 490 if err != nil { 491 return nil, 0, err 492 } 493 } 494 495 newpdf := i.pdf //copy 496 497 err = newpdf.injectFontsToPDF(i.fontDatas) 498 if err != nil { 499 return nil, 0, err 500 } 501 502 //ยัด subsetfont obj กลับไป 503 for _, fontData := range i.fontDatas { 504 505 var fontobj, cidObj, unicodeMapObj, fontDescObj, dictionaryObj PDFObjData 506 507 fontobj.objID = fontData.fontID 508 fontobj.data = fontData.fontStream.Bytes() 509 510 cidObj.objID = fontData.cidID 511 cidObj.data = fontData.cidStream.Bytes() 512 513 unicodeMapObj.objID = fontData.unicodeMapID 514 unicodeMapObj.data = fontData.unicodeMapStream.Bytes() 515 516 fontDescObj.objID = fontData.fontDescID 517 fontDescObj.data = fontData.fontDescStream.Bytes() 518 519 dictionaryObj.objID = fontData.dictionaryID 520 dictionaryObj.data = fontData.dictionaryStream.Bytes() 521 522 newpdf.put(fontobj) 523 newpdf.put(cidObj) 524 newpdf.put(unicodeMapObj) 525 newpdf.put(fontDescObj) 526 newpdf.put(dictionaryObj) 527 } 528 529 for j, pdfImg := range i.pdfImgs { 530 nextID++ 531 var obj PDFObjData 532 obj.objID = nextID 533 obj.data = pdfImg.imgObj.GetObjBuff().Bytes() 534 i.pdfImgs[j].objID = obj.objID 535 //fmt.Printf("---->%d\n", obj.objID) 536 newpdf.put(obj) 537 } 538 539 err = newpdf.injectImgsToPDF(i.pdfImgs) 540 if err != nil { 541 return nil, 0, err 542 } 543 544 err = newpdf.injectContentToPDF(&i.contenters) 545 if err != nil { 546 return nil, 0, err 547 } 548 549 //set for protection 550 if i.protection() != nil { 551 max := newpdf.Len() 552 x := 0 553 for x < max { 554 newpdf.objs[x].encrypt(i.protection()) 555 x++ 556 } 557 } 558 559 return &newpdf, nextID, nil 560 } 561 562 func (i *PDFt) toStream(newpdf *PDFData, lastID int) (*bytes.Buffer, error) { 563 564 //set for protection 565 encryptionObjID := -1 566 if i.protection() != nil { 567 lastID++ 568 encryptionObjID = lastID 569 enObj := i.protection().EncryptionObj() 570 err := enObj.Build(lastID) 571 if err != nil { 572 return nil, err 573 } 574 buff := enObj.GetObjBuff() 575 var enPDFObjData PDFObjData 576 enPDFObjData.data = buff.Bytes() 577 enPDFObjData.objID = lastID 578 newpdf.put(enPDFObjData) 579 } 580 581 var buff bytes.Buffer 582 buff.WriteString("%PDF-1.7\n\n") 583 xrefs := make(map[int]int) 584 for _, obj := range newpdf.objs { 585 //xrefs = append(xrefs, buff.Len()) 586 //fmt.Printf("%d\n", obj.objID) 587 xrefs[obj.objID] = buff.Len() 588 buff.WriteString(fmt.Sprintf("\n%d 0 obj\n", obj.objID)) 589 buff.WriteString(strings.TrimSpace(string(obj.data))) 590 buff.WriteString("\nendobj\n") 591 } 592 i.xref(xrefs, &buff, lastID, newpdf.trailer.rootObjID, encryptionObjID) 593 594 return &buff, nil 595 } 596 597 type xrefrow struct { 598 offset int 599 gen string 600 flag string 601 } 602 603 func (i *PDFt) xref(linelens map[int]int, buff *bytes.Buffer, size int, rootID int, encryptionObjID int) { 604 xrefbyteoffset := buff.Len() 605 606 //start xref 607 buff.WriteString("\nxref\n") 608 buff.WriteString(fmt.Sprintf("0 %d\r\n", size+1)) 609 var xrefrows []xrefrow 610 xrefrows = append(xrefrows, xrefrow{offset: 0, flag: "f", gen: "65535"}) 611 lastIndexOfF := 0 612 j := 1 613 //fmt.Printf("size:%d\n", size) 614 for j <= size { 615 if linelen, ok := linelens[j]; ok { 616 xrefrows = append(xrefrows, xrefrow{offset: linelen, flag: "n", gen: "00000"}) 617 } else { 618 xrefrows = append(xrefrows, xrefrow{offset: 0, flag: "f", gen: "65535"}) 619 offset := len(xrefrows) - 1 620 xrefrows[lastIndexOfF].offset = offset 621 lastIndexOfF = offset 622 } 623 j++ 624 } 625 626 for _, xrefrow := range xrefrows { 627 buff.WriteString(i.formatXrefline(xrefrow.offset) + " " + xrefrow.gen + " " + xrefrow.flag + " \n") 628 } 629 //end xref 630 631 buff.WriteString("trailer\n") 632 buff.WriteString("<<\n") 633 buff.WriteString(fmt.Sprintf("/Size %d\n", size+1)) 634 buff.WriteString(fmt.Sprintf("/Root %d 0 R\n", rootID)) 635 if i.protection() != nil { 636 buff.WriteString(fmt.Sprintf("/Encrypt %d 0 R\n", encryptionObjID)) 637 buff.WriteString("/ID [()()]\n") 638 } 639 buff.WriteString(">>\n") 640 641 buff.WriteString("startxref\n") 642 buff.WriteString(strconv.Itoa(xrefbyteoffset)) 643 buff.WriteString("\n%%EOF\n") 644 } 645 646 func (i *PDFt) formatXrefline(n int) string { 647 str := strconv.Itoa(n) 648 for len(str) < 10 { 649 str = "0" + str 650 } 651 return str 652 } 653 654 // SetProtection set pdf protection 655 func (i *PDFt) SetProtection( 656 permissions int, 657 userPass []byte, 658 ownerPass []byte, 659 ) error { 660 var p gopdf.PDFProtection 661 err := p.SetProtection(permissions, userPass, ownerPass) 662 if err != nil { 663 return err 664 } 665 i.pdfProtection = &p 666 return nil 667 } 668 669 // Regular - font style regular 670 const Regular = 0 //000000 671 // Italic - font style italic 672 const Italic = 1 //000001 673 // Bold - font style bold 674 const Bold = 2 //000010 675 // Underline - font style underline 676 const Underline = 4 //000100 677 678 func getConvertedStyle(fontStyle string) (style int) { 679 fontStyle = strings.ToUpper(fontStyle) 680 if strings.Contains(fontStyle, "B") { 681 style = style | Bold 682 } 683 if strings.Contains(fontStyle, "I") { 684 style = style | Italic 685 } 686 if strings.Contains(fontStyle, "U") { 687 style = style | Underline 688 } 689 return 690 }