github.com/signintech/pdft@v0.5.0/minigopdf/pdf_dictionary_obj.go (about) 1 package gopdf 2 3 import ( 4 "bytes" 5 "compress/zlib" 6 "errors" 7 "sort" 8 "strconv" 9 10 "github.com/signintech/pdft/minigopdf/fontmaker/core" 11 ) 12 13 // EntrySelectors entry selectors 14 var EntrySelectors = []int{ 15 0, 0, 1, 1, 2, 2, 16 2, 2, 3, 3, 3, 3, 17 3, 3, 3, 3, 4, 4, 18 4, 4, 4, 4, 4, 4, 19 4, 4, 4, 4, 4, 4, 4, 20 } 21 22 // ErrNotSupportShortIndexYet not suport none short index yet 23 var ErrNotSupportShortIndexYet = errors.New("not suport none short index yet") 24 25 // PdfDictionaryObj pdf dictionary object 26 type PdfDictionaryObj struct { 27 buffer bytes.Buffer 28 PtrToSubsetFontObj *SubsetFontObj 29 //getRoot func() *GoPdf 30 pdfProtection *PDFProtection 31 } 32 33 func (p *PdfDictionaryObj) init(funcGetRoot func() *GoPdf) { 34 //p.getRoot = funcGetRoot 35 } 36 37 func (p *PdfDictionaryObj) setProtection(pr *PDFProtection) { 38 p.pdfProtection = pr 39 } 40 41 func (p *PdfDictionaryObj) protection() *PDFProtection { 42 return p.pdfProtection 43 } 44 45 func (p *PdfDictionaryObj) build(objID int) error { 46 b, err := p.makeFont() 47 if err != nil { 48 //log.Panicf("%s", err.Error()) 49 return err 50 } 51 52 //zipvar buff bytes.Buffer 53 var zbuff bytes.Buffer 54 gzipwriter := zlib.NewWriter(&zbuff) 55 _, err = gzipwriter.Write(b) 56 if err != nil { 57 return err 58 } 59 gzipwriter.Close() 60 61 p.buffer.WriteString("<</Length " + strconv.Itoa(zbuff.Len()) + "\n") 62 p.buffer.WriteString("/Filter /FlateDecode\n") 63 p.buffer.WriteString("/Length1 " + strconv.Itoa(len(b)) + "\n") 64 p.buffer.WriteString(">>\n") 65 p.buffer.WriteString("stream\n") 66 if p.protection() != nil { 67 tmp, err := rc4Cip(p.protection().objectkey(objID), zbuff.Bytes()) 68 if err != nil { 69 return err 70 } 71 p.buffer.Write(tmp) 72 //p.buffer.WriteString("\n") 73 } else { 74 p.buffer.Write(zbuff.Bytes()) 75 } 76 p.buffer.WriteString("\nendstream\n") 77 return nil 78 } 79 80 func (p *PdfDictionaryObj) getType() string { 81 return "PdfDictionary" 82 } 83 84 func (p *PdfDictionaryObj) getObjBuff() *bytes.Buffer { 85 return &p.buffer 86 } 87 88 // SetPtrToSubsetFontObj set subsetFontObj pointer 89 func (p *PdfDictionaryObj) SetPtrToSubsetFontObj(ptr *SubsetFontObj) { 90 p.PtrToSubsetFontObj = ptr 91 } 92 93 func (p *PdfDictionaryObj) makeGlyfAndLocaTable() ([]byte, []int, error) { 94 ttfp := p.PtrToSubsetFontObj.GetTTFParser() 95 var glyf core.TableDirectoryEntry 96 97 numGlyphs := int(ttfp.NumGlyphs()) 98 99 glyphArray := p.completeGlyphClosure(p.PtrToSubsetFontObj.CharacterToGlyphIndex) 100 glyphCount := len(glyphArray) 101 sort.Ints(glyphArray) 102 103 size := 0 104 for idx := 0; idx < glyphCount; idx++ { 105 size += p.getGlyphSize(glyphArray[idx]) 106 } 107 glyf.Length = uint(size) 108 109 glyphTable := make([]byte, glyf.PaddedLength()) 110 locaTable := make([]int, numGlyphs+1) 111 112 glyphOffset := 0 113 glyphIndex := 0 114 for idx := 0; idx < numGlyphs; idx++ { 115 locaTable[idx] = glyphOffset 116 if glyphIndex < glyphCount && glyphArray[glyphIndex] == idx { 117 glyphIndex++ 118 bytes := p.getGlyphData(idx) 119 length := len(bytes) 120 if length > 0 { 121 for i := 0; i < length; i++ { 122 glyphTable[glyphOffset+i] = bytes[i] 123 } 124 glyphOffset += length 125 } 126 } 127 } //end for 128 locaTable[numGlyphs] = glyphOffset 129 return glyphTable, locaTable, nil 130 } 131 132 func (p *PdfDictionaryObj) getGlyphSize(glyph int) int { 133 134 ttfp := p.PtrToSubsetFontObj.GetTTFParser() 135 glyf := ttfp.GetTables()["glyf"] 136 start := int(glyf.Offset + ttfp.LocaTable[glyph]) 137 next := int(glyf.Offset + ttfp.LocaTable[glyph+1]) 138 return next - start 139 } 140 141 func (p *PdfDictionaryObj) getGlyphData(glyph int) []byte { 142 ttfp := p.PtrToSubsetFontObj.GetTTFParser() 143 glyf := ttfp.GetTables()["glyf"] 144 start := int(glyf.Offset + ttfp.LocaTable[glyph]) 145 next := int(glyf.Offset + ttfp.LocaTable[glyph+1]) 146 count := next - start 147 var data []byte 148 i := 0 149 for i < count { 150 data = append(data, ttfp.FontData()[start+i]) 151 i++ 152 } 153 return data 154 } 155 156 func (p *PdfDictionaryObj) makeFont() ([]byte, error) { 157 var buff Buff 158 ttfp := p.PtrToSubsetFontObj.GetTTFParser() 159 tables := make(map[string]core.TableDirectoryEntry) 160 tables["cvt "] = ttfp.GetTables()["cvt "] //มีช่องว่างด้วยนะ 161 tables["fpgm"] = ttfp.GetTables()["fpgm"] 162 tables["glyf"] = ttfp.GetTables()["glyf"] 163 tables["head"] = ttfp.GetTables()["head"] 164 tables["hhea"] = ttfp.GetTables()["hhea"] 165 tables["hmtx"] = ttfp.GetTables()["hmtx"] 166 tables["loca"] = ttfp.GetTables()["loca"] 167 tables["maxp"] = ttfp.GetTables()["maxp"] 168 tables["prep"] = ttfp.GetTables()["prep"] 169 tableCount := len(tables) 170 selector := EntrySelectors[tableCount] 171 172 glyphTable, locaTable, err := p.makeGlyfAndLocaTable() 173 if err != nil { 174 return nil, err 175 } 176 177 WriteUInt32(&buff, 0x00010000) 178 WriteUInt16(&buff, uint(tableCount)) 179 WriteUInt16(&buff, ((1 << uint(selector)) * 16)) 180 WriteUInt16(&buff, uint(selector)) 181 WriteUInt16(&buff, (uint(tableCount)-(1<<uint(selector)))*16) 182 183 var tags []string 184 for tag := range tables { 185 tags = append(tags, tag) //copy all tag 186 } 187 sort.Strings(tags) //order 188 idx := 0 189 tablePosition := int(12 + 16*tableCount) 190 for idx < tableCount { 191 entry := tables[tags[idx]] 192 //write data 193 offset := uint64(tablePosition) 194 buff.SetPosition(tablePosition) 195 if tags[idx] == "glyf" { 196 entry.Length = uint(len(glyphTable)) 197 entry.CheckSum = CheckSum(glyphTable) 198 WriteBytes(&buff, glyphTable, 0, entry.PaddedLength()) 199 } else if tags[idx] == "loca" { 200 if ttfp.IsShortIndex { 201 entry.Length = uint(len(locaTable) * 2) 202 } else { 203 entry.Length = uint(len(locaTable) * 4) 204 } 205 206 data := make([]byte, entry.PaddedLength()) 207 length := len(locaTable) 208 byteIdx := 0 209 if ttfp.IsShortIndex { 210 for idx := 0; idx < length; idx++ { 211 val := locaTable[idx] / 2 212 data[byteIdx] = byte(val >> 8) 213 byteIdx++ 214 data[byteIdx] = byte(val) 215 byteIdx++ 216 } 217 } else { 218 for idx := 0; idx < length; idx++ { 219 val := locaTable[idx] 220 data[byteIdx] = byte(val >> 24) 221 byteIdx++ 222 data[byteIdx] = byte(val >> 16) 223 byteIdx++ 224 data[byteIdx] = byte(val >> 8) 225 byteIdx++ 226 data[byteIdx] = byte(val) 227 byteIdx++ 228 } 229 } 230 entry.CheckSum = CheckSum(data) 231 WriteBytes(&buff, data, 0, len(data)) 232 } else { 233 WriteBytes(&buff, ttfp.FontData(), int(entry.Offset), entry.PaddedLength()) 234 } 235 endPosition := buff.Position() 236 tablePosition = endPosition 237 238 //write table 239 buff.SetPosition(idx*16 + 12) 240 WriteTag(&buff, tags[idx]) 241 WriteUInt32(&buff, uint(entry.CheckSum)) 242 WriteUInt32(&buff, uint(offset)) //offset 243 WriteUInt32(&buff, uint(entry.Length)) 244 245 tablePosition = endPosition 246 idx++ 247 } 248 //DebugSubType(buff.Bytes()) 249 //me.buffer.Write(buff.Bytes()) 250 return buff.Bytes(), nil 251 } 252 253 func (p *PdfDictionaryObj) completeGlyphClosure(mapOfglyphs *MapOfCharacterToGlyphIndex) []int { 254 var glyphArray []int 255 //copy 256 isContainZero := false 257 glyphs := mapOfglyphs.AllVals() 258 for _, v := range glyphs { 259 glyphArray = append(glyphArray, int(v)) 260 if v == 0 { 261 isContainZero = true 262 } 263 } 264 if !isContainZero { 265 glyphArray = append(glyphArray, 0) 266 } 267 268 i := 0 269 count := len(glyphs) 270 for i < count { 271 p.AddCompositeGlyphs(&glyphArray, glyphArray[i]) 272 i++ 273 } 274 return glyphArray 275 } 276 277 // AddCompositeGlyphs add composite glyph 278 // composite glyph is a Unicode entity that can be defined as a sequence of one or more other characters. 279 func (p *PdfDictionaryObj) AddCompositeGlyphs(glyphArray *[]int, glyph int) { 280 start := p.GetOffset(int(glyph)) 281 if start == p.GetOffset(int(glyph)+1) { 282 return 283 } 284 285 offset := start 286 ttfp := p.PtrToSubsetFontObj.GetTTFParser() 287 fontData := ttfp.FontData() 288 numContours, step := ReadShortFromByte(fontData, offset) 289 offset += step 290 if numContours >= 0 { 291 return 292 } 293 294 offset += 8 295 for { 296 flags, step1 := ReadUShortFromByte(fontData, offset) 297 offset += step1 298 cGlyph, step2 := ReadUShortFromByte(fontData, offset) 299 offset += step2 300 //check cGlyph is contain in glyphArray? 301 glyphContainsKey := false 302 for _, g := range *glyphArray { 303 if g == int(cGlyph) { 304 glyphContainsKey = true 305 break 306 } 307 } 308 if !glyphContainsKey { 309 *glyphArray = append(*glyphArray, int(cGlyph)) 310 } 311 312 if (flags & MORE_COMPONENTS) == 0 { 313 return 314 } 315 offsetAppend := 4 316 if (flags & ARG_1_AND_2_ARE_WORDS) == 0 { 317 offsetAppend = 2 318 } 319 if (flags & WE_HAVE_A_SCALE) != 0 { 320 offsetAppend += 2 321 } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0 { 322 offsetAppend += 4 323 } 324 if (flags & WE_HAVE_A_TWO_BY_TWO) != 0 { 325 offsetAppend += 8 326 } 327 offset += offsetAppend 328 } 329 } 330 331 const WE_HAVE_A_SCALE = 8 332 const MORE_COMPONENTS = 32 333 const ARG_1_AND_2_ARE_WORDS = 1 334 const WE_HAVE_AN_X_AND_Y_SCALE = 64 335 const WE_HAVE_A_TWO_BY_TWO = 128 336 337 // GetOffset get offset from glyf table 338 func (p *PdfDictionaryObj) GetOffset(glyph int) int { 339 ttfp := p.PtrToSubsetFontObj.GetTTFParser() 340 glyf := ttfp.GetTables()["glyf"] 341 offset := int(glyf.Offset + ttfp.LocaTable[glyph]) 342 return offset 343 } 344 345 // Build build buffer 346 func (p *PdfDictionaryObj) Build(objID int) error { 347 return p.build(objID) 348 } 349 350 // GetObjBuff get buffer 351 func (p *PdfDictionaryObj) GetObjBuff() *bytes.Buffer { 352 return p.getObjBuff() 353 } 354 355 // CheckSum check sum 356 func CheckSum(data []byte) uint { 357 358 var byte3, byte2, byte1, byte0 uint64 359 byte3 = 0 360 byte2 = 0 361 byte1 = 0 362 byte0 = 0 363 length := len(data) 364 i := 0 365 for i < length { 366 byte3 += uint64(data[i]) 367 i++ 368 byte2 += uint64(data[i]) 369 i++ 370 byte1 += uint64(data[i]) 371 i++ 372 byte0 += uint64(data[i]) 373 i++ 374 } 375 //var result uint32 376 result := uint32(byte3<<24) + uint32(byte2<<16) + uint32(byte1<<8) + uint32(byte0) 377 return uint(result) 378 }