github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/ps/parser_test.go (about)

     1  /*
     2   * This file is subject to the terms and conditions defined in
     3   * file 'LICENSE.md', which is part of this source code package.
     4   */
     5  
     6  package ps
     7  
     8  import (
     9  	"bufio"
    10  	"bytes"
    11  	"fmt"
    12  	"math"
    13  	"testing"
    14  
    15  	"github.com/unidoc/unidoc/common"
    16  )
    17  
    18  func init() {
    19  	common.SetLogger(common.NewConsoleLogger(common.LogLevelDebug))
    20  	//common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
    21  }
    22  
    23  func makeReaderForText(txt string) *bufio.Reader {
    24  	buf := []byte(txt)
    25  	bufReader := bytes.NewReader(buf)
    26  	bufferedReader := bufio.NewReader(bufReader)
    27  	return bufferedReader
    28  }
    29  
    30  func quickEval(progText string) (PSObject, error) {
    31  	parser := NewPSParser([]byte(progText))
    32  
    33  	prog, err := parser.Parse()
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	fmt.Printf("%s\n", progText)
    39  	fmt.Printf("-> Program: %s\n", prog.DebugString())
    40  
    41  	exec := NewPSExecutor(prog)
    42  
    43  	outputs, err := exec.Execute(nil)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	if len(outputs) != 1 {
    49  		return nil, fmt.Errorf("Stack result has too many values (>1)")
    50  	}
    51  
    52  	stack := PSStack(outputs)
    53  	fmt.Printf("=> Result Stack: %s\n", stack.DebugString())
    54  
    55  	return outputs[0], nil
    56  }
    57  
    58  func quickTest(progText string) (*PSStack, error) {
    59  	parser := NewPSParser([]byte(progText))
    60  
    61  	prog, err := parser.Parse()
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	fmt.Printf("%s\n", progText)
    66  	fmt.Printf("-> Program: %s\n", prog.DebugString())
    67  
    68  	exec := NewPSExecutor(prog)
    69  
    70  	outputs, err := exec.Execute(nil)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	stack := PSStack(outputs)
    75  
    76  	return &stack, nil
    77  }
    78  
    79  func TestAdd1(t *testing.T) {
    80  	progText := "{ 1 1 add }"
    81  
    82  	obj, err := quickEval(progText)
    83  	if err != nil {
    84  		t.Errorf("Error: %v", err)
    85  		return
    86  	}
    87  
    88  	val, ok := obj.(*PSInteger)
    89  	if !ok {
    90  		t.Errorf("Wrong output type")
    91  		return
    92  	}
    93  
    94  	if val.Val != 2 {
    95  		t.Errorf("Wrong result")
    96  		return
    97  	}
    98  }
    99  
   100  func TestAdd2(t *testing.T) {
   101  	progText := "{ 1.1 1 add 3 4 add add }"
   102  
   103  	obj, err := quickEval(progText)
   104  	if err != nil {
   105  		t.Errorf("Error: %v", err)
   106  		return
   107  	}
   108  
   109  	val, ok := obj.(*PSReal)
   110  	if !ok {
   111  		t.Errorf("Wrong output type")
   112  		return
   113  	}
   114  	if math.Abs(val.Val-9.1) > TOLERANCE {
   115  		t.Errorf("Wrong result")
   116  		return
   117  	}
   118  }
   119  
   120  //// 8.3 6.6 sub -> 1.7 (real)
   121  // 8 6.3 sub -> 1.7 (real)
   122  // 8 6 sub -> 2 (int)
   123  func TestSub1(t *testing.T) {
   124  	progText := "{ 8.3 6.6 sub }"
   125  
   126  	obj, err := quickEval(progText)
   127  	if err != nil {
   128  		t.Errorf("Error: %v", err)
   129  		return
   130  	}
   131  
   132  	val, ok := obj.(*PSReal)
   133  	if !ok {
   134  		t.Errorf("Wrong output type")
   135  		return
   136  	}
   137  	if math.Abs(val.Val-1.7) > TOLERANCE {
   138  		t.Errorf("Wrong result")
   139  		return
   140  	}
   141  }
   142  
   143  func TestSub2(t *testing.T) {
   144  	progText := "{ 8 6.3 sub }"
   145  
   146  	obj, err := quickEval(progText)
   147  	if err != nil {
   148  		t.Errorf("Error: %v", err)
   149  		return
   150  	}
   151  
   152  	val, ok := obj.(*PSReal)
   153  	if !ok {
   154  		t.Errorf("Wrong output type")
   155  		return
   156  	}
   157  	if math.Abs(val.Val-1.7) > TOLERANCE {
   158  		t.Errorf("Wrong result")
   159  		return
   160  	}
   161  }
   162  
   163  func TestSub3(t *testing.T) {
   164  	progText := "{ 8 6 sub }"
   165  
   166  	obj, err := quickEval(progText)
   167  	if err != nil {
   168  		t.Errorf("Error: %v", err)
   169  		return
   170  	}
   171  
   172  	val, ok := obj.(*PSInteger)
   173  	if !ok {
   174  		t.Errorf("Wrong output type")
   175  		return
   176  	}
   177  	if val.Val != 2 {
   178  		t.Errorf("Wrong result")
   179  		return
   180  	}
   181  }
   182  
   183  // 6 + (3/8) -> 6.375
   184  // 3 8 div 6 add
   185  // 6 3 8 div add
   186  //
   187  // 8 - (7*3) -> -13
   188  // 8 7 3 mul sub
   189  // 7 3 mul 8 exch sub
   190  // Simple test entry with a single expected PSObject output.
   191  type SimpleTestEntry struct {
   192  	progText string
   193  	expected PSObject
   194  }
   195  
   196  func TestArithmetics(t *testing.T) {
   197  	testcases := []SimpleTestEntry{
   198  		{progText: "{ 3 8 div 6 add }", expected: MakeReal(6.375)},
   199  		{progText: "{ 6 3 8 div add }", expected: MakeReal(6.375)},
   200  		{progText: "{ 8 7 3 mul sub }", expected: MakeInteger(-13)},
   201  		{progText: "{ 7 3 mul 8 exch sub }", expected: MakeInteger(-13)},
   202  	}
   203  
   204  	for _, testcase := range testcases {
   205  		obj, err := quickEval(testcase.progText)
   206  		if err != nil {
   207  			t.Errorf("Error: %v", err)
   208  			return
   209  		}
   210  
   211  		// Maybe not the most robust test (comparing the strings), but should do.
   212  		if obj.DebugString() != testcase.expected.DebugString() {
   213  			t.Errorf("Wrong result: %s != %s", obj.DebugString(), testcase.expected.DebugString())
   214  			return
   215  		}
   216  	}
   217  }
   218  
   219  // Complex test entry can have a more complex output.
   220  type ComplexTestEntry struct {
   221  	progText string
   222  	expected string
   223  }
   224  
   225  func TestStackOperations(t *testing.T) {
   226  	testcases := []ComplexTestEntry{
   227  		{progText: "{ 7 8 9 3 1 roll }", expected: "[ int:9 int:7 int:8 ]"},
   228  		{progText: "{ 7 8 9 3 -1 roll }", expected: "[ int:8 int:9 int:7 ]"},
   229  		{progText: "{ 9 7 8 3 -1 roll }", expected: "[ int:7 int:8 int:9 ]"},
   230  		{progText: "{ 1 1 0.2 7 8 9 3 1 roll }", expected: "[ int:1 int:1 real:0.20000 int:9 int:7 int:8 ]"},
   231  	}
   232  
   233  	for _, testcase := range testcases {
   234  		stack, err := quickTest(testcase.progText)
   235  		if err != nil {
   236  			t.Errorf("Error: %v", err)
   237  			return
   238  		}
   239  
   240  		// Maybe not the most robust test (comparing the strings), but should do.
   241  		if stack.DebugString() != testcase.expected {
   242  			t.Errorf("Wrong result: '%s' != '%s'", stack.DebugString(), testcase.expected)
   243  			return
   244  		}
   245  	}
   246  }
   247  
   248  func TestFunctionOperations(t *testing.T) {
   249  	testcases := []ComplexTestEntry{
   250  		// atan
   251  		{progText: "{ 0 1 atan }", expected: "[ real:0.00000 ]"},
   252  		{progText: "{ 1 0 atan }", expected: "[ real:90.00000 ]"},
   253  		{progText: "{ -100 0 atan }", expected: "[ real:270.00000 ]"},
   254  		{progText: "{ 4 4 atan }", expected: "[ real:45.00000 ]"},
   255  	}
   256  
   257  	for _, testcase := range testcases {
   258  		stack, err := quickTest(testcase.progText)
   259  		if err != nil {
   260  			t.Errorf("Error: %v", err)
   261  			return
   262  		}
   263  
   264  		// Maybe not the most robust test (comparing the strings), but should do.
   265  		if stack.DebugString() != testcase.expected {
   266  			t.Errorf("Wrong result: '%s' != '%s'", stack.DebugString(), testcase.expected)
   267  			return
   268  		}
   269  	}
   270  }
   271  
   272  func TestVariousCases(t *testing.T) {
   273  	testcases := []ComplexTestEntry{
   274  		// dup
   275  		{progText: "{ 99 dup }", expected: "[ int:99 int:99 ]"},
   276  		// ceiling
   277  		{progText: "{ 3.2 ceiling }", expected: "[ real:4.00000 ]"},
   278  		{progText: "{ -4.8 ceiling }", expected: "[ real:-4.00000 ]"},
   279  		{progText: "{ 99 ceiling }", expected: "[ int:99 ]"},
   280  		// floor
   281  		{progText: "{ 3.2 floor }", expected: "[ real:3.00000 ]"},
   282  		{progText: "{ -4.8 floor }", expected: "[ real:-5.00000 ]"},
   283  		{progText: "{ 99 floor }", expected: "[ int:99 ]"},
   284  		// exp
   285  		{progText: "{ 9 0.5 exp }", expected: "[ real:3.00000 ]"},
   286  		{progText: "{ -9 -1 exp }", expected: "[ real:-0.11111 ]"},
   287  		// and
   288  		{progText: "{ true true and }", expected: "[ bool:true ]"},
   289  		{progText: "{ true false and }", expected: "[ bool:false ]"},
   290  		{progText: "{ false true and }", expected: "[ bool:false ]"},
   291  		{progText: "{ false false and }", expected: "[ bool:false ]"},
   292  		{progText: "{ 99 1 and }", expected: "[ int:1 ]"},
   293  		{progText: "{ 52 7 and }", expected: "[ int:4 ]"},
   294  		// bitshift
   295  		{progText: "{ 7 3 bitshift }", expected: "[ int:56 ]"},
   296  		{progText: "{ 142 -3 bitshift }", expected: "[ int:17 ]"},
   297  		// copy
   298  		{progText: "{ 7 3 2 copy }", expected: "[ int:7 int:3 int:7 int:3 ]"},
   299  		{progText: "{ 7 3 0 copy }", expected: "[ int:7 int:3 ]"},
   300  		// cos
   301  		{progText: "{ 0 cos }", expected: "[ real:1.00000 ]"},
   302  		{progText: "{ 90 cos }", expected: "[ real:0.00000 ]"},
   303  		// eq.
   304  		{progText: "{ 4.0 4 eq }", expected: "[ bool:true ]"},
   305  		{progText: "{ 4 4.0 eq }", expected: "[ bool:true ]"},
   306  		{progText: "{ 4.0 4.0 eq }", expected: "[ bool:true ]"},
   307  		{progText: "{ 4 4 eq }", expected: "[ bool:true ]"},
   308  		{progText: "{ -4 4 eq }", expected: "[ bool:false ]"},
   309  		{progText: "{ false false eq }", expected: "[ bool:true ]"},
   310  		{progText: "{ true false eq }", expected: "[ bool:false ]"},
   311  		{progText: "{ true 4 eq }", expected: "[ bool:false ]"},
   312  		// ge
   313  		{progText: "{ 4.2 4 ge }", expected: "[ bool:true ]"},
   314  		{progText: "{ 4 4 ge }", expected: "[ bool:true ]"},
   315  		{progText: "{ 3.9 4 ge }", expected: "[ bool:false ]"},
   316  		// gt
   317  		{progText: "{ 4.2 4 gt }", expected: "[ bool:true ]"},
   318  		{progText: "{ 4 4 gt }", expected: "[ bool:false ]"},
   319  		{progText: "{ 3.9 4 gt }", expected: "[ bool:false ]"},
   320  		// if
   321  		{progText: "{ 4.2 4 gt {5} if }", expected: "[ int:5 ]"},
   322  		{progText: "{ 4.2 4 gt {4.0 4.0 ge {3} if} if}", expected: "[ int:3 ]"},
   323  		{progText: "{ 4.0 4.0 gt {5} if }", expected: "[ ]"},
   324  		// ifelse
   325  		{progText: "{ 4.2 4 gt {5} {4} ifelse }", expected: "[ int:5 ]"},
   326  		{progText: "{ 3 4 gt {5} {4} ifelse }", expected: "[ int:4 ]"},
   327  		// index
   328  		{progText: "{ 0 1 2 3 4 5 2 index }", expected: "[ int:0 int:1 int:2 int:3 int:4 int:5 int:3 ]"},
   329  		{progText: "{ 9 8 7 2 index }", expected: "[ int:9 int:8 int:7 int:9 ]"},
   330  		// le
   331  		{progText: "{ 4.2 4 le }", expected: "[ bool:false ]"},
   332  		{progText: "{ 4 4 le }", expected: "[ bool:true ]"},
   333  		{progText: "{ 3.9 4 le }", expected: "[ bool:true ]"},
   334  		// ln
   335  		{progText: "{ 10 ln }", expected: "[ real:2.30259 ]"},
   336  		{progText: "{ 100 ln }", expected: "[ real:4.60517 ]"},
   337  		// log
   338  		{progText: "{ 10 log }", expected: "[ real:1.00000 ]"},
   339  		{progText: "{ 100 log }", expected: "[ real:2.00000 ]"},
   340  		// lt
   341  		{progText: "{ 4.2 4 lt }", expected: "[ bool:false ]"},
   342  		{progText: "{ 4 4 lt }", expected: "[ bool:false ]"},
   343  		{progText: "{ 3.9 4 lt }", expected: "[ bool:true ]"},
   344  		// ne
   345  		{progText: "{ 4.0 4 ne }", expected: "[ bool:false ]"},
   346  		{progText: "{ 4 4.0 ne }", expected: "[ bool:false ]"},
   347  		{progText: "{ 4.0 4.0 ne }", expected: "[ bool:false ]"},
   348  		{progText: "{ 4 4 ne }", expected: "[ bool:false ]"},
   349  		{progText: "{ -4 4 ne }", expected: "[ bool:true ]"},
   350  		{progText: "{ false false ne }", expected: "[ bool:false ]"},
   351  		{progText: "{ true false ne }", expected: "[ bool:true ]"},
   352  		{progText: "{ true 4 ne }", expected: "[ bool:true ]"},
   353  		// neg
   354  		// not
   355  		{progText: "{ true not }", expected: "[ bool:false ]"},
   356  		{progText: "{ false not }", expected: "[ bool:true ]"},
   357  		{progText: "{ 52 not }", expected: "[ int:-53 ]"},
   358  		// or
   359  		{progText: "{ true true or }", expected: "[ bool:true ]"},
   360  		{progText: "{ true false or }", expected: "[ bool:true ]"},
   361  		{progText: "{ false true or }", expected: "[ bool:true ]"},
   362  		{progText: "{ false false or }", expected: "[ bool:false ]"},
   363  		{progText: "{ 17 5 or }", expected: "[ int:21 ]"},
   364  		// pop
   365  		{progText: "{ 1 2 3 pop }", expected: "[ int:1 int:2 ]"},
   366  		{progText: "{ 1 2 pop }", expected: "[ int:1 ]"},
   367  		{progText: "{ 1 pop }", expected: "[ ]"},
   368  		// round
   369  		{progText: "{ 3.2 round }", expected: "[ real:3.00000 ]"},
   370  		{progText: "{ 6.5 round }", expected: "[ real:7.00000 ]"},
   371  		{progText: "{ -4.8 round }", expected: "[ real:-5.00000 ]"},
   372  		{progText: "{ -6.5 round }", expected: "[ real:-6.00000 ]"},
   373  		{progText: "{ 99 round }", expected: "[ int:99 ]"},
   374  		// roll
   375  		{progText: "{ 1 2 3 3 -1 roll }", expected: "[ int:2 int:3 int:1 ]"},
   376  		{progText: "{ 1 2 3 3 1 roll }", expected: "[ int:3 int:1 int:2 ]"},
   377  		{progText: "{ 1 2 3 3 0 roll }", expected: "[ int:1 int:2 int:3 ]"},
   378  		// sin
   379  		{progText: "{ 0 sin }", expected: "[ real:0.00000 ]"},
   380  		{progText: "{ 90 sin }", expected: "[ real:1.00000 ]"},
   381  		// sqrt
   382  		{progText: "{ 4 sqrt }", expected: "[ real:2.00000 ]"},
   383  		{progText: "{ 2 sqrt }", expected: "[ real:1.41421 ]"},
   384  		// truncate
   385  		{progText: "{ 3.2 truncate }", expected: "[ real:3.00000 ]"},
   386  		{progText: "{ -4.8 truncate }", expected: "[ real:-4.00000 ]"},
   387  		{progText: "{ 99 truncate }", expected: "[ int:99 ]"},
   388  		// xor
   389  		{progText: "{ true true xor }", expected: "[ bool:false ]"},
   390  		{progText: "{ true false xor }", expected: "[ bool:true ]"},
   391  		{progText: "{ false true xor }", expected: "[ bool:true ]"},
   392  		{progText: "{ false false xor }", expected: "[ bool:false ]"},
   393  		{progText: "{ 7 3 xor }", expected: "[ int:4 ]"},
   394  		{progText: "{ 12 3 xor }", expected: "[ int:15 ]"},
   395  	}
   396  
   397  	for _, testcase := range testcases {
   398  		stack, err := quickTest(testcase.progText)
   399  		if err != nil {
   400  			t.Errorf("Error: %v", err)
   401  			return
   402  		}
   403  
   404  		// Maybe not the most robust test (comparing the strings), but should do.
   405  		if stack.DebugString() != testcase.expected {
   406  			t.Errorf("Wrong result: '%s' != '%s'", stack.DebugString(), testcase.expected)
   407  			return
   408  		}
   409  	}
   410  }
   411  
   412  func TestTintTransform1(t *testing.T) {
   413  	testcases := []ComplexTestEntry{
   414  		// from corpus epson_pages3_color_pages1.pdf.
   415  		{progText: "{ 0.0000 dup 0 mul exch dup 0 mul exch dup 0 mul exch 1 mul }", expected: "[ real:0.00000 real:0.00000 real:0.00000 real:0.00000 ]"},
   416  	}
   417  
   418  	for _, testcase := range testcases {
   419  		stack, err := quickTest(testcase.progText)
   420  		if err != nil {
   421  			t.Errorf("Error: %v", err)
   422  			return
   423  		}
   424  
   425  		// Maybe not the most robust test (comparing the strings), but should do.
   426  		if stack.DebugString() != testcase.expected {
   427  			t.Errorf("Wrong result: '%s' != '%s'", stack.DebugString(), testcase.expected)
   428  			return
   429  		}
   430  	}
   431  }