diff --git a/pdf/core/ccittfaxdecode/codes.go b/pdf/core/ccittfaxdecode/codes.go new file mode 100644 index 00000000..099591dd --- /dev/null +++ b/pdf/core/ccittfaxdecode/codes.go @@ -0,0 +1,1074 @@ +package ccittfaxdecode + +var ( + BTerms map[int]Code + WTerms map[int]Code + BMakeups map[int]Code + WMakeups map[int]Code + CommonMakeups map[int]Code + masks map[int]byte + + EOL = Code{ + Code: 1 << 4, + BitsWritten: 12, + } + + EOL1 = Code{ + Code: 3 << 3, + BitsWritten: 13, + } + + EOL0 = Code{ + Code: 2 << 3, + BitsWritten: 13, + } + + P = Code{ + Code: 1 << 12, + BitsWritten: 4, + } + + H = Code{ + Code: 1 << 13, + BitsWritten: 3, + } + + V0 = Code{ + Code: 1 << 15, + BitsWritten: 1, + } + + V1R = Code{ + Code: 3 << 13, + BitsWritten: 3, + } + + V2R = Code{ + Code: 3 << 10, + BitsWritten: 6, + } + + V3R = Code{ + Code: 3 << 9, + BitsWritten: 7, + } + + V1L = Code{ + Code: 2 << 13, + BitsWritten: 3, + } + + V2L = Code{ + Code: 2 << 10, + BitsWritten: 6, + } + + V3L = Code{ + Code: 2 << 9, + BitsWritten: 7, + } +) + +type Code struct { + Code uint16 + BitsWritten int +} + +func init() { + BTerms = make(map[int]Code) + + BTerms[0] = Code{ + Code: 13<<8 | 3<<6, + BitsWritten: 10, + } + + BTerms[1] = Code{ + Code: 2 << (5 + 8), + BitsWritten: 3, + } + + BTerms[2] = Code{ + Code: 3 << (6 + 8), + BitsWritten: 2, + } + + BTerms[3] = Code{ + Code: 2 << (6 + 8), + BitsWritten: 2, + } + + BTerms[4] = Code{ + Code: 3 << (5 + 8), + BitsWritten: 3, + } + + BTerms[5] = Code{ + Code: 3 << (4 + 8), + BitsWritten: 4, + } + + BTerms[6] = Code{ + Code: 2 << (4 + 8), + BitsWritten: 4, + } + + BTerms[7] = Code{ + Code: 3 << (3 + 8), + BitsWritten: 5, + } + + BTerms[8] = Code{ + Code: 5 << (2 + 8), + BitsWritten: 6, + } + + BTerms[9] = Code{ + Code: 4 << (2 + 8), + BitsWritten: 6, + } + + BTerms[10] = Code{ + Code: 4 << (1 + 8), + BitsWritten: 7, + } + + BTerms[11] = Code{ + Code: 5 << (1 + 8), + BitsWritten: 7, + } + + BTerms[12] = Code{ + Code: 7 << (1 + 8), + BitsWritten: 7, + } + + BTerms[13] = Code{ + Code: 4 << 8, + BitsWritten: 8, + } + + BTerms[14] = Code{ + Code: 7 << 8, + BitsWritten: 8, + } + + BTerms[15] = Code{ + Code: 12 << 8, + BitsWritten: 9, + } + + BTerms[16] = Code{ + Code: 5<<8 | 3<<6, + BitsWritten: 10, + } + + BTerms[17] = Code{ + Code: 6 << 8, + BitsWritten: 10, + } + + BTerms[18] = Code{ + Code: 2 << 8, + BitsWritten: 10, + } + + BTerms[19] = Code{ + Code: 12<<8 | 7<<5, + BitsWritten: 11, + } + + BTerms[20] = Code{ + Code: 13 << 8, + BitsWritten: 11, + } + + BTerms[21] = Code{ + Code: 13<<8 | 4<<5, + BitsWritten: 11, + } + + BTerms[22] = Code{ + Code: 6<<8 | 7<<5, + BitsWritten: 11, + } + + BTerms[23] = Code{ + Code: 5 << 8, + BitsWritten: 11, + } + + BTerms[24] = Code{ + Code: 2<<8 | 7<<5, + BitsWritten: 11, + } + + BTerms[25] = Code{ + Code: 3 << 8, + BitsWritten: 11, + } + + BTerms[26] = Code{ + Code: 12<<8 | 10<<4, + BitsWritten: 12, + } + + BTerms[27] = Code{ + Code: 12<<8 | 11<<4, + BitsWritten: 12, + } + + BTerms[28] = Code{ + Code: 12<<8 | 12<<4, + BitsWritten: 12, + } + + BTerms[29] = Code{ + Code: 12<<8 | 13<<4, + BitsWritten: 12, + } + + BTerms[30] = Code{ + Code: 6<<8 | 8<<4, + BitsWritten: 12, + } + + BTerms[31] = Code{ + Code: 6<<8 | 9<<4, + BitsWritten: 12, + } + + BTerms[32] = Code{ + Code: 6<<8 | 10<<4, + BitsWritten: 12, + } + + BTerms[33] = Code{ + Code: 6<<8 | 11<<4, + BitsWritten: 12, + } + + BTerms[34] = Code{ + Code: 13<<8 | 2<<4, + BitsWritten: 12, + } + + BTerms[35] = Code{ + Code: 13<<8 | 3<<4, + BitsWritten: 12, + } + + BTerms[36] = Code{ + Code: 13<<8 | 4<<4, + BitsWritten: 12, + } + + BTerms[37] = Code{ + Code: 13<<8 | 5<<4, + BitsWritten: 12, + } + + BTerms[38] = Code{ + Code: 13<<8 | 6<<4, + BitsWritten: 12, + } + + BTerms[39] = Code{ + Code: 13<<8 | 7<<4, + BitsWritten: 12, + } + + BTerms[40] = Code{ + Code: 6<<8 | 12<<4, + BitsWritten: 12, + } + + BTerms[41] = Code{ + Code: 6<<8 | 13<<4, + BitsWritten: 12, + } + + BTerms[42] = Code{ + Code: 13<<8 | 10<<4, + BitsWritten: 12, + } + + BTerms[43] = Code{ + Code: 13<<8 | 11<<4, + BitsWritten: 12, + } + + BTerms[44] = Code{ + Code: 5<<8 | 4<<4, + BitsWritten: 12, + } + + BTerms[45] = Code{ + Code: 5<<8 | 5<<4, + BitsWritten: 12, + } + + BTerms[46] = Code{ + Code: 5<<8 | 6<<4, + BitsWritten: 12, + } + + BTerms[47] = Code{ + Code: 5<<8 | 7<<4, + BitsWritten: 12, + } + + BTerms[48] = Code{ + Code: 6<<8 | 4<<4, + BitsWritten: 12, + } + + BTerms[49] = Code{ + Code: 6<<8 | 5<<4, + BitsWritten: 12, + } + + BTerms[50] = Code{ + Code: 5<<8 | 2<<4, + BitsWritten: 12, + } + + BTerms[51] = Code{ + Code: 5<<8 | 3<<4, + BitsWritten: 12, + } + + BTerms[52] = Code{ + Code: 2<<8 | 4<<4, + BitsWritten: 12, + } + + BTerms[53] = Code{ + Code: 3<<8 | 7<<4, + BitsWritten: 12, + } + + BTerms[54] = Code{ + Code: 3<<8 | 8<<4, + BitsWritten: 12, + } + + BTerms[55] = Code{ + Code: 2<<8 | 7<<4, + BitsWritten: 12, + } + + BTerms[56] = Code{ + Code: 2<<8 | 8<<4, + BitsWritten: 12, + } + + BTerms[57] = Code{ + Code: 5<<8 | 8<<4, + BitsWritten: 12, + } + + BTerms[58] = Code{ + Code: 5<<8 | 9<<4, + BitsWritten: 12, + } + + BTerms[59] = Code{ + Code: 2<<8 | 11<<4, + BitsWritten: 12, + } + + BTerms[60] = Code{ + Code: 2<<8 | 12<<4, + BitsWritten: 12, + } + + BTerms[61] = Code{ + Code: 5<<8 | 10<<4, + BitsWritten: 12, + } + + BTerms[62] = Code{ + Code: 6<<8 | 6<<4, + BitsWritten: 12, + } + + BTerms[63] = Code{ + Code: 6<<8 | 7<<4, + BitsWritten: 12, + } + + WTerms = make(map[int]Code) + + WTerms[0] = Code{ + Code: 53 << 8, + BitsWritten: 8, + } + + WTerms[1] = Code{ + Code: 7 << (2 + 8), + BitsWritten: 6, + } + + WTerms[2] = Code{ + Code: 7 << (4 + 8), + BitsWritten: 4, + } + + WTerms[3] = Code{ + Code: 8 << (4 + 8), + BitsWritten: 4, + } + + WTerms[4] = Code{ + Code: 11 << (4 + 8), + BitsWritten: 4, + } + + WTerms[5] = Code{ + Code: 12 << (4 + 8), + BitsWritten: 4, + } + + WTerms[6] = Code{ + Code: 14 << (4 + 8), + BitsWritten: 4, + } + + WTerms[7] = Code{ + Code: 15 << (4 + 8), + BitsWritten: 4, + } + + WTerms[8] = Code{ + Code: 19 << (3 + 8), + BitsWritten: 5, + } + + WTerms[9] = Code{ + Code: 20 << (3 + 8), + BitsWritten: 5, + } + + WTerms[10] = Code{ + Code: 7 << (3 + 8), + BitsWritten: 5, + } + + WTerms[11] = Code{ + Code: 8 << (3 + 8), + BitsWritten: 5, + } + + WTerms[12] = Code{ + Code: 8 << (2 + 8), + BitsWritten: 6, + } + + WTerms[13] = Code{ + Code: 3 << (2 + 8), + BitsWritten: 6, + } + + WTerms[14] = Code{ + Code: 52 << (2 + 8), + BitsWritten: 6, + } + + WTerms[15] = Code{ + Code: 53 << (2 + 8), + BitsWritten: 6, + } + + WTerms[16] = Code{ + Code: 42 << (2 + 8), + BitsWritten: 6, + } + + WTerms[17] = Code{ + Code: 43 << (2 + 8), + BitsWritten: 6, + } + + WTerms[18] = Code{ + Code: 39 << (1 + 8), + BitsWritten: 7, + } + + WTerms[19] = Code{ + Code: 12 << (1 + 8), + BitsWritten: 7, + } + + WTerms[20] = Code{ + Code: 8 << (1 + 8), + BitsWritten: 7, + } + + WTerms[21] = Code{ + Code: 23 << (1 + 8), + BitsWritten: 7, + } + + WTerms[22] = Code{ + Code: 3 << (1 + 8), + BitsWritten: 7, + } + + WTerms[23] = Code{ + Code: 4 << (1 + 8), + BitsWritten: 7, + } + + WTerms[24] = Code{ + Code: 40 << (1 + 8), + BitsWritten: 7, + } + + WTerms[25] = Code{ + Code: 43 << (1 + 8), + BitsWritten: 7, + } + + WTerms[26] = Code{ + Code: 19 << (1 + 8), + BitsWritten: 7, + } + + WTerms[27] = Code{ + Code: 36 << (1 + 8), + BitsWritten: 7, + } + + WTerms[28] = Code{ + Code: 24 << (1 + 8), + BitsWritten: 7, + } + + WTerms[29] = Code{ + Code: 2 << 8, + BitsWritten: 8, + } + + WTerms[30] = Code{ + Code: 3 << 8, + BitsWritten: 8, + } + + WTerms[31] = Code{ + Code: 26 << 8, + BitsWritten: 8, + } + + WTerms[32] = Code{ + Code: 27 << 8, + BitsWritten: 8, + } + + WTerms[33] = Code{ + Code: 18 << 8, + BitsWritten: 8, + } + + WTerms[34] = Code{ + Code: 19 << 8, + BitsWritten: 8, + } + + WTerms[35] = Code{ + Code: 20 << 8, + BitsWritten: 8, + } + + WTerms[36] = Code{ + Code: 21 << 8, + BitsWritten: 8, + } + + WTerms[37] = Code{ + Code: 22 << 8, + BitsWritten: 8, + } + + WTerms[38] = Code{ + Code: 23 << 8, + BitsWritten: 8, + } + + WTerms[39] = Code{ + Code: 40 << 8, + BitsWritten: 8, + } + + WTerms[40] = Code{ + Code: 41 << 8, + BitsWritten: 8, + } + + WTerms[41] = Code{ + Code: 42 << 8, + BitsWritten: 8, + } + + WTerms[42] = Code{ + Code: 43 << 8, + BitsWritten: 8, + } + + WTerms[43] = Code{ + Code: 44 << 8, + BitsWritten: 8, + } + + WTerms[44] = Code{ + Code: 45 << 8, + BitsWritten: 8, + } + + WTerms[45] = Code{ + Code: 4 << 8, + BitsWritten: 8, + } + + WTerms[46] = Code{ + Code: 5 << 8, + BitsWritten: 8, + } + + WTerms[47] = Code{ + Code: 10 << 8, + BitsWritten: 8, + } + + WTerms[48] = Code{ + Code: 11 << 8, + BitsWritten: 8, + } + + WTerms[49] = Code{ + Code: 82 << 8, + BitsWritten: 8, + } + + WTerms[50] = Code{ + Code: 83 << 8, + BitsWritten: 8, + } + + WTerms[51] = Code{ + Code: 84 << 8, + BitsWritten: 8, + } + + WTerms[52] = Code{ + Code: 85 << 8, + BitsWritten: 8, + } + + WTerms[53] = Code{ + Code: 36 << 8, + BitsWritten: 8, + } + + WTerms[54] = Code{ + Code: 37 << 8, + BitsWritten: 8, + } + + WTerms[55] = Code{ + Code: 88 << 8, + BitsWritten: 8, + } + + WTerms[56] = Code{ + Code: 89 << 8, + BitsWritten: 8, + } + + WTerms[57] = Code{ + Code: 90 << 8, + BitsWritten: 8, + } + + WTerms[58] = Code{ + Code: 91 << 8, + BitsWritten: 8, + } + + WTerms[59] = Code{ + Code: 74 << 8, + BitsWritten: 8, + } + + WTerms[60] = Code{ + Code: 75 << 8, + BitsWritten: 8, + } + + WTerms[61] = Code{ + Code: 50 << 8, + BitsWritten: 8, + } + + WTerms[62] = Code{ + Code: 51 << 8, + BitsWritten: 8, + } + + WTerms[63] = Code{ + Code: 52 << 8, + BitsWritten: 8, + } + + BMakeups = make(map[int]Code) + + BMakeups[64] = Code{ + Code: 3<<8 | 3<<6, + BitsWritten: 10, + } + + BMakeups[128] = Code{ + Code: 12<<8 | 8<<4, + BitsWritten: 12, + } + + BMakeups[192] = Code{ + Code: 12<<8 | 9<<4, + BitsWritten: 12, + } + + BMakeups[256] = Code{ + Code: 5<<8 | 11<<4, + BitsWritten: 12, + } + + BMakeups[320] = Code{ + Code: 3<<8 | 3<<4, + BitsWritten: 12, + } + + BMakeups[384] = Code{ + Code: 3<<8 | 4<<4, + BitsWritten: 12, + } + + BMakeups[448] = Code{ + Code: 3<<8 | 5<<4, + BitsWritten: 12, + } + + BMakeups[512] = Code{ + Code: 3<<8 | 12<<3, + BitsWritten: 13, + } + + BMakeups[576] = Code{ + Code: 3<<8 | 13<<3, + BitsWritten: 13, + } + + BMakeups[640] = Code{ + Code: 2<<8 | 10<<3, + BitsWritten: 13, + } + + BMakeups[704] = Code{ + Code: 2<<8 | 11<<3, + BitsWritten: 13, + } + + BMakeups[768] = Code{ + Code: 2<<8 | 12<<3, + BitsWritten: 13, + } + + BMakeups[832] = Code{ + Code: 2<<8 | 13<<3, + BitsWritten: 13, + } + + BMakeups[896] = Code{ + Code: 3<<8 | 18<<3, + BitsWritten: 13, + } + + BMakeups[960] = Code{ + Code: 3<<8 | 19<<3, + BitsWritten: 13, + } + + BMakeups[1024] = Code{ + Code: 3<<8 | 20<<3, + BitsWritten: 13, + } + + BMakeups[1088] = Code{ + Code: 3<<8 | 21<<3, + BitsWritten: 13, + } + + BMakeups[1152] = Code{ + Code: 3<<8 | 22<<3, + BitsWritten: 13, + } + + BMakeups[1216] = Code{ + Code: 119 << 3, + BitsWritten: 13, + } + + BMakeups[1280] = Code{ + Code: 2<<8 | 18<<3, + BitsWritten: 13, + } + + BMakeups[1344] = Code{ + Code: 2<<8 | 19<<3, + BitsWritten: 13, + } + + BMakeups[1408] = Code{ + Code: 2<<8 | 20<<3, + BitsWritten: 13, + } + + BMakeups[1472] = Code{ + Code: 2<<8 | 21<<3, + BitsWritten: 13, + } + + BMakeups[1536] = Code{ + Code: 2<<8 | 26<<3, + BitsWritten: 13, + } + + BMakeups[1600] = Code{ + Code: 2<<8 | 27<<3, + BitsWritten: 13, + } + + BMakeups[1664] = Code{ + Code: 3<<8 | 4<<3, + BitsWritten: 13, + } + + BMakeups[1728] = Code{ + Code: 3<<8 | 5<<3, + BitsWritten: 13, + } + + WMakeups = make(map[int]Code) + + WMakeups[64] = Code{ + Code: 27 << (3 + 8), + BitsWritten: 5, + } + + WMakeups[128] = Code{ + Code: 18 << (3 + 8), + BitsWritten: 5, + } + + WMakeups[192] = Code{ + Code: 23 << (2 + 8), + BitsWritten: 6, + } + + WMakeups[256] = Code{ + Code: 55 << (1 + 8), + BitsWritten: 7, + } + + WMakeups[320] = Code{ + Code: 54 << 8, + BitsWritten: 8, + } + + WMakeups[384] = Code{ + Code: 55 << 8, + BitsWritten: 8, + } + + WMakeups[448] = Code{ + Code: 100 << 8, + BitsWritten: 8, + } + + WMakeups[512] = Code{ + Code: 101 << 8, + BitsWritten: 8, + } + + WMakeups[576] = Code{ + Code: 104 << 8, + BitsWritten: 8, + } + + WMakeups[640] = Code{ + Code: 103 << 8, + BitsWritten: 8, + } + + WMakeups[704] = Code{ + Code: 102 << 8, + BitsWritten: 9, + } + + WMakeups[768] = Code{ + Code: 102<<8 | 1<<7, + BitsWritten: 9, + } + + WMakeups[832] = Code{ + Code: 105 << 8, + BitsWritten: 9, + } + + WMakeups[896] = Code{ + Code: 105<<8 | 1<<7, + BitsWritten: 9, + } + + WMakeups[960] = Code{ + Code: 106 << 8, + BitsWritten: 9, + } + + WMakeups[1024] = Code{ + Code: 106<<8 | 1<<7, + BitsWritten: 9, + } + + WMakeups[1088] = Code{ + Code: 107 << 8, + BitsWritten: 9, + } + + WMakeups[1152] = Code{ + Code: 107<<8 | 1<<7, + BitsWritten: 9, + } + + WMakeups[1216] = Code{ + Code: 108 << 8, + BitsWritten: 9, + } + + WMakeups[1280] = Code{ + Code: 108<<8 | 1<<7, + BitsWritten: 9, + } + + WMakeups[1344] = Code{ + Code: 109 << 8, + BitsWritten: 9, + } + + WMakeups[1408] = Code{ + Code: 109<<8 | 1<<7, + BitsWritten: 9, + } + + WMakeups[1472] = Code{ + Code: 76 << 8, + BitsWritten: 9, + } + + WMakeups[1536] = Code{ + Code: 76<<8 | 1<<7, + BitsWritten: 9, + } + + WMakeups[1600] = Code{ + Code: 77 << 8, + BitsWritten: 9, + } + + WMakeups[1664] = Code{ + Code: 24 << (2 + 8), + BitsWritten: 6, + } + + WMakeups[1728] = Code{ + Code: 77<<8 | 1<<7, + BitsWritten: 9, + } + + CommonMakeups = make(map[int]Code) + + CommonMakeups[1792] = Code{ + Code: 1 << 8, + BitsWritten: 11, + } + + CommonMakeups[1856] = Code{ + Code: 1<<8 | 4<<5, + BitsWritten: 11, + } + + CommonMakeups[1920] = Code{ + Code: 1<<8 | 5<<5, + BitsWritten: 11, + } + + CommonMakeups[1984] = Code{ + Code: 1<<8 | 2<<4, + BitsWritten: 12, + } + + CommonMakeups[2048] = Code{ + Code: 1<<8 | 3<<4, + BitsWritten: 12, + } + + CommonMakeups[2112] = Code{ + Code: 1<<8 | 4<<4, + BitsWritten: 12, + } + + CommonMakeups[2176] = Code{ + Code: 1<<8 | 5<<4, + BitsWritten: 12, + } + + CommonMakeups[2240] = Code{ + Code: 1<<8 | 6<<4, + BitsWritten: 12, + } + + CommonMakeups[2304] = Code{ + Code: 1<<8 | 7<<4, + BitsWritten: 12, + } + + CommonMakeups[2368] = Code{ + Code: 1<<8 | 12<<4, + BitsWritten: 12, + } + + CommonMakeups[2432] = Code{ + Code: 1<<8 | 13<<4, + BitsWritten: 12, + } + + CommonMakeups[2496] = Code{ + Code: 1<<8 | 14<<4, + BitsWritten: 12, + } + + CommonMakeups[2560] = Code{ + Code: 1<<8 | 15<<4, + BitsWritten: 12, + } + + masks = make(map[int]byte) + + masks[0] = 0xFF + masks[1] = 0xFE + masks[2] = 0xFC + masks[3] = 0xF8 + masks[4] = 0xF0 + masks[5] = 0xE0 + masks[6] = 0xC0 + masks[7] = 0x80 + masks[8] = 0x00 +} \ No newline at end of file diff --git a/pdf/core/ccittfaxdecode/decode.go b/pdf/core/ccittfaxdecode/decode.go new file mode 100644 index 00000000..d7f49033 --- /dev/null +++ b/pdf/core/ccittfaxdecode/decode.go @@ -0,0 +1,733 @@ +package ccittfaxdecode + +import ( + "errors" + "image" + "image/png" + "log" + "os" +) + +var ( + whiteTree = &decodingTreeNode{ + Val: 255, + } + blackTree = &decodingTreeNode{ + Val: 255, + } + twoDimTree = &decodingTreeNode{ + Val: 255, + } +) + +func init() { + for runLen, code := range WTerms { + addNode(whiteTree, code, 0, runLen) + } + + for runLen, code := range WMakeups { + addNode(whiteTree, code, 0, runLen) + } + + for runLen, code := range BTerms { + addNode(blackTree, code, 0, runLen) + } + + for runLen, code := range BMakeups { + addNode(blackTree, code, 0, runLen) + } + + for runLen, code := range CommonMakeups { + addNode(whiteTree, code, 0, runLen) + addNode(blackTree, code, 0, runLen) + } + + addNode(twoDimTree, P, 0, 0) + addNode(twoDimTree, H, 0, 0) + addNode(twoDimTree, V0, 0, 0) + addNode(twoDimTree, V1R, 0, 0) + addNode(twoDimTree, V2R, 0, 0) + addNode(twoDimTree, V3R, 0, 0) + addNode(twoDimTree, V1L, 0, 0) + addNode(twoDimTree, V2L, 0, 0) + addNode(twoDimTree, V3L, 0, 0) +} + +func (e *Encoder) Decode(encoded []byte) ([][]byte, error) { + if e.BlackIs1 { + white = 0 + black = 1 + } else { + white = 1 + black = 0 + } + + if e.K == 0 { + return e.decodeG31D(encoded) + } + + if e.K > 0 { + return e.decodeG32D(encoded) + } + + if e.K < 4 { + return e.decodeG4(encoded) + } + + return nil, nil +} + +func (e *Encoder) decodeG31D(encoded []byte) ([][]byte, error) { + var pixels [][]byte + + // do g31d decoding + var bitPos int + for (bitPos / 8) < len(encoded) { + var gotEOL bool + + gotEOL, bitPos = tryFetchEOL(encoded, bitPos) + if !gotEOL { + if e.EndOfLine { + return nil, errors.New("no EOL found while the EndOfLine parameter is true") + } + } else { + // 5 EOLs left to fill RTC + for i := 0; i < 5; i++ { + gotEOL, bitPos = tryFetchEOL(encoded, bitPos) + + if !gotEOL { + if i == 0 { + break + } + + return nil, errors.New("invalid EOL") + } + } + + // got RTC + if gotEOL { + break + } + } + + var row []byte + row, bitPos = e.decodeRow1D(encoded, bitPos) + + if e.EncodedByteAlign && bitPos%8 != 0 { + bitPos += 8 - bitPos%8 + } + + pixels = append(pixels, row) + + if e.Rows > 0 && !e.EndOfBlock && len(pixels) >= e.Rows { + break + } + } + + //saveToImage(pixels, "/home/darkrengarius/Downloads/yeatAnotherTest.png") + + return pixels, nil +} + +func saveToImage(pixels [][]byte, path string) error { + img := image.NewRGBA(image.Rect(0, 0, len(pixels[0]), len(pixels))) + + imgPix := 0 + for i := range pixels { + for j := range pixels[i] { + img.Pix[imgPix] = 255 * pixels[i][j] + img.Pix[imgPix+1] = 255 * pixels[i][j] + img.Pix[imgPix+2] = 255 * pixels[i][j] + img.Pix[imgPix+3] = 255 + + imgPix += 4 + } + } + + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + err = png.Encode(f, img) + if err != nil { + return err + } + + return nil +} + +func (e *Encoder) decodeG32D(encoded []byte) ([][]byte, error) { + var ( + pixels [][]byte + bitPos int + err error + ) +byteLoop: + for (bitPos / 8) < len(encoded) { + var gotEOL bool + gotEOL, bitPos, err = tryFetchRTC2D(encoded, bitPos) + if err != nil { + return nil, err + } + + if gotEOL { + break + } + + gotEOL, bitPos = tryFetchEOL1(encoded, bitPos) + + if !gotEOL { + if e.EndOfLine { + return nil, errors.New("no EOL found while the EndOfLine parameter is true") + } + } + + // decode 1 of K rows as 1D + var row []byte + row, bitPos = e.decodeRow1D(encoded, bitPos) + + if e.EncodedByteAlign && bitPos%8 != 0 { + bitPos += 8 - bitPos%8 + } + + if row != nil { + pixels = append(pixels, row) + } + + if e.Rows > 0 && !e.EndOfBlock && len(pixels) >= e.Rows { + break + } + + // decode K-1 rows as 2D + for i := 1; i < e.K && (bitPos/8) < len(encoded); i++ { + gotEOL, bitPos = tryFetchEOL0(encoded, bitPos) + if !gotEOL { + gotEOL, bitPos, err = tryFetchRTC2D(encoded, bitPos) + if err != nil { + return nil, err + } + + if gotEOL { + break byteLoop + } else { + if e.EndOfLine { + return nil, errors.New("no EOL found while the EndOfLine parameter is true") + } + } + } + + var ( + twoDimCode Code + ok bool + ) + + isWhite := true + var pixelsRow []byte + a0 := -1 + for twoDimCode, bitPos, ok = fetchNext2DCode(encoded, bitPos); ok; twoDimCode, bitPos, ok = fetchNext2DCode(encoded, bitPos) { + switch twoDimCode { + case P: + // do pass mode decoding + pixelsRow, a0 = decodePassMode(pixels, pixelsRow, isWhite, a0) + case H: + // do horizontal mode decoding + pixelsRow, bitPos, a0, err = decodeHorizontalMode(encoded, pixelsRow, bitPos, isWhite, a0) + if err != nil { + return nil, err + } + case V0: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, 0) + + isWhite = !isWhite + case V1R: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, 1) + + isWhite = !isWhite + case V2R: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, 2) + + isWhite = !isWhite + case V3R: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, 3) + + isWhite = !isWhite + case V1L: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, -1) + + isWhite = !isWhite + case V2L: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, -2) + + isWhite = !isWhite + case V3L: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, -3) + + isWhite = !isWhite + } + + // TODO: additionally check for errors. EOL should be fetched when a0 == len(pixels[len(pixels)-1]) + if len(pixelsRow) >= e.Columns { + break + } + } + + if e.EncodedByteAlign && bitPos%8 != 0 { + bitPos += 8 - bitPos%8 + } + + if pixelsRow != nil { + pixels = append(pixels, pixelsRow) + } + + if e.Rows > 0 && !e.EndOfBlock && len(pixels) >= e.Rows { + break byteLoop + } + } + } + + return pixels, nil +} + +func (e *Encoder) decodeG4(encoded []byte) ([][]byte, error) { + whiteReferenceLine := make([]byte, e.Columns) + for i := range whiteReferenceLine { + whiteReferenceLine[i] = white + } + + pixels := make([][]byte, 1) + pixels[0] = whiteReferenceLine + + var ( + gotEOL bool + err error + bitPos int + ) + for (bitPos / 8) < len(encoded) { + if len(pixels) == 1198 { + log.Println() + } + // try get EOFB + gotEOL, bitPos, err = tryFetchEOFB(encoded, bitPos) + if err != nil { + return nil, err + } + if gotEOL { + break + } + + var ( + twoDimCode Code + ok bool + ) + + isWhite := true + var pixelsRow []byte + a0 := -1 + for a0 < e.Columns { + twoDimCode, bitPos, ok = fetchNext2DCode(encoded, bitPos) + if !ok { + return nil, errors.New("wrong 2 dim code") + } + + switch twoDimCode { + case P: + // do pass mode decoding + pixelsRow, a0 = decodePassMode(pixels, pixelsRow, isWhite, a0) + case H: + // do horizontal mode decoding + pixelsRow, bitPos, a0, err = decodeHorizontalMode(encoded, pixelsRow, bitPos, isWhite, a0) + if err != nil { + return nil, err + } + case V0: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, 0) + + isWhite = !isWhite + case V1R: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, 1) + + isWhite = !isWhite + case V2R: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, 2) + + isWhite = !isWhite + case V3R: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, 3) + + isWhite = !isWhite + case V1L: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, -1) + + isWhite = !isWhite + case V2L: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, -2) + + isWhite = !isWhite + case V3L: + pixelsRow, a0 = decodeVerticalMode(pixels, pixelsRow, isWhite, a0, -3) + + isWhite = !isWhite + } + + if len(pixelsRow) >= e.Columns { + break + } + } + + if e.EncodedByteAlign && bitPos%8 != 0 { + bitPos += 8 - bitPos%8 + } + + pixels = append(pixels, pixelsRow) + + if e.Rows > 0 && !e.EndOfBlock && len(pixels) >= e.Rows { + break + } + } + + pixels = pixels[1:] + + return pixels, nil +} + +func decodeVerticalMode(pixels [][]byte, pixelsRow []byte, isWhite bool, a0, shift int) ([]byte, int) { + b1 := seekB12D(pixelsRow, pixels[len(pixels)-1], a0, isWhite) + // true for V0 + a1 := b1 + shift + + if a0 == -1 { + pixelsRow = drawPixels(pixelsRow, isWhite, a1-a0-1) + } else { + pixelsRow = drawPixels(pixelsRow, isWhite, a1-a0) + } + + a0 = a1 + + return pixelsRow, a0 +} + +func decodePassMode(pixels [][]byte, pixelsRow []byte, isWhite bool, a0 int) ([]byte, int) { + b1 := seekB12D(pixelsRow, pixels[len(pixels)-1], a0, isWhite) + b2 := seekChangingElem(pixels[len(pixels)-1], b1) + + if a0 == -1 { + pixelsRow = drawPixels(pixelsRow, isWhite, b2-a0-1) + } else { + pixelsRow = drawPixels(pixelsRow, isWhite, b2-a0) + } + + a0 = b2 + + return pixelsRow, a0 +} + +func decodeHorizontalMode(encoded, pixelsRow []byte, bitPos int, isWhite bool, a0 int) ([]byte, int, int, error) { + startingBitPos := bitPos + + var err error + pixelsRow, bitPos, err = decodeNextRunLen(encoded, pixelsRow, bitPos, isWhite) + if err != nil { + return pixelsRow, startingBitPos, a0, err + } + + isWhite = !isWhite + + pixelsRow, bitPos, err = decodeNextRunLen(encoded, pixelsRow, bitPos, isWhite) + if err != nil { + return pixelsRow, startingBitPos, a0, err + } + + // the last code was the code of a1a2 run. a0 was moved to a2 + // while encoding. that's why we put a0 on the a2 and continue + a0 = len(pixelsRow) + + return pixelsRow, bitPos, a0, nil +} + +func decodeNextRunLen(encoded, pixelsRow []byte, bitPos int, isWhite bool) ([]byte, int, error) { + startingBitPos := bitPos + + var runLen int + + for runLen, bitPos = fetchNextRunLen(encoded, bitPos, isWhite); runLen != -1; runLen, bitPos = fetchNextRunLen(encoded, bitPos, isWhite) { + pixelsRow = drawPixels(pixelsRow, isWhite, runLen) + + // got terminal code, switch color + if runLen < 64 { + break + } + } + + if runLen == -1 { + return pixelsRow, startingBitPos, errors.New("wrong code in horizontal mode") + } + + return pixelsRow, bitPos, nil +} + +func tryFetchRTC2D(encoded []byte, bitPos int) (bool, int, error) { + startingBitPos := bitPos + var gotEOL = false + + // 6 EOL1s to fill RTC + for i := 0; i < 6; i++ { + gotEOL, bitPos = tryFetchEOL1(encoded, bitPos) + + if !gotEOL { + if i > 1 { + return false, startingBitPos, errors.New("RTC code is corrupted") + } else { + break + } + } + } + + return gotEOL, bitPos, nil +} + +func tryFetchEOFB(encoded []byte, bitPos int) (bool, int, error) { + startingBitPos := bitPos + + var gotEOL bool + gotEOL, bitPos = tryFetchEOL(encoded, bitPos) + if gotEOL { + // 1 EOL to fill EOFB + gotEOL, bitPos = tryFetchEOL(encoded, bitPos) + + if gotEOL { + return true, bitPos, nil + } else { + return false, startingBitPos, errors.New("EOFB code is corrupted") + } + } + + return false, startingBitPos, nil +} + +func bitFromUint16(num uint16, bitPos int) byte { + if bitPos < 8 { + num >>= 8 + } + + bitPos %= 8 + + mask := byte(0x01 << (7 - uint(bitPos))) + + return (byte(num) & mask) >> (7 - uint(bitPos)) +} + +func fetchNextRunLen(data []byte, bitPos int, isWhite bool) (int, int) { + var ( + codeNum uint16 + codeBitPos int + startingBitPos int + ) + + startingBitPos = bitPos + codeNum, codeBitPos, bitPos = fetchNextCode(data, bitPos) + + runLen, code := decodeRunLenFromUint16(codeNum, codeBitPos, isWhite) + if runLen == -1 { + return -1, startingBitPos + } + + return runLen, startingBitPos + code.BitsWritten +} + +func fetchNext2DCode(data []byte, bitPos int) (Code, int, bool) { + var ( + codeNum uint16 + codeBitPos int + startingBitPos int + ) + + startingBitPos = bitPos + codeNum, codeBitPos, bitPos = fetchNextCode(data, bitPos) + + code, ok := get2DCodeFromUint16(codeNum, codeBitPos) + if !ok { + return Code{}, startingBitPos, false + } + + return code, startingBitPos + code.BitsWritten, true +} + +func get2DCodeFromUint16(encoded uint16, bitPos int) (Code, bool) { + _, codePtr := findRunLen(twoDimTree, encoded, bitPos) + + if codePtr == nil { + return Code{}, false + } + + return *codePtr, true +} + +func decodeRunLenFromUint16(encoded uint16, bitPos int, isWhite bool) (int, Code) { + var runLenPtr *int + var codePtr *Code + + if isWhite { + runLenPtr, codePtr = findRunLen(whiteTree, encoded, bitPos) + } else { + runLenPtr, codePtr = findRunLen(blackTree, encoded, bitPos) + } + + if runLenPtr == nil { + return -1, Code{} + } + + return *runLenPtr, *codePtr +} + +func fetchNextCode(data []byte, bitPos int) (uint16, int, int) { + startingBitPos := bitPos + + bytePos := bitPos / 8 + bitPos %= 8 + + if bytePos >= len(data) { + return 0, 16, startingBitPos + } + + // take the rest bits from the current byte + mask := byte(0xFF >> uint(bitPos)) + code := uint16((data[bytePos]&mask)<= len(data) { + return code >> (16 - uint(bitsWritten)), 16 - bitsWritten, startingBitPos + bitsWritten + } + + // take the whole next byte + code |= uint16(data[bytePos]) << (8 - uint(bitsWritten)) + + bitsWritten += 8 + + bytePos++ + bitPos = 0 + + if bytePos >= len(data) { + return code >> (16 - uint(bitsWritten)), 16 - bitsWritten, startingBitPos + bitsWritten + } + + if bitsWritten == 16 { + return code, 0, startingBitPos + bitsWritten + } + + // take the needed bits from the next byte + leftToWrite := 16 - bitsWritten + + code |= uint16(data[bytePos] >> (8 - uint(leftToWrite))) + + return code, 0, startingBitPos + 16 +} + +func (e *Encoder) decodeRow1D(encoded []byte, bitPos int) ([]byte, int) { + var pixelsRow []byte + + isWhite := true + + var runLen int + runLen, bitPos = fetchNextRunLen(encoded, bitPos, isWhite) + for runLen != -1 { + pixelsRow = drawPixels(pixelsRow, isWhite, runLen) + + // got terminal code, switch color + if runLen < 64 { + if len(pixelsRow) >= e.Columns { + break + } + + isWhite = !isWhite + } + + runLen, bitPos = fetchNextRunLen(encoded, bitPos, isWhite) + } + + return pixelsRow, bitPos +} + +func drawPixels(row []byte, isWhite bool, length int) []byte { + runLenPixels := make([]byte, length) + if isWhite { + for i := 0; i < len(runLenPixels); i++ { + runLenPixels[i] = white + } + } else { + for i := 0; i < len(runLenPixels); i++ { + runLenPixels[i] = black + } + } + + row = append(row, runLenPixels...) + + return row +} + +func tryFetchEOL(encoded []byte, bitPos int) (bool, int) { + startingBitPos := bitPos + + var ( + code uint16 + codeBitPos int + ) + + code, codeBitPos, bitPos = fetchNextCode(encoded, bitPos) + + // when this is true, the fetched code if less than 12 bits long, + // which doesn't fit the EOL code length + if codeBitPos > 4 { + return false, startingBitPos + } + + // format the fetched code + code >>= uint(4 - codeBitPos) + code <<= 4 + + if code != EOL.Code { + return false, startingBitPos + } else { + return true, bitPos - 4 + codeBitPos + } +} + +func tryFetchExtendedEOL(encoded []byte, bitPos int, eolCode Code) (bool, int) { + startingBitPos := bitPos + + var ( + code uint16 + codeBitPos int + ) + + code, codeBitPos, bitPos = fetchNextCode(encoded, bitPos) + + // when this is true, the fetched code if less than 13 bits long, + // which doesn't fit the EOL0/EOL1 code length + if codeBitPos > 3 { + return false, startingBitPos + } + + // format the fetched code + code >>= uint(3 - codeBitPos) + code <<= 3 + + if code != eolCode.Code { + return false, startingBitPos + } else { + return true, bitPos - 3 + codeBitPos + } +} + +func tryFetchEOL0(encoded []byte, bitPos int) (bool, int) { + return tryFetchExtendedEOL(encoded, bitPos, EOL0) +} + +func tryFetchEOL1(encoded []byte, bitPos int) (bool, int) { + return tryFetchExtendedEOL(encoded, bitPos, EOL1) +} \ No newline at end of file diff --git a/pdf/core/ccittfaxdecode/decode_test.go b/pdf/core/ccittfaxdecode/decode_test.go new file mode 100644 index 00000000..1b675a5f --- /dev/null +++ b/pdf/core/ccittfaxdecode/decode_test.go @@ -0,0 +1,292 @@ +package ccittfaxdecode + +import "testing" + +func TestBitFromUint16(t *testing.T) { + type testData struct { + Num uint16 + BitPos int + Want byte + } + var tests []testData + + var num uint16 = 43690 + for i := 0; i < 16; i++ { + var want byte = 1 + if (i % 2) != 0 { + want = 0 + } + + tests = append(tests, testData{ + Num: num, + BitPos: i, + Want: want, + }) + } + + for _, test := range tests { + bit := bitFromUint16(test.Num, test.BitPos) + + if bit != test.Want { + t.Errorf("Wrong bit. Got %v want %v\n", bit, test.Want) + } + } +} + +func TestFetchNextCode(t *testing.T) { + testData := []byte{95, 21, 197} + + type testResult struct { + Code uint16 + CodeBitPos int + DataBitPos int + } + + tests := []struct { + Data []byte + BitPos int + Want testResult + }{ + { + Data: testData, + BitPos: 0, + Want: testResult{ + Code: 24341, + CodeBitPos: 0, + DataBitPos: 16, + }, + }, + { + Data: testData, + BitPos: 1, + Want: testResult{ + Code: 48683, + CodeBitPos: 0, + DataBitPos: 17, + }, + }, + { + Data: testData, + BitPos: 2, + Want: testResult{ + Code: 31831, + CodeBitPos: 0, + DataBitPos: 18, + }, + }, + { + Data: testData, + BitPos: 3, + Want: testResult{ + Code: 63662, + CodeBitPos: 0, + DataBitPos: 19, + }, + }, + { + Data: testData, + BitPos: 4, + Want: testResult{ + Code: 61788, + CodeBitPos: 0, + DataBitPos: 20, + }, + }, + { + Data: testData, + BitPos: 5, + Want: testResult{ + Code: 58040, + CodeBitPos: 0, + DataBitPos: 21, + }, + }, + { + Data: testData, + BitPos: 6, + Want: testResult{ + Code: 50545, + CodeBitPos: 0, + DataBitPos: 22, + }, + }, + { + Data: testData, + BitPos: 7, + Want: testResult{ + Code: 35554, + CodeBitPos: 0, + DataBitPos: 23, + }, + }, + { + Data: testData, + BitPos: 8, + Want: testResult{ + Code: 5573, + CodeBitPos: 0, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 9, + Want: testResult{ + Code: 5573, + CodeBitPos: 1, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 10, + Want: testResult{ + Code: 5573, + CodeBitPos: 2, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 11, + Want: testResult{ + Code: 5573, + CodeBitPos: 3, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 12, + Want: testResult{ + Code: 1477, + CodeBitPos: 4, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 13, + Want: testResult{ + Code: 1477, + CodeBitPos: 5, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 14, + Want: testResult{ + Code: 453, + CodeBitPos: 6, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 15, + Want: testResult{ + Code: 453, + CodeBitPos: 7, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 16, + Want: testResult{ + Code: 197, + CodeBitPos: 8, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 17, + Want: testResult{ + Code: 69, + CodeBitPos: 9, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 18, + Want: testResult{ + Code: 5, + CodeBitPos: 10, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 19, + Want: testResult{ + Code: 5, + CodeBitPos: 11, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 20, + Want: testResult{ + Code: 5, + CodeBitPos: 12, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 21, + Want: testResult{ + Code: 5, + CodeBitPos: 13, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 22, + Want: testResult{ + Code: 1, + CodeBitPos: 14, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 23, + Want: testResult{ + Code: 1, + CodeBitPos: 15, + DataBitPos: 24, + }, + }, + { + Data: testData, + BitPos: 24, + Want: testResult{ + Code: 0, + CodeBitPos: 16, + DataBitPos: 24, + }, + }, + } + + for _, test := range tests { + gotCode, gotCodeBitPos, gotDataBitPos := fetchNextCode(test.Data, test.BitPos) + + if gotCode != test.Want.Code { + t.Errorf("Wrong code. Got %v, want %v\n", gotCode, test.Want.Code) + } + + if gotCodeBitPos != test.Want.CodeBitPos { + t.Errorf("Wrong code bit pos. Got %v, want %v\n", gotCodeBitPos, test.Want.CodeBitPos) + } + + if gotDataBitPos != test.Want.DataBitPos { + t.Errorf("Wrong data bit pos. Got %v, want %v\n", gotDataBitPos, test.Want.DataBitPos) + } + } +} diff --git a/pdf/core/ccittfaxdecode/decoding_tree.go b/pdf/core/ccittfaxdecode/decoding_tree.go new file mode 100644 index 00000000..1a3e3833 --- /dev/null +++ b/pdf/core/ccittfaxdecode/decoding_tree.go @@ -0,0 +1,70 @@ +package ccittfaxdecode + +type decodingTreeNode struct { + Val byte + RunLen *int + Code *Code + Left *decodingTreeNode + Right *decodingTreeNode +} + +func addNode(root *decodingTreeNode, code Code, bitPos int, runLen int) { + val := bitFromUint16(code.Code, bitPos) + bitPos++ + + if val == 1 { + if root.Right == nil { + root.Right = &decodingTreeNode{ + Val: val, + } + } + + if bitPos == code.BitsWritten { + root.Right.RunLen = &runLen + root.Right.Code = &code + } else { + addNode(root.Right, code, bitPos, runLen) + } + } else { + if root.Left == nil { + root.Left = &decodingTreeNode{ + Val: val, + } + } + + if bitPos == code.BitsWritten { + root.Left.RunLen = &runLen + root.Left.Code = &code + } else { + addNode(root.Left, code, bitPos, runLen) + } + } +} + +func findRunLen(root *decodingTreeNode, code uint16, bitPos int) (*int, *Code) { + if root == nil { + return nil, nil + } + + if bitPos == 16 { + return root.RunLen, root.Code + } + + val := bitFromUint16(code, bitPos) + bitPos++ + + var runLenPtr *int + var codePtr *Code + if val == 1 { + runLenPtr, codePtr = findRunLen(root.Right, code, bitPos) + } else { + runLenPtr, codePtr = findRunLen(root.Left, code, bitPos) + } + + if runLenPtr == nil { + runLenPtr = root.RunLen + codePtr = root.Code + } + + return runLenPtr, codePtr +} \ No newline at end of file diff --git a/pdf/core/ccittfaxdecode/encoder.go b/pdf/core/ccittfaxdecode/encoder.go new file mode 100644 index 00000000..2df2405d --- /dev/null +++ b/pdf/core/ccittfaxdecode/encoder.go @@ -0,0 +1,539 @@ +package ccittfaxdecode + +import ( + "math" +) + +var ( + white byte = 1 + black byte = 0 +) + +type Encoder struct { + K int + EndOfLine bool + EncodedByteAlign bool + Columns int + Rows int + EndOfBlock bool + BlackIs1 bool + DamagedRowsBeforeError int +} + +func (e *Encoder) Encode(pixels [][]byte) []byte { + if e.BlackIs1 { + white = 0 + black = 1 + } else { + white = 1 + black = 0 + } + + if e.K == 0 { + // do G31D + return e.encodeG31D(pixels) + } + + if e.K > 0 { + // do G32D + return e.encodeG32D(pixels) + } + + if e.K < 0 { + // do G4 + return e.encodeG4(pixels) + } + + // never happens + return nil +} + +func (e *Encoder) encodeG31D(pixels [][]byte) []byte { + var encoded []byte + + prevBitPos := 0 + for i := range pixels { + if e.Rows > 0 && !e.EndOfBlock && i == e.Rows { + break + } + + encodedRow, bitPos := encodeRow1D(pixels[i], prevBitPos, EOL) + + encoded = e.appendEncodedRow(encoded, encodedRow, prevBitPos) + + if e.EncodedByteAlign { + bitPos = 0 + } + + prevBitPos = bitPos + } + + if e.EndOfBlock { + // put rtc + encodedRTC, _ := encodeRTC(prevBitPos) + + encoded = e.appendEncodedRow(encoded, encodedRTC, prevBitPos) + } + + return encoded +} + +func (e *Encoder) encodeG32D(pixels [][]byte) []byte { + var encoded []byte + var prevBitPos int + for i := 0; i < len(pixels); i += e.K { + if e.Rows > 0 && !e.EndOfBlock && i == e.Rows { + break + } + + encodedRow, bitPos := encodeRow1D(pixels[i], prevBitPos, EOL1) + + encoded = e.appendEncodedRow(encoded, encodedRow, prevBitPos) + + if e.EncodedByteAlign { + bitPos = 0 + } + + prevBitPos = bitPos + + for j := i + 1; j < (i+e.K) && j < len(pixels); j++ { + if e.Rows > 0 && !e.EndOfBlock && j == e.Rows { + break + } + + encodedRow, bitPos := addCode(nil, prevBitPos, EOL0) + + var a1, b1, b2 int + + a0 := -1 + + // !EOL + for a0 < len(pixels[j]) { + a1 = seekChangingElem(pixels[j], a0) + b1 = seekB1(pixels[j], pixels[j-1], a0) + b2 = seekChangingElem(pixels[j-1], b1) + + if b2 < a1 { + // do pass mode + encodedRow, bitPos = encodePassMode(encodedRow, bitPos) + + a0 = b2 + } else { + if math.Abs(float64(b1-a1)) > 3 { + // do horizontal mode + encodedRow, bitPos, a0 = encodeHorizontalMode(pixels[j], encodedRow, bitPos, a0, a1) + } else { + // do vertical mode + encodedRow, bitPos = encodeVerticalMode(encodedRow, bitPos, a1, b1) + + a0 = a1 + } + } + } + + encoded = e.appendEncodedRow(encoded, encodedRow, prevBitPos) + + if e.EncodedByteAlign { + bitPos = 0 + } + + prevBitPos = bitPos % 8 + } + } + + if e.EndOfBlock { + // put rtc + encodedRTC, _ := encodeRTC2D(prevBitPos) + + encoded = e.appendEncodedRow(encoded, encodedRTC, prevBitPos) + } + + return encoded +} + +func (e *Encoder) encodeG4(pixels [][]byte) []byte { + pixels = appendWhiteReferenceLine(pixels) + + var encoded []byte + var prevBitPos int + for i := 1; i < len(pixels); i++ { + if e.Rows > 0 && !e.EndOfBlock && i == e.Rows { + break + } + + var encodedRow []byte + var a1, b1, b2 int + + bitPos := prevBitPos + a0 := -1 + + // !EOL + for a0 < len(pixels[i]) { + a1 = seekChangingElem(pixels[i], a0) + b1 = seekB1(pixels[i], pixels[i-1], a0) + b2 = seekChangingElem(pixels[i-1], b1) + + if b2 < a1 { + // do pass mode + encodedRow, bitPos = addCode(encodedRow, bitPos, P) + + a0 = b2 + } else { + if math.Abs(float64(b1-a1)) > 3 { + encodedRow, bitPos, a0 = encodeHorizontalMode(pixels[i], encodedRow, bitPos, a0, a1) + } else { + // do vertical mode + encodedRow, bitPos = encodeVerticalMode(encodedRow, bitPos, a1, b1) + + a0 = a1 + } + } + } + + encoded = e.appendEncodedRow(encoded, encodedRow, prevBitPos) + + if e.EncodedByteAlign { + bitPos = 0 + } + + prevBitPos = bitPos % 8 + } + + if e.EndOfBlock { + // put eofb + encodedEOFB, _ := encodeEOFB(prevBitPos) + + encoded = e.appendEncodedRow(encoded, encodedEOFB, prevBitPos) + } + + return encoded +} + +// encodeRTC encodes the RTC code for the G3 1D (6 EOL in a row). bitPos is equal to the one in +// encodeRow +func encodeRTC(bitPos int) ([]byte, int) { + var encoded []byte + + for i := 0; i < 6; i++ { + encoded, bitPos = addCode(encoded, bitPos, EOL) + } + + return encoded, bitPos % 8 +} + +// encodeRTC2D encodes the RTC code for the G3 2D (6 EOL + 1 bit in a row). bitPos is equal to the one in +// encodeRow +func encodeRTC2D(bitPos int) ([]byte, int) { + var encoded []byte + + for i := 0; i < 6; i++ { + encoded, bitPos = addCode(encoded, bitPos, EOL1) + } + + return encoded, bitPos % 8 +} + +// encodeEOFB encodes the EOFB code for the G4 (2 EOL in a row). bitPos is equal to the one in +// encodeRow +func encodeEOFB(bitPos int) ([]byte, int) { + var encoded []byte + + for i := 0; i < 2; i++ { + encoded, bitPos = addCode(encoded, bitPos, EOL) + } + + return encoded, bitPos % 8 +} + +// encodeRow1D encodes single raw of the image in 1D mode. bitPos is the bit position +// global for the row array. bitPos is used to indicate where to start the +// encoded sequences. It is used for the EncodedByteAlign option implementation. +// Returns the encoded data and the number of the bits taken from the last byte +func encodeRow1D(row []byte, bitPos int, prefix Code) ([]byte, int) { + // always start with whites + isWhite := true + var encoded []byte + + // always add EOL before the scan line + encoded, bitPos = addCode(nil, bitPos, prefix) + + bytePos := 0 + var runLen int + for bytePos < len(row) { + runLen, bytePos = calcRun(row, isWhite, bytePos) + + encoded, bitPos = encodeRunLen(encoded, bitPos, runLen, isWhite) + + // switch color + isWhite = !isWhite + } + + return encoded, bitPos % 8 +} + +// encodeRunLen encodes the calculated run length to the encoded starting with bitPos bit +func encodeRunLen(encoded []byte, bitPos int, runLen int, isWhite bool) ([]byte, int) { + var ( + code Code + isTerminal bool + ) + + for !isTerminal { + code, runLen, isTerminal = getRunCode(runLen, isWhite) + encoded, bitPos = addCode(encoded, bitPos, code) + } + + return encoded, bitPos +} + +// getRunCode gets the code for the specified run. If the code is not +// terminal, returns the remainder to be determined later. Otherwise +// returns 0 remainder. Also returns the bool flag to indicate if +// the code is terminal +func getRunCode(runLen int, isWhite bool) (Code, int, bool) { + if runLen < 64 { + if isWhite { + return WTerms[runLen], 0, true + } else { + return BTerms[runLen], 0, true + } + } else { + multiplier := runLen / 64 + + // stands for lens more than 2560 which are not + // covered by the Huffman codes + if multiplier > 40 { + return CommonMakeups[2560], runLen - 2560, false + } + + // stands for lens more than 1728. These should be common + // for both colors + if multiplier > 27 { + return CommonMakeups[multiplier*64], runLen - multiplier*64, false + } + + // for lens < 27 we use the specific makeups for each color + if isWhite { + return WMakeups[multiplier*64], runLen - multiplier*64, false + } else { + return BMakeups[multiplier*64], runLen - multiplier*64, false + } + } +} + +// calcRun calculates the nex pixel run. Returns the number of the +// pixels and the new position in the original array +func calcRun(row []byte, isWhite bool, pos int) (int, int) { + count := 0 + for pos < len(row) { + if isWhite { + if row[pos] != white { + break + } + } else { + if row[pos] != black { + break + } + } + + count++ + pos++ + } + + return count, pos +} + +// addCode encodes the specified code to the encoded starting with pos bit. pos +// bit is a bit position global to the whole encoded array +func addCode(encoded []byte, pos int, code Code) ([]byte, int) { + i := 0 + for i < code.BitsWritten { + bytePos := pos / 8 + bitPos := pos % 8 + + if bytePos >= len(encoded) { + encoded = append(encoded, 0) + } + + toWrite := 8 - bitPos + leftToWrite := code.BitsWritten - i + if toWrite > leftToWrite { + toWrite = leftToWrite + } + + if i < 8 { + encoded[bytePos] = encoded[bytePos] | byte(code.Code>>uint(8+bitPos-i))&masks[8-toWrite-bitPos] + } else { + encoded[bytePos] = encoded[bytePos] | (byte(code.Code<>uint(bitPos) + } + + pos += toWrite + + i += toWrite + } + + return encoded, pos +} + +// appendEncodedRow appends the newly encoded row to the array of the encoded data. +// bitPos is a bitPos in the last byte of the encoded data. bitPos points where to +// write the next piece of data +func (e *Encoder) appendEncodedRow(encoded, newRow []byte, bitPos int) []byte { + if len(encoded) > 0 && bitPos != 0 && !e.EncodedByteAlign { + encoded[len(encoded)-1] = encoded[len(encoded)-1] | newRow[0] + + encoded = append(encoded, newRow[1:]...) + } else { + encoded = append(encoded, newRow...) + } + + return encoded +} + +func rightShiftArray(row []byte, shiftAmount int) []byte { + for i := len(row) - 1; i >= 0; i-- { + row[i] = row[i] >> uint(shiftAmount) + + if i > 0 { + row[i] |= row[i-1] << uint(8-shiftAmount) + } + } + + return row +} + +// seekChangingElem gets the position of the changing elem in the row based on +// the position of the currElem +func seekChangingElem(row []byte, currElem int) int { + if currElem >= len(row) { + return currElem + } + + var color byte + if currElem > -1 { + color = row[currElem] + } else { + color = white + } + + i := currElem + 1 + for i < len(row) { + if row[i] != color { + break + } + + i++ + } + + return i +} + +// seekB1 gets the position of b1 based on the position of a0 +func seekB1(codingLine, refLine []byte, a0 int) int { + changingElem := seekChangingElem(refLine, a0) + + if changingElem < len(refLine) && (a0 == -1 && refLine[changingElem] == white || + a0 >= 0 && a0 < len(codingLine) && codingLine[a0] == refLine[changingElem] || + a0 >= len(codingLine) && codingLine[a0-1] != refLine[changingElem]) { + changingElem = seekChangingElem(refLine, changingElem) + } + + return changingElem +} + +func seekB12D(codingLine, refLine []byte, a0 int, a0isWhite bool) int { + changingElem := seekChangingElem(refLine, a0) + + if changingElem < len(refLine) && (a0 == -1 && refLine[changingElem] == white || + a0 >= 0 && a0 < len(codingLine) && codingLine[a0] == refLine[changingElem] || + a0 >= len(codingLine) && a0isWhite && refLine[changingElem] == white || + a0 >= len(codingLine) && !a0isWhite && refLine[changingElem] == black) { + changingElem = seekChangingElem(refLine, changingElem) + } + + return changingElem +} + +// encodePassMode encodes the pass mode to the encodedRow starting with bitPos bit +func encodePassMode(encodedRow []byte, bitPos int) ([]byte, int) { + return addCode(encodedRow, bitPos, P) +} + +// encodeHorizontalMode encodes the horizontal mode to the encodedRow starting with bitPos bit +func encodeHorizontalMode(row, encodedRow []byte, bitPos, a0, a1 int) ([]byte, int, int) { + a2 := seekChangingElem(row, a1) + + isWhite := a0 >= 0 && row[a0] == white || a0 == -1 + + encodedRow, bitPos = addCode(encodedRow, bitPos, H) + var a0a1RunLen int + if a0 > -1 { + a0a1RunLen = a1 - a0 + } else { + a0a1RunLen = a1 - a0 - 1 + } + + encodedRow, bitPos = encodeRunLen(encodedRow, bitPos, a0a1RunLen, isWhite) + + isWhite = !isWhite + + a1a2RunLen := a2 - a1 + + encodedRow, bitPos = encodeRunLen(encodedRow, bitPos, a1a2RunLen, isWhite) + + a0 = a2 + + return encodedRow, bitPos, a0 +} + +// encodeVerticalMode encodes vertical mode to the encodedRow starting with bitPos bit +func encodeVerticalMode(encodedRow []byte, bitPos, a1, b1 int) ([]byte, int) { + vCode := getVCode(a1, b1) + + encodedRow, bitPos = addCode(encodedRow, bitPos, vCode) + + return encodedRow, bitPos +} + +// getVCode get the code for the vertical mode based on +// the locations of a1 and b1 +func getVCode(a1, b1 int) Code { + var vCode Code + + switch b1 - a1 { + case -1: + vCode = V1R + case -2: + vCode = V2R + case -3: + vCode = V3R + case 0: + vCode = V0 + case 1: + vCode = V1L + case 2: + vCode = V2L + case 3: + vCode = V3L + } + + return vCode +} + +// appendWhiteReferenceLine appends the line full of white pixels just before +// the first image line +func appendWhiteReferenceLine(pixels [][]byte) [][]byte { + whiteRefLine := make([]byte, len(pixels[0])) + for i := range whiteRefLine { + whiteRefLine[i] = white + } + + pixels = append(pixels, []byte{}) + for i := len(pixels) - 1; i > 0; i-- { + pixels[i] = pixels[i-1] + } + + pixels[0] = whiteRefLine + + return pixels +} \ No newline at end of file diff --git a/pdf/core/encoding.go b/pdf/core/encoding.go index 8da6fd6e..f542d5e2 100644 --- a/pdf/core/encoding.go +++ b/pdf/core/encoding.go @@ -34,6 +34,7 @@ import ( lzw1 "golang.org/x/image/tiff/lzw" "github.com/unidoc/unidoc/common" + "github.com/unidoc/unidoc/pdf/core/ccittfaxdecode" ) const ( @@ -1521,39 +1522,457 @@ func (enc *RawEncoder) EncodeBytes(data []byte) ([]byte, error) { return data, nil } -// CCITTFaxEncoder implements CCITTFax encoder/decoder (dummy, for now) -// FIXME: implement -type CCITTFaxEncoder struct{} +// +// CCITTFax encoder/decoder (dummy, for now) +// +type CCITTFaxEncoder struct { + Mode CCITTMode + K int + EndOfLine bool + EncodedByteAlign bool + Columns int + Rows int + EndOfBlock bool + BlackIs1 bool + DamagedRowsBeforeError int +} + +type CCITTMode int + +const ( + Group3 CCITTMode = iota + Group4 + GroupMixed + GroupUnknown +) + +func (mode CCITTMode) String() string { + switch mode { + case Group3: + return "Group 3" + case Group4: + return "Group 4" + case GroupMixed: + return "Mixed" + default: + return "Unknown" + } +} + +// <0 : Pure two-dimensional encoding (Group 4) +// =0 : Pure one-dimensional encoding (Group 3, 1-D) +// >0 : Mixed one- and two-dimensional encoding (Group 3, 2-D) +func IntToCCITTMode(v int) CCITTMode { + switch { + case v == 0: + return Group3 + case v < 0: + return Group4 + case v > 0: + return GroupMixed + default: + return GroupUnknown + } +} func NewCCITTFaxEncoder() *CCITTFaxEncoder { return &CCITTFaxEncoder{} } -func (enc *CCITTFaxEncoder) GetFilterName() string { +func (this *CCITTFaxEncoder) GetFilterName() string { return StreamEncodingFilterNameCCITTFax } -func (enc *CCITTFaxEncoder) MakeDecodeParams() PdfObject { - return nil +func (this *CCITTFaxEncoder) MakeDecodeParams() PdfObject { + decodeParams := MakeDict() + decodeParams.Set("K", MakeInteger(int64(this.K))) + decodeParams.Set("Columns", MakeInteger(int64(this.Columns))) + decodeParams.Set("BlackIs1", MakeBool(this.BlackIs1)) + decodeParams.Set("EncodedByteAlign", MakeBool(this.EncodedByteAlign)) + decodeParams.Set("EndOfLine", MakeBool(this.EndOfLine)) + decodeParams.Set("Rows", MakeInteger(int64(this.Rows))) + decodeParams.Set("EndOfBlock", MakeBool(this.EndOfBlock)) + decodeParams.Set("DamagedRowsBeforeError", MakeInteger(int64(this.DamagedRowsBeforeError))) + + return decodeParams } -// MakeStreamDict makes a new instance of an encoding dictionary for a stream object. -func (enc *CCITTFaxEncoder) MakeStreamDict() *PdfObjectDictionary { - return MakeDict() +// Make a new instance of an encoding dictionary for a stream object. +func (this *CCITTFaxEncoder) MakeStreamDict() *PdfObjectDictionary { + dict := MakeDict() + dict.Set("Filter", MakeName(this.GetFilterName())) + + decodeParams := this.MakeDecodeParams() + if decodeParams != nil { + dict.Set("DecodeParms", decodeParams) + } + + return dict } -func (enc *CCITTFaxEncoder) DecodeBytes(encoded []byte) ([]byte, error) { - common.Log.Debug("Error: Attempting to use unsupported encoding %s", enc.GetFilterName()) - return encoded, ErrNoCCITTFaxDecode +// Create a new CCITTFax decoder from a stream object, getting all the encoding parameters +// from the DecodeParms stream object dictionary entry. +func newCCITTFaxEncoderFromStream(streamObj *PdfObjectStream, decodeParams *PdfObjectDictionary) (*CCITTFaxEncoder, error) { + encoder := NewCCITTFaxEncoder() + + encDict := streamObj.PdfObjectDictionary + if encDict == nil { + // No encoding dictionary. + return encoder, nil + } + + // If decodeParams not provided, see if we can get from the stream. + if decodeParams == nil { + obj := encDict.Get("DecodeParms") + if obj != nil { + if dp, isDict := obj.(*PdfObjectDictionary); isDict { + decodeParams = dp + } else if a, isArr := obj.(*PdfObjectArray); isArr { + if a.Len() == 1 { + if dp, isDict := a.Get(0).(*PdfObjectDictionary); isDict { + decodeParams = dp + } + } + } + if decodeParams == nil { + common.Log.Error("DecodeParms not a dictionary %#v", obj) + return nil, fmt.Errorf("Invalid DecodeParms") + } + } + } + + obj := decodeParams.Get("K") + if obj != nil { + k, ok := obj.(*PdfObjectInteger) + if !ok { + return nil, fmt.Errorf("K is invalid") + } + fmt.Printf("K = %v\n", *k) + + encoder.Mode = IntToCCITTMode(int(*k)) + encoder.K = int(*k) + } + + encoder.Columns = 1728 + obj = decodeParams.Get("Columns") + if obj != nil { + columns, ok := obj.(*PdfObjectInteger) + if !ok { + return nil, fmt.Errorf("Columns is invalid") + } + + encoder.Columns = int(*columns) + } + + obj = decodeParams.Get("BlackIs1") + if obj != nil { + switch inverse := obj.(type) { + case *PdfObjectInteger: + encoder.BlackIs1 = inverse != nil && *inverse > 0 + case *PdfObjectBool: + encoder.BlackIs1 = inverse != nil && bool(*inverse) + default: + common.Log.Trace("BlackIs1 type: %T", obj) + return nil, fmt.Errorf("BlackIs1 is invalid") + } + } else { + obj = encDict.Get("Decode") + if arr, ok := obj.(*PdfObjectArray); ok { + intArr, err := arr.ToIntegerArray() + if err != nil { + return nil, fmt.Errorf("Decode is invalid") + } + + encoder.BlackIs1 = intArr[0] == 1 && intArr[1] == 0 + } + } + + obj = decodeParams.Get("EncodedByteAlign") + if obj != nil { + switch align := obj.(type) { + case *PdfObjectInteger: + encoder.EncodedByteAlign = align != nil && *align > 0 + case *PdfObjectBool: + encoder.EncodedByteAlign = align != nil && bool(*align) + default: + common.Log.Trace("EncodedByteAlign type: %T", obj) + return nil, fmt.Errorf("EncodedByteAlign is invalid") + } + } + + obj = decodeParams.Get("EndOfLine") + if obj != nil { + switch eol := obj.(type) { + case *PdfObjectInteger: + encoder.EndOfLine = eol != nil && *eol > 0 + case *PdfObjectBool: + encoder.EncodedByteAlign = eol != nil && bool(*eol) + default: + common.Log.Trace("EncodedByteAlign type: %T", obj) + return nil, fmt.Errorf("EncodedByteAlign is invalid") + } + } + + encoder.Rows = 0 + obj = decodeParams.Get("Rows") + if obj != nil { + rows, ok := obj.(*PdfObjectInteger) + if !ok { + return nil, fmt.Errorf("Rows is invalid") + } + + encoder.Columns = int(*rows) + } + + encoder.EndOfBlock = true + obj = decodeParams.Get("EndOfBlock") + if obj != nil { + switch eob := obj.(type) { + case *PdfObjectInteger: + if eob != nil && *eob <= 0 { + encoder.EndOfBlock = false + } + case *PdfObjectBool: + if eob != nil { + encoder.EndOfBlock = bool(*eob) + } + default: + common.Log.Trace("EncodedByteAlign type: %T", obj) + return nil, fmt.Errorf("EncodedByteAlign is invalid") + } + } + + encoder.DamagedRowsBeforeError = 0 + obj = decodeParams.Get("DamagedRowsBeforeError") + if obj != nil { + rows, ok := obj.(*PdfObjectInteger) + if !ok { + return nil, fmt.Errorf("DamagedRowsBeforeError is invalid") + } + + encoder.DamagedRowsBeforeError = int(*rows) + } + + common.Log.Trace("decode params: %s", decodeParams.String()) + return encoder, nil } -func (enc *CCITTFaxEncoder) DecodeStream(streamObj *PdfObjectStream) ([]byte, error) { - common.Log.Debug("Error: Attempting to use unsupported encoding %s", enc.GetFilterName()) - return streamObj.Stream, ErrNoCCITTFaxDecode +/*func getPixels(file io.Reader) ([][]byte, error) { + img, _, err := goimage.Decode(file) + if err != nil { + return nil, err + } + bounds := img.Bounds() + w, h := bounds.Max.X, bounds.Max.Y + var pixels [][]byte + for y := 0; y < h; y++ { + var row []byte + for x := 0; x < w; x++ { + r, g, b, _ := img.At(x, y).RGBA() + if r == 65535 && g == 65535 && b == 65535 { + // append white + row = append(row, 1) + } else { + row = append(row, 0) + } + } + pixels = append(pixels, row) + } + return pixels, nil +}*/ + +func (this *CCITTFaxEncoder) DecodeBytes(encoded []byte) ([]byte, error) { + encoder := &ccittfaxdecode.Encoder{ + K: this.K, + Columns: this.Columns, + EndOfLine: this.EndOfLine, + EndOfBlock: this.EndOfBlock, + BlackIs1: this.BlackIs1, + DamagedRowsBeforeError: this.DamagedRowsBeforeError, + Rows: this.Rows, + EncodedByteAlign: this.EncodedByteAlign, + } + + pixels, err := encoder.Decode(encoded) + if err != nil { + return nil, err + } + + decoded := make([]byte, len(pixels)*len(pixels[0])*3) + + decodedInd := 0 + for i := range pixels { + for j := range pixels[i] { + if pixels[i][j] == 1 { + decoded[decodedInd] = 255 + decoded[decodedInd+1] = 255 + decoded[decodedInd+2] = 255 + //decoded[decodedInd+3] = 255 + } else { + decoded[decodedInd] = 0 + decoded[decodedInd+1] = 0 + decoded[decodedInd+2] = 0 + //decoded[decodedInd+3] = 255 + } + + decodedInd += 3 + } + } + + /*goimage.RegisterFormat("png", "png", png.Decode, png.DecodeConfig) + t := goimage.NewRGBA(goimage.Rect(0, 0, len(pixels[0]), len(pixels))) + t.Pix = decoded + file, err := os.Create("/home/darkrengarius/Downloads/test2222222.png") + if err != nil { + log.Fatalf("Error opening file: %v\n", err) + } + defer file.Close() + err = png.Encode(file, t) + if err != nil { + return nil, err + }*/ + + /*file, err := os.Open("/home/darkrengarius/Downloads/scan223.png") + if err != nil { + log.Fatalf("Error opening file: %v\n", err) + } + defer file.Close() + img, _, err := goimage.Decode(file) + if err != nil { + return nil, err + } + bounds := img.Bounds() + w, h := bounds.Max.X, bounds.Max.Y + for y := 0; y < h; y++ { + var row []byte + for x := 0; x < w; x++ { + r, g, b, _ := img.At(x, y).RGBA() + if r == 65535 && g == 65535 && b == 65535 { + // append white + row = append(row, 1) + } else { + row = append(row, 0) + } + } + pixels = append(pixels, row) + }*/ + + /*img := image.NewRGBA(image.Rect(0, 0, len(pixels[0]), len(pixels))) + imgPix := 0 + for i := range pixels { + for j := range pixels[i] { + //log.Printf("%v: %v\n", i, j) + img.Pix[imgPix] = 255 * pixels[i][j] + img.Pix[imgPix+1] = 255 * pixels[i][j] + img.Pix[imgPix+2] = 255 * pixels[i][j] + img.Pix[imgPix+3] = 255 + imgPix += 4 + } + } + buf := new(bytes.Buffer) + err := png.Encode(buf, img) + if err != nil { + return nil, err + } + imgData := buf.Bytes() + f, err := os.Create("/home/darkrengarius/Downloads/testDecodePdf.png") + if err != nil { + log.Fatalf("Error writing to file: %v\n", err) + } + defer f.Close() + if _, err := f.Write(imgData); err != nil { + log.Fatalf("Error writing to file: %v\n", err) + }*/ + + /*arr, err := ccittfaxdecode.NewCCITTFaxDecoder(uint(this.Columns), encoded).Decode() + if err != nil { + return nil, err + } + result := make([]byte, 0) + for i := range arr { + result = append(result, arr[i]...) + }*/ + + return decoded, nil } -func (enc *CCITTFaxEncoder) EncodeBytes(data []byte) ([]byte, error) { - common.Log.Debug("Error: Attempting to use unsupported encoding %s", enc.GetFilterName()) +func (this *CCITTFaxEncoder) DecodeStream(streamObj *PdfObjectStream) ([]byte, error) { + return this.DecodeBytes(streamObj.Stream) +} + +func (this *CCITTFaxEncoder) EncodeBytes(data []byte) ([]byte, error) { + /*file, err := os.Open("/home/darkrengarius/Downloads/scan223.png") + if err != nil { + log.Fatalf("Error opening file: %v\n", err) + } + defer file.Close() + img, _, err := image.Decode(file) + if err != nil { + return nil, err + } + bounds := img.Bounds() + w, h := bounds.Max.X, bounds.Max.Y + var pixels [][]byte + for y := 0; y < h; y++ { + var row []byte + for x := 0; x < w; x++ { + if y == 8 && x == 12 { + log.Println() + } + r, g, b, _ := img.At(x, y).RGBA() + if r == 65535 && g == 65535 && b == 65535 { + // append white + row = append(row, 1) + } else { + row = append(row, 0) + } + } + pixels = append(pixels, row) + }*/ + + var pixels [][]byte + + for i := 0; i < len(data); i += 3 * this.Columns { + pixelsRow := make([]byte, this.Columns) + + pixel := 0 + for j := 0; j < 3*this.Columns; j += 3 { + + // TODO: check BlackIs1 + if data[i+j] == 255 { + if this.BlackIs1 { + pixelsRow[pixel] = 0 + } else { + pixelsRow[pixel] = 1 + } + } else { + if this.BlackIs1 { + pixelsRow[pixel] = 1 + } else { + pixelsRow[pixel] = 0 + } + } + + pixel++ + } + + pixels = append(pixels, pixelsRow) + } + + encoder := &ccittfaxdecode.Encoder{ + K: this.K, + Columns: this.Columns, + EndOfLine: this.EndOfLine, + EndOfBlock: this.EndOfBlock, + BlackIs1: this.BlackIs1, + DamagedRowsBeforeError: this.DamagedRowsBeforeError, + Rows: this.Rows, + EncodedByteAlign: this.EncodedByteAlign, + } + + return encoder.Encode(pixels), nil + + common.Log.Debug("Error: Attempting to use unsupported encoding %s", this.GetFilterName()) return data, ErrNoCCITTFaxDecode } diff --git a/pdf/core/stream.go b/pdf/core/stream.go index f45a0c01..53e9f3a1 100644 --- a/pdf/core/stream.go +++ b/pdf/core/stream.go @@ -68,7 +68,7 @@ func NewEncoderFromStream(streamObj *PdfObjectStream) (StreamEncoder, error) { } else if *method == StreamEncodingFilterNameASCII85 || *method == "A85" { return NewASCII85Encoder(), nil } else if *method == StreamEncodingFilterNameCCITTFax { - return NewCCITTFaxEncoder(), nil + return newCCITTFaxEncoderFromStream(streamObj, nil) } else if *method == StreamEncodingFilterNameJBIG2 { return NewJBIG2Encoder(), nil } else if *method == StreamEncodingFilterNameJPX {