github.com/pdfcpu/pdfcpu@v0.11.1/pkg/filter/filter_test.go (about)

     1  /*
     2  Copyright 2018 The pdfcpu Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package filter_test
    18  
    19  import (
    20  	"errors"
    21  	"io"
    22  	"os"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/pdfcpu/pdfcpu/pkg/filter"
    27  )
    28  
    29  func TestFilterSupport(t *testing.T) {
    30  	var filtersTests = []struct {
    31  		filterName string
    32  		expected   error
    33  	}{
    34  		{filter.ASCII85, nil},
    35  		{filter.ASCIIHex, nil},
    36  		{filter.RunLength, nil},
    37  		{filter.LZW, nil},
    38  		{filter.Flate, nil},
    39  		{filter.CCITTFax, nil},
    40  		{filter.DCT, nil},
    41  		{filter.JBIG2, filter.ErrUnsupportedFilter},
    42  		{filter.JPX, filter.ErrUnsupportedFilter},
    43  		{"INVALID_FILTER", errors.New("Invalid filter: <INVALID_FILTER>")},
    44  	}
    45  	for _, tt := range filtersTests {
    46  		_, err := filter.NewFilter(tt.filterName, nil)
    47  		if (tt.expected != nil && err != nil && err.Error() != tt.expected.Error()) ||
    48  			((err == nil || tt.expected == nil) && err != tt.expected) {
    49  			t.Errorf("Problem: '%s' (expected '%s')\n", err.Error(), tt.expected.Error())
    50  		}
    51  	}
    52  }
    53  
    54  // Encode a test string with filterName then decode and check if result matches original.
    55  func encodeDecodeString(t *testing.T, filterName string) {
    56  	t.Helper()
    57  
    58  	filter, err := filter.NewFilter(filterName, nil)
    59  	if err != nil {
    60  		t.Fatalf("Problem: %v\n", err)
    61  	}
    62  
    63  	want := "Hello, Gopher!"
    64  	t.Logf("encoding using filter %s: len:%d % X <%s>\n", filterName, len(want), want, want)
    65  
    66  	b1, err := filter.Encode(strings.NewReader(want))
    67  	if err != nil {
    68  		t.Fatalf("Problem encoding 1: %v\n", err)
    69  	}
    70  	//t.Logf("encoded 1:  len:%d % X <%s>\n", b1.Len(), b1.Bytes(), b1.Bytes())
    71  
    72  	b2, err := filter.Encode(b1)
    73  	if err != nil {
    74  		t.Fatalf("Problem encoding 2: %v\n", err)
    75  	}
    76  	//t.Logf("encoded 2:  len:%d % X <%s>\n", b2.Len(), b2.Bytes(), b2.Bytes())
    77  
    78  	c1, err := filter.Decode(b2)
    79  	if err != nil {
    80  		t.Fatalf("Problem decoding 2: %v\n", err)
    81  	}
    82  	//t.Logf("decoded 2:  len:%d % X <%s>\n", c1.Len(), c1.Bytes(), c1.Bytes())
    83  
    84  	c2, err := filter.Decode(c1)
    85  	if err != nil {
    86  		t.Fatalf("Problem decoding 1: %v\n", err)
    87  	}
    88  	//t.Logf("decoded 1:  len:%d % X <%s>\n", c2.Len(), c2.Bytes(), c2.Bytes())
    89  
    90  	bb, err := io.ReadAll(c2)
    91  	if err != nil {
    92  		t.Fatalf("%v\n", err)
    93  	}
    94  	got := string(bb)
    95  	if got != want {
    96  		t.Fatalf("got:%s want:%s\n", got, want)
    97  	}
    98  }
    99  
   100  func TestEncodeDecodeString(t *testing.T) {
   101  	for _, f := range filter.List() {
   102  		encodeDecodeString(t, f)
   103  	}
   104  }
   105  
   106  var filenames = []string{
   107  	"testdata/gettysburg.txt",
   108  	"testdata/e.txt",
   109  	"testdata/pi.txt",
   110  	"testdata/Mark.Twain-Tom.Sawyer.txt",
   111  }
   112  
   113  // Encode fileName with filterName then decode and check if result matches original.
   114  func encodeDecode(t *testing.T, fileName, filterName string) {
   115  	t.Helper()
   116  
   117  	t.Logf("testFile: %s with filter:%s\n", fileName, filterName)
   118  
   119  	f, err := filter.NewFilter(filterName, nil)
   120  	if err != nil {
   121  		t.Errorf("Problem: %v\n", err)
   122  	}
   123  
   124  	raw, err := os.Open(fileName)
   125  	if err != nil {
   126  		t.Errorf("%s: %v", fileName, err)
   127  		return
   128  	}
   129  	defer raw.Close()
   130  
   131  	enc, err := f.Encode(raw)
   132  	if err != nil {
   133  		t.Errorf("Problem encoding: %v\n", err)
   134  	}
   135  
   136  	dec, err := f.Decode(enc)
   137  	if err != nil {
   138  		t.Errorf("Problem decoding: %v\n", err)
   139  	}
   140  
   141  	// Compare decoded bytes with original bytes.
   142  	golden, err := os.Open(fileName)
   143  	if err != nil {
   144  		t.Errorf("%s: %v", fileName, err)
   145  		return
   146  	}
   147  	defer golden.Close()
   148  
   149  	g, err := io.ReadAll(golden)
   150  	if err != nil {
   151  		t.Errorf("%s: %v", fileName, err)
   152  		return
   153  	}
   154  
   155  	d, err := io.ReadAll(dec)
   156  	if err != nil {
   157  		t.Errorf("%s: %v", fileName, err)
   158  		return
   159  	}
   160  
   161  	if len(d) != len(g) {
   162  		t.Errorf("%s: length mismatch %d != %d", fileName, len(d), len(g))
   163  		return
   164  	}
   165  
   166  	for i := 0; i < len(d); i++ {
   167  		if d[i] != g[i] {
   168  			t.Errorf("%s: mismatch at %d, 0x%02x != 0x%02x\n", fileName, i, d[i], g[i])
   169  			return
   170  		}
   171  	}
   172  
   173  }
   174  
   175  func TestEncodeDecode(t *testing.T) {
   176  	for _, filterName := range filter.List() {
   177  		for _, filename := range filenames {
   178  			encodeDecode(t, filename, filterName)
   179  		}
   180  	}
   181  }
   182  
   183  func encode(t *testing.T, r io.Reader, filterName string) io.Reader {
   184  	t.Helper()
   185  
   186  	f, err := filter.NewFilter(filterName, nil)
   187  	if err != nil {
   188  		t.Errorf("Problem: %v\n", err)
   189  	}
   190  
   191  	r, err = f.Encode(r)
   192  	if err != nil {
   193  		t.Errorf("Problem encoding: %v\n", err)
   194  	}
   195  
   196  	return r
   197  }
   198  
   199  func decode(t *testing.T, r io.Reader, filterName string) io.Reader {
   200  	t.Helper()
   201  
   202  	f, err := filter.NewFilter(filterName, nil)
   203  	if err != nil {
   204  		t.Errorf("Problem: %v\n", err)
   205  	}
   206  
   207  	r, err = f.Decode(r)
   208  	if err != nil {
   209  		t.Errorf("Problem decoding: %v\n", err)
   210  	}
   211  
   212  	return r
   213  }
   214  
   215  // Encode fileName with filter pipeline then decode and check if result matches original.
   216  func encodeDecodeFilterPipeline(t *testing.T, fileName string, fpl []string) {
   217  	t.Helper()
   218  
   219  	f0, err := os.Open(fileName)
   220  	if err != nil {
   221  		t.Errorf("%s: %v", fileName, err)
   222  		return
   223  	}
   224  	defer f0.Close()
   225  
   226  	r := io.Reader(f0)
   227  
   228  	for i := len(fpl) - 1; i >= 0; i-- {
   229  		r = encode(t, r, fpl[i])
   230  	}
   231  
   232  	for _, f := range fpl {
   233  		r = decode(t, r, f)
   234  	}
   235  
   236  	// Compare decoded bytes with original bytes.
   237  	golden, err := os.Open(fileName)
   238  	if err != nil {
   239  		t.Errorf("%s: %v", fileName, err)
   240  		return
   241  	}
   242  	defer golden.Close()
   243  
   244  	g, err := io.ReadAll(golden)
   245  	if err != nil {
   246  		t.Errorf("%s: %v", fileName, err)
   247  		return
   248  	}
   249  
   250  	d, err := io.ReadAll(r)
   251  	if err != nil {
   252  		t.Errorf("%s: %v", fileName, err)
   253  		return
   254  	}
   255  
   256  	if len(d) != len(g) {
   257  		t.Errorf("%s: length mismatch %d != %d", fileName, len(d), len(g))
   258  		return
   259  	}
   260  
   261  	for i := 0; i < len(d); i++ {
   262  		if d[i] != g[i] {
   263  			t.Errorf("%s: mismatch at %d, 0x%02x != 0x%02x\n", fileName, i, d[i], g[i])
   264  			return
   265  		}
   266  	}
   267  }
   268  
   269  func TestEncodeDecodeFilterPipeline(t *testing.T) {
   270  	for _, filename := range filenames {
   271  		encodeDecodeFilterPipeline(t, filename, []string{filter.ASCII85, filter.Flate})
   272  	}
   273  }
   274  
   275  // TestASCII85DecodeWithCRLF tests that ASCII85 decoding works correctly
   276  // when the encoded data has CRLF line endings (issue #1112)
   277  func TestASCII85DecodeWithCRLF(t *testing.T) {
   278  	f, err := filter.NewFilter(filter.ASCII85, nil)
   279  	if err != nil {
   280  		t.Fatalf("Failed to create ASCII85 filter: %v", err)
   281  	}
   282  
   283  	testCases := []struct {
   284  		name     string
   285  		input    string
   286  		ending   string
   287  		expected string
   288  	}{
   289  		{"LF ending", "Hello, Gopher!", "\n", "Hello, Gopher!"},
   290  		{"CR ending", "Hello, Gopher!", "\r", "Hello, Gopher!"},
   291  		{"CRLF ending", "Hello, Gopher!", "\r\n", "Hello, Gopher!"},
   292  		{"No ending", "Hello, Gopher!", "", "Hello, Gopher!"},
   293  	}
   294  
   295  	for _, tc := range testCases {
   296  		t.Run(tc.name, func(t *testing.T) {
   297  			// Encode the input
   298  			encoded, err := f.Encode(strings.NewReader(tc.input))
   299  			if err != nil {
   300  				t.Fatalf("Encoding failed: %v", err)
   301  			}
   302  
   303  			// Read encoded data
   304  			encodedBytes, err := io.ReadAll(encoded)
   305  			if err != nil {
   306  				t.Fatalf("Reading encoded data failed: %v", err)
   307  			}
   308  
   309  			// Add the specified line ending
   310  			encodedWithEnding := append(encodedBytes, []byte(tc.ending)...)
   311  
   312  			// Decode
   313  			decoded, err := f.Decode(strings.NewReader(string(encodedWithEnding)))
   314  			if err != nil {
   315  				t.Fatalf("Decoding failed with %q ending: %v", tc.ending, err)
   316  			}
   317  
   318  			// Verify result
   319  			result, err := io.ReadAll(decoded)
   320  			if err != nil {
   321  				t.Fatalf("Reading decoded data failed: %v", err)
   322  			}
   323  
   324  			if string(result) != tc.expected {
   325  				t.Errorf("Mismatch: got %q, want %q", string(result), tc.expected)
   326  			}
   327  		})
   328  	}
   329  }