Add CCITTFaxDecode filter

This commit is contained in:
nkryuchkov 2019-02-04 19:37:28 +03:00
parent 15f437dc92
commit 43625d5c66
7 changed files with 3145 additions and 18 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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)<<uint(bitPos)) << 8
bitsWritten := 8 - bitPos
bytePos++
bitPos = 0
if bytePos >= 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)
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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(i-8))&masks[8-toWrite])>>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
}

View File

@ -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
}

View File

@ -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 {