github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/core/parser_test.go (about) 1 /* 2 * This file is subject to the terms and conditions defined in 3 * file 'LICENSE.md', which is part of this source code package. 4 */ 5 6 package core 7 8 import ( 9 "bufio" 10 "bytes" 11 "encoding/hex" 12 //"fmt" 13 "io" 14 //"os" 15 "testing" 16 17 "github.com/unidoc/unidoc/common" 18 ) 19 20 func init() { 21 common.SetLogger(common.ConsoleLogger{}) 22 } 23 24 func makeReaderForText(txt string) (*bytes.Reader, *bufio.Reader, int64) { 25 buf := []byte(txt) 26 bufReader := bytes.NewReader(buf) 27 bufferedReader := bufio.NewReader(bufReader) 28 return bufReader, bufferedReader, int64(len(txt)) 29 } 30 31 func makeParserForText(txt string) *PdfParser { 32 rs, reader, fileSize := makeReaderForText(txt) 33 return &PdfParser{rs: rs, reader: reader, fileSize: fileSize} 34 } 35 36 func BenchmarkSkipSpaces(b *testing.B) { 37 parser := makeParserForText(" \t\t \tABC") 38 for n := 0; n < b.N; n++ { 39 parser.skipSpaces() 40 parser.SetFileOffset(0) 41 } 42 } 43 44 var namePairs = map[string]string{ 45 "/Name1": "Name1", 46 "/ASomewhatLongerName": "ASomewhatLongerName", 47 "/A;Name_With-Various***Characters?": "A;Name_With-Various***Characters?", 48 "/1.2": "1.2", 49 "/$$": "$$", 50 "/@pattern": "@pattern", 51 "/.notdef": ".notdef", 52 "/Lime#20Green": "Lime Green", 53 "/paired#28#29parentheses": "paired()parentheses", 54 "/The_Key_of_F#23_Minor": "The_Key_of_F#_Minor", 55 "/A#42": "AB", 56 "/": "", 57 "/ ": "", 58 "/#3CBC88#3E#3CC5ED#3E#3CD544#3E#3CC694#3E": "<BC88><C5ED><D544><C694>", 59 } 60 61 func BenchmarkNameParsing(b *testing.B) { 62 for n := 0; n < b.N; n++ { 63 for str, name := range namePairs { 64 parser := makeParserForText(str) 65 o, err := parser.parseName() 66 if err != nil && err != io.EOF { 67 b.Errorf("Unable to parse name string, error: %s", err) 68 } 69 if string(o) != name { 70 b.Errorf("Mismatch %s != %s", o, name) 71 } 72 } 73 } 74 } 75 76 func TestNameParsing(t *testing.T) { 77 for str, name := range namePairs { 78 parser := makeParserForText(str) 79 o, err := parser.parseName() 80 if err != nil && err != io.EOF { 81 t.Errorf("Unable to parse name string, error: %s", err) 82 } 83 if string(o) != name { 84 t.Errorf("Mismatch %s != %s", o, name) 85 } 86 } 87 88 // Should fail (require starting with '/') 89 parser := makeParserForText(" /Name") 90 _, err := parser.parseName() 91 if err == nil || err == io.EOF { 92 t.Errorf("Should be invalid name") 93 } 94 } 95 96 type testStringEntry struct { 97 raw string 98 expected string 99 } 100 101 func BenchmarkStringParsing(b *testing.B) { 102 entry := "(Strings may contain balanced parenthesis () and\nspecial characters (*!&}^% and so on).)" 103 parser := makeParserForText(entry) 104 for n := 0; n < b.N; n++ { 105 _, err := parser.parseString() 106 if err != nil && err != io.EOF { 107 b.Errorf("Unable to parse string, error: %s", err) 108 } 109 parser.SetFileOffset(0) 110 } 111 } 112 113 var stringPairs = map[string]string{ 114 "(This is a string)": "This is a string", 115 "(Strings may contain\n newlines and such)": "Strings may contain\n newlines and such", 116 "(Strings may contain balanced parenthesis () and\nspecial characters (*!&}^% and so on).)": "Strings may contain balanced parenthesis () and\nspecial characters (*!&}^% and so on).", 117 "(These \\\ntwo strings \\\nare the same.)": "These two strings are the same.", 118 "(These two strings are the same.)": "These two strings are the same.", 119 "(\\\\)": "\\", 120 "(This string has an end-of-line at the end of it.\n)": "This string has an end-of-line at the end of it.\n", 121 "(So does this one.\\n)": "So does this one.\n", 122 "(\\0053)": "\0053", 123 "(\\53)": "\053", 124 "(\\053)": "+", 125 "(\\53\\101)": "+A", 126 } 127 128 func TestStringParsing(t *testing.T) { 129 for raw, expected := range stringPairs { 130 parser := makeParserForText(raw) 131 o, err := parser.parseString() 132 if err != nil && err != io.EOF { 133 t.Errorf("Unable to parse string, error: %s", err) 134 } 135 if string(o) != expected { 136 t.Errorf("String Mismatch %s: \"%s\" != \"%s\"", raw, o, expected) 137 } 138 } 139 } 140 141 func TestReadTextLine(t *testing.T) { 142 // reading text ling + rewinding should be idempotent, that is: 143 // if we rewind back len(str) bytes after reading string str we should arrive at beginning of str 144 rawText := "abc\xb0cde" 145 parser := makeParserForText(rawText) 146 s, err := parser.readTextLine() 147 if err != nil && err != io.EOF { 148 t.Errorf("Unable to parse string, error: %s", err) 149 } 150 if parser.GetFileOffset() != int64(len(s)) { 151 t.Errorf("File offset after reading string of length %d is %d", len(s), parser.GetFileOffset()) 152 } 153 } 154 155 func TestBinStringParsing(t *testing.T) { 156 // From an example O entry in Encrypt dictionary. 157 rawText1 := "(\xE6\x00\xEC\xC2\x02\x88\xAD\x8B\\r\x64\xA9" + 158 "\\)\xC6\xA8\x3E\xE2\x51\x76\x79\xAA\x02\x18\xBE\xCE\xEA" + 159 "\x8B\x79\x86\x72\x6A\x8C\xDB)" 160 161 parser := PdfParser{} 162 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText1) 163 o, err := parser.parseString() 164 if err != nil && err != io.EOF { 165 t.Errorf("Unable to parse string, error: %s", err) 166 } 167 if len(string(o)) != 32 { 168 t.Errorf("Wrong length, should be 32 (got %d)", len(string(o))) 169 } 170 } 171 172 // Main challenge in the text is "\\278A" which is "\\27" octal and 8A 173 func TestStringParsing2(t *testing.T) { 174 rawText := "[(\\227\\224`\\274\\31W\\216\\276\\23\\231\\246U\\33\\317\\6-)(\\210S\\377:\\322\\278A\\200$*/e]\\371|)]" 175 176 parser := PdfParser{} 177 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 178 list, err := parser.parseArray() 179 if err != nil { 180 t.Errorf("Failed to parse string list (%s)", err) 181 return 182 } 183 if len(list) != 2 { 184 t.Errorf("Length of list should be 2 (%d)", len(list)) 185 return 186 } 187 } 188 189 func TestBoolParsing(t *testing.T) { 190 // 7.3.2 191 testEntries := map[string]bool{} 192 testEntries["false"] = false 193 testEntries["true"] = true 194 195 for key, expected := range testEntries { 196 parser := PdfParser{} 197 parser.rs, parser.reader, parser.fileSize = makeReaderForText(key) 198 val, err := parser.parseBool() 199 if err != nil { 200 t.Errorf("Error parsing bool: %s", err) 201 return 202 } 203 if bool(val) != expected { 204 t.Errorf("bool not as expected (got %t, expected %t)", bool(val), expected) 205 return 206 } 207 } 208 } 209 210 func BenchmarkNumbericParsing(b *testing.B) { 211 txt1 := "[34.5 -3.62 1 +123.6 4. -.002 0.0]" 212 parser := PdfParser{} 213 parser.rs, parser.reader, parser.fileSize = makeReaderForText(txt1) 214 215 for n := 0; n < b.N; n++ { 216 _, err := parser.parseArray() 217 if err != nil { 218 b.Errorf("Error parsing array") 219 return 220 } 221 parser.SetFileOffset(0) 222 } 223 } 224 225 func TestNumericParsing1(t *testing.T) { 226 // 7.3.3 227 txt1 := "[34.5 -3.62 1 +123.6 4. -.002 0.0]" 228 parser := PdfParser{} 229 parser.rs, parser.reader, parser.fileSize = makeReaderForText(txt1) 230 list, err := parser.parseArray() 231 if err != nil { 232 t.Errorf("Error parsing array") 233 return 234 } 235 if len(list) != 7 { 236 t.Errorf("Len list != 7 (%d)", len(list)) 237 return 238 } 239 240 expectedFloats := map[int]float32{ 241 0: 34.5, 242 1: -3.62, 243 3: 123.6, 244 4: 4.0, 245 5: -0.002, 246 6: 0.0, 247 } 248 249 for idx, val := range expectedFloats { 250 num, ok := list[idx].(*PdfObjectFloat) 251 if !ok { 252 t.Errorf("Idx %d not float (%f)", idx, val) 253 return 254 } 255 if float32(*num) != val { 256 t.Errorf("Idx %d, value incorrect (%f)", idx, val) 257 } 258 } 259 260 inum, ok := list[2].(*PdfObjectInteger) 261 if !ok { 262 t.Errorf("Number 3 not int") 263 return 264 } 265 if *inum != 1 { 266 t.Errorf("Number 3, val != 1") 267 return 268 } 269 } 270 271 func TestNumericParsing2(t *testing.T) { 272 // 7.3.3 273 txt1 := "[+4.-.002]" // 4.0 and -0.002 274 parser := PdfParser{} 275 parser.rs, parser.reader, parser.fileSize = makeReaderForText(txt1) 276 list, err := parser.parseArray() 277 if err != nil { 278 t.Errorf("Error parsing array") 279 return 280 } 281 if len(list) != 2 { 282 t.Errorf("Len list != 2 (%d)", len(list)) 283 return 284 } 285 286 expectedFloats := map[int]float32{ 287 0: 4.0, 288 1: -0.002, 289 } 290 291 for idx, val := range expectedFloats { 292 num, ok := list[idx].(*PdfObjectFloat) 293 if !ok { 294 t.Errorf("Idx %d not float (%f)", idx, val) 295 return 296 } 297 if float32(*num) != val { 298 t.Errorf("Idx %d, value incorrect (%f)", idx, val) 299 } 300 } 301 } 302 303 // Includes exponential numbers. 304 func TestNumericParsing3(t *testing.T) { 305 // 7.3.3 306 txt1 := "[+4.-.002+3e-2-2e0]" // 4.0, -0.002, 1e-2, -2.0 307 parser := PdfParser{} 308 parser.rs, parser.reader, parser.fileSize = makeReaderForText(txt1) 309 list, err := parser.parseArray() 310 if err != nil { 311 t.Errorf("Error parsing array (%s)", err) 312 return 313 } 314 if len(list) != 4 { 315 t.Errorf("Len list != 2 (%d)", len(list)) 316 return 317 } 318 319 expectedFloats := map[int]float32{ 320 0: 4.0, 321 1: -0.002, 322 2: 0.03, 323 3: -2.0, 324 } 325 326 for idx, val := range expectedFloats { 327 num, ok := list[idx].(*PdfObjectFloat) 328 if !ok { 329 t.Errorf("Idx %d not float (%f)", idx, val) 330 return 331 } 332 if float32(*num) != val { 333 t.Errorf("Idx %d, value incorrect (%f)", idx, val) 334 } 335 } 336 } 337 338 func BenchmarkHexStringParsing(b *testing.B) { 339 var ref bytes.Buffer 340 for i := 0; i < 0xff; i++ { 341 ref.WriteByte(byte(i)) 342 } 343 parser := makeParserForText("<" + hex.EncodeToString(ref.Bytes()) + ">") 344 for n := 0; n < b.N; n++ { 345 hs, err := parser.parseHexString() 346 if err != nil { 347 b.Errorf("Error parsing hex string: %s", err.Error()) 348 return 349 } 350 if string(hs) != ref.String() { 351 b.Errorf("Reference and parsed hex strings mismatch") 352 } 353 parser.SetFileOffset(0) 354 } 355 } 356 357 func TestHexStringParsing(t *testing.T) { 358 // 7.3.4.3 359 } 360 361 // TODO. 362 // Test reference to object outside of cross-ref table - should be 0 363 // Test xref object with offset 0, should be treated as 'f'ree. 364 // (compatibility with malformed writers). 365 366 func TestDictParsing1(t *testing.T) { 367 txt1 := "<<\n\t/Name /Game /key/val/data\t[0 1 2 3.14 5]\t\n\n>>" 368 parser := PdfParser{} 369 parser.rs, parser.reader, parser.fileSize = makeReaderForText(txt1) 370 dict, err := parser.ParseDict() 371 if err != nil { 372 t.Errorf("Error parsing dict") 373 } 374 375 if len(dict.Keys()) != 3 { 376 t.Errorf("Length of dict != 3") 377 } 378 379 name, ok := dict.Get("Name").(*PdfObjectName) 380 if !ok || *name != "Game" { 381 t.Errorf("Value error") 382 } 383 384 key, ok := dict.Get("key").(*PdfObjectName) 385 if !ok || *key != "val" { 386 t.Errorf("Value error") 387 } 388 389 data, ok := dict.Get("data").(*PdfObjectArray) 390 if !ok { 391 t.Errorf("Invalid data") 392 } 393 integer, ok := (*data)[2].(*PdfObjectInteger) 394 if !ok || *integer != 2 { 395 t.Errorf("Wrong data") 396 } 397 398 float, ok := (*data)[3].(*PdfObjectFloat) 399 if !ok || *float != 3.14 { 400 t.Error("Wrong data") 401 } 402 } 403 404 func TestDictParsing2(t *testing.T) { 405 rawText := "<< /Type /Example\n" + 406 "/Subtype /DictionaryExample /Version 0.01\n" + 407 "/IntegerItem 12 \n" + 408 "/StringItem (a string) /Subdictionary << /Item1 0.4\n" + 409 "/Item2 true /LastItem (not!) /VeryLastItem (OK)\n" + 410 ">>\n >>" 411 412 parser := PdfParser{} 413 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 414 dict, err := parser.ParseDict() 415 if err != nil { 416 t.Errorf("Error parsing dict") 417 } 418 419 if len(dict.Keys()) != 6 { 420 t.Errorf("Length of dict != 6") 421 } 422 423 typeName, ok := dict.Get("Type").(*PdfObjectName) 424 if !ok || *typeName != "Example" { 425 t.Errorf("Wrong type") 426 } 427 428 str, ok := dict.Get("StringItem").(*PdfObjectString) 429 if !ok || *str != "a string" { 430 t.Errorf("Invalid string item") 431 } 432 433 subDict, ok := dict.Get("Subdictionary").(*PdfObjectDictionary) 434 if !ok { 435 t.Errorf("Invalid sub dictionary") 436 } 437 item2, ok := subDict.Get("Item2").(*PdfObjectBool) 438 if !ok || *item2 != true { 439 t.Errorf("Invalid bool item") 440 } 441 realnum, ok := subDict.Get("Item1").(*PdfObjectFloat) 442 if !ok || *realnum != 0.4 { 443 t.Errorf("Invalid real number") 444 } 445 } 446 447 func TestDictParsing3(t *testing.T) { 448 rawText := "<<>>" 449 450 parser := PdfParser{} 451 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 452 dict, err := parser.ParseDict() 453 if err != nil { 454 t.Errorf("Error parsing dict") 455 } 456 457 if len(dict.Keys()) != 0 { 458 t.Errorf("Length of dict != 0") 459 } 460 } 461 462 /* 463 func TestDictParsing4(t *testing.T) { 464 rawText := "<</Key>>" 465 466 parser := PdfParser{} 467 parser.rs, parser.reader = makeReaderForText(rawText) 468 dict, err := parser.ParseDict() 469 if err != nil { 470 t.Errorf("Error parsing dict (%s)", err) 471 return 472 } 473 474 if len(*dict) != 1 { 475 t.Errorf("Length of dict != 1") 476 return 477 } 478 479 _, ok := (*dict)["Key"].(*PdfObjectNull) 480 if !ok { 481 t.Errorf("Invalid object (should be PDF null)") 482 return 483 } 484 } 485 */ 486 487 func TestArrayParsing(t *testing.T) { 488 // 7.3.7. 489 } 490 491 func TestReferenceParsing(t *testing.T) { 492 // TODO 493 } 494 495 func TestNullParsing(t *testing.T) { 496 // TODO 497 } 498 499 func TestStreamParsing(t *testing.T) { 500 // TODO 501 } 502 503 func TestIndirectObjParsing1(t *testing.T) { 504 rawText := `1 0 obj 505 << 506 /Names 2 0 R 507 /Pages 3 0 R 508 /Metadata 4 0 R 509 /ViewerPreferences 510 << 511 /Rights 512 << 513 /Document [/FullSave] 514 /TimeOfUbiquitization (D:20071210131309Z) 515 /RightsID [(x\\Ä-z<80><83>ã[W< b<99>\rhvèC©ðFüE^TN£^\jó]ç=çø\n<8f>:˹\(<9a>\r=§^\~CÌÁxîÚð^V/=Î|Q\r<99>¢ ) (#$ÐJ^C<98>^ZX<86>^TÞ¿ø¸^N]ú<8f>^N×2<9f>§ø±D^Q\r!'¡<8a>dp°,l¿<9d>É<82>«eæ§B}«Ç8p·<97>\fl¿²G/x¹>) (kc2²µ^?-©¸þ$åiØ.Aé7^P½ÒÏð^S^^Y×rùç^O̵¶¿Hp^?*NËwóúËo§ü1ª<97>îFÜ\\<8f>OÚ^P[¸<93>0^)] 516 /Version 1 517 /Msg (This form has document rights applied to it. These rights allow anyone completing this form, with the free Adobe Reader, to save their filled-in form locally.) 518 /Form [/Import /Export /SubmitStandalone /SpawnTemplate] 519 >> 520 >> 521 /AcroForm 5 0 R 522 /Type /Catalog 523 >> 524 endobj 525 3 0 obj 526 ` 527 parser := PdfParser{} 528 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 529 530 obj, err := parser.ParseIndirectObject() 531 if err != nil { 532 t.Errorf("Failed to parse indirect obj (%s)", err) 533 return 534 } 535 536 common.Log.Debug("Parsed obj: %s", obj) 537 } 538 539 // Test /Prev and xref tables. Check if the priority order is right. 540 // Test recovering xref tables. Refactor to recovery.go ? 541 542 func TestXrefStreamParse(t *testing.T) { 543 rawText := `99 0 obj 544 << /Type /XRef 545 /Index [0 5] 546 /W [1 2 2] 547 /Filter /ASCIIHexDecode 548 /Size 5 549 /Length 65 550 >> 551 stream 552 00 0000 FFFF 553 02 000F 0000 554 02 000F 0001 555 02 000F 0002 556 01 BA5E 0000> 557 endstream 558 endobj` 559 parser := PdfParser{} 560 parser.xrefs = make(XrefTable) 561 parser.objstms = make(ObjectStreams) 562 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 563 564 xrefDict, err := parser.parseXrefStream(nil) 565 if err != nil { 566 t.Errorf("Invalid xref stream object (%s)", err) 567 return 568 } 569 570 typeName, ok := xrefDict.Get("Type").(*PdfObjectName) 571 if !ok || *typeName != "XRef" { 572 t.Errorf("Invalid Type != XRef") 573 return 574 } 575 576 if len(parser.xrefs) != 4 { 577 t.Errorf("Wrong length (%d)", len(parser.xrefs)) 578 return 579 } 580 581 if parser.xrefs[3].xtype != XREF_OBJECT_STREAM { 582 t.Errorf("Invalid type") 583 return 584 } 585 if parser.xrefs[3].osObjNumber != 15 { 586 t.Errorf("Wrong object stream obj number") 587 return 588 } 589 if parser.xrefs[3].osObjIndex != 2 { 590 t.Errorf("Wrong object stream obj index") 591 return 592 } 593 594 common.Log.Debug("Xref dict: %s", xrefDict) 595 } 596 597 func TestObjectParse(t *testing.T) { 598 parser := PdfParser{} 599 600 // Test object detection. 601 // Invalid object type. 602 rawText := " \t9 0 false" 603 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 604 obj, err := parser.parseObject() 605 if err != nil { 606 t.Error("Should ignore tab/space") 607 return 608 } 609 610 // Integer 611 rawText = "9 0 false" 612 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 613 obj, err = parser.parseObject() 614 615 if err != nil { 616 t.Errorf("Error parsing object") 617 return 618 } 619 nump, ok := obj.(*PdfObjectInteger) 620 if !ok { 621 t.Errorf("Unable to identify integer") 622 return 623 } 624 if *nump != 9 { 625 t.Errorf("Wrong value, expecting 9 (%d)", *nump) 626 return 627 } 628 629 // Reference 630 rawText = "9 0 R false" 631 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 632 obj, err = parser.parseObject() 633 if err != nil { 634 t.Errorf("Error parsing object") 635 return 636 } 637 refp, ok := obj.(*PdfObjectReference) 638 if !ok { 639 t.Errorf("Unable to identify reference") 640 return 641 } 642 if (*refp).ObjectNumber != 9 { 643 t.Errorf("Wrong value, expecting object number 9") 644 return 645 } 646 647 // Reference 648 rawText = "909 0 R false" 649 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 650 obj, err = parser.parseObject() 651 if err != nil { 652 t.Errorf("Error parsing object") 653 return 654 } 655 refp, ok = obj.(*PdfObjectReference) 656 if !ok { 657 t.Errorf("Unable to identify reference") 658 return 659 } 660 if (*refp).ObjectNumber != 909 { 661 t.Errorf("Wrong value, expecting object number 9") 662 return 663 } 664 665 // Bool 666 rawText = "false 9 0 R" 667 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 668 obj, err = parser.parseObject() 669 if err != nil { 670 t.Errorf("Error parsing object") 671 return 672 } 673 boolp, ok := obj.(*PdfObjectBool) 674 if !ok { 675 t.Errorf("Unable to identify bool object") 676 return 677 } 678 if *boolp != false { 679 t.Errorf("Wrong value, expecting false") 680 return 681 } 682 } 683 684 /* 685 var file1 = "../testfiles/minimal.pdf" 686 687 func TestMinimalPDFFile(t *testing.T) { 688 file, err := os.Open(file1) 689 if err != nil { 690 t.Errorf("Unable to open minimal test file (%s)", err) 691 return 692 } 693 defer file.Close() 694 695 reader, err := NewPdfReader(file) 696 if err != nil { 697 t.Errorf("Unable to read test file (%s)", err) 698 return 699 } 700 701 numPages, err := reader.GetNumPages() 702 if err != nil { 703 t.Error("Unable to get number of pages") 704 } 705 706 fmt.Printf("Num pages: %d\n", numPages) 707 if numPages != 1 { 708 t.Error("Wrong number of pages") 709 } 710 711 parser := reader.parser 712 if len(parser.xrefs) != 4 { 713 t.Errorf("Wrong number of xrefs %d != 4", len(parser.xrefs)) 714 } 715 716 if parser.xrefs[1].objectNumber != 1 { 717 t.Errorf("Invalid xref0 object number != 1 (%d)", parser.xrefs[0].objectNumber) 718 } 719 if parser.xrefs[1].offset != 18 { 720 t.Errorf("Invalid offset != 18 (%d)", parser.xrefs[0].offset) 721 } 722 if parser.xrefs[1].xtype != XREF_TABLE_ENTRY { 723 t.Errorf("Invalid xref type") 724 } 725 if parser.xrefs[3].objectNumber != 3 { 726 t.Errorf("Invalid xref object number != 3 (%d)", parser.xrefs[2].objectNumber) 727 } 728 if parser.xrefs[3].offset != 178 { 729 t.Errorf("Invalid offset != 178") 730 } 731 if parser.xrefs[3].xtype != XREF_TABLE_ENTRY { 732 t.Errorf("Invalid xref type") 733 } 734 735 // Check catalog object. 736 catalogObj, err := parser.LookupByNumber(1) 737 if err != nil { 738 t.Error("Unable to look up catalog object") 739 } 740 catalog, ok := catalogObj.(*PdfIndirectObject) 741 if !ok { 742 t.Error("Unable to look up catalog object") 743 } 744 catalogDict, ok := catalog.PdfObject.(*PdfObjectDictionary) 745 if !ok { 746 t.Error("Unable to find dictionary") 747 } 748 typename, ok := (*catalogDict)["Type"].(*PdfObjectName) 749 if !ok { 750 t.Error("Unable to check type") 751 } 752 if *typename != "Catalog" { 753 t.Error("Wrong type name (%s != Catalog)", *typename) 754 } 755 756 // Check Page object. 757 pageObj, err := parser.LookupByNumber(3) 758 if err != nil { 759 t.Error("Unable to look up Page") 760 } 761 page, ok := pageObj.(*PdfIndirectObject) 762 if !ok { 763 t.Error("Unable to look up Page") 764 } 765 pageDict, ok := page.PdfObject.(*PdfObjectDictionary) 766 if !ok { 767 t.Error("Unable to load Page dictionary") 768 } 769 if len(*pageDict) != 4 { 770 t.Error("Page dict should have 4 objects (%d)", len(*pageDict)) 771 } 772 resourcesDict, ok := (*pageDict)["Resources"].(*PdfObjectDictionary) 773 if !ok { 774 t.Error("Unable to load Resources dictionary") 775 } 776 if len(*resourcesDict) != 1 { 777 t.Error("Page Resources dict should have 1 member (%d)", len(*resourcesDict)) 778 } 779 fontDict, ok := (*resourcesDict)["Font"].(*PdfObjectDictionary) 780 if !ok { 781 t.Error("Unable to load font") 782 } 783 f1Dict, ok := (*fontDict)["F1"].(*PdfObjectDictionary) 784 if !ok { 785 t.Error("Unable to load F1 dict") 786 } 787 if len(*f1Dict) != 3 { 788 t.Error("Invalid F1 dict length 3 != %d", len(*f1Dict)) 789 } 790 baseFont, ok := (*f1Dict)["BaseFont"].(*PdfObjectName) 791 if !ok { 792 t.Error("Unable to load base font") 793 } 794 if *baseFont != "Times-Roman" { 795 t.Error("Invalid base font (should be Times-Roman not %s)", *baseFont) 796 } 797 } 798 */