mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-04 22:17:22 +08:00
Add CCITTFaxDecode filter
This commit is contained in:
parent
15f437dc92
commit
43625d5c66
1074
pdf/core/ccittfaxdecode/codes.go
Normal file
1074
pdf/core/ccittfaxdecode/codes.go
Normal file
File diff suppressed because it is too large
Load Diff
733
pdf/core/ccittfaxdecode/decode.go
Normal file
733
pdf/core/ccittfaxdecode/decode.go
Normal 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)
|
||||
}
|
292
pdf/core/ccittfaxdecode/decode_test.go
Normal file
292
pdf/core/ccittfaxdecode/decode_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
70
pdf/core/ccittfaxdecode/decoding_tree.go
Normal file
70
pdf/core/ccittfaxdecode/decoding_tree.go
Normal 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
|
||||
}
|
539
pdf/core/ccittfaxdecode/encoder.go
Normal file
539
pdf/core/ccittfaxdecode/encoder.go
Normal 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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user