github.com/andybalholm/giopdf@v0.0.0-20220317170119-aad9a095ad48/render.go (about)

     1  package giopdf
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"gioui.org/op"
     7  	"github.com/andybalholm/giopdf/pdf"
     8  )
     9  
    10  // RenderPage draws the contents of a PDF page to ops.
    11  // The caller should do the appropriate transformation and scaling to ensure
    12  // that the content is not rendered upside down. (The PDF coordinate system
    13  // starts in the lower left, not in the upper left like Gio's.)
    14  func RenderPage(ops *op.Ops, page pdf.Page) error {
    15  	c := NewCanvas(ops)
    16  
    17  	stream := page.V.Key("Contents")
    18  	cs := pdf.NewContentStream(stream.Reader())
    19  
    20  	for {
    21  		args, op := cs.ReadInstruction()
    22  		switch op {
    23  		case "":
    24  			return nil
    25  		default:
    26  			fmt.Println(args, op)
    27  
    28  		case "B", "B*":
    29  			c.FillAndStroke()
    30  		case "BT":
    31  			c.BeginText()
    32  		case "c":
    33  			c.CurveTo(args[0].Float32(), args[1].Float32(), args[2].Float32(), args[3].Float32(), args[4].Float32(), args[5].Float32())
    34  		case "cm":
    35  			c.Transform(args[0].Float32(), args[1].Float32(), args[2].Float32(), args[3].Float32(), args[4].Float32(), args[5].Float32())
    36  		case "d":
    37  			array := args[0]
    38  			phase := args[1].Float32()
    39  			dashes := make([]float32, array.Len())
    40  			for i := range dashes {
    41  				dashes[i] = array.Index(i).Float32()
    42  			}
    43  			c.SetDash(dashes, phase)
    44  		case "Do":
    45  			x := page.Resources().Key("XObject").Key(args[0].Name())
    46  			if x.IsNull() {
    47  				fmt.Printf("XObject resource missing: %v", args[0])
    48  				continue
    49  			}
    50  			switch x.Key("Subtype").Name() {
    51  			case "Image":
    52  				img, err := decodeImage(x)
    53  				if err != nil {
    54  					fmt.Println(err)
    55  					continue
    56  				}
    57  				c.Image(img)
    58  			default:
    59  				fmt.Printf("Unsupported XObject: %v\n", x)
    60  			}
    61  		case "ET":
    62  			c.EndText()
    63  		case "f", "f*":
    64  			c.Fill()
    65  		case "G":
    66  			c.SetStrokeGray(args[0].Float32())
    67  		case "g":
    68  			c.SetFillGray(args[0].Float32())
    69  		case "gs":
    70  			gs := page.Resources().Key("ExtGState").Key(args[0].Name())
    71  			if gs.IsNull() {
    72  				fmt.Printf("ExtGState resource missing: %v", args[0])
    73  				continue
    74  			}
    75  			for _, k := range gs.Keys() {
    76  				v := gs.Key(k)
    77  				switch k {
    78  				case "Type":
    79  					// ignore
    80  				case "LW":
    81  					c.SetLineWidth(v.Float32())
    82  				case "LC":
    83  					c.SetLineCap(v.Int())
    84  				case "LJ":
    85  					c.SetLineJoin(v.Int())
    86  				case "ML":
    87  					c.SetMiterLimit(v.Float32())
    88  				case "D":
    89  					array := v.Index(0)
    90  					phase := v.Index(1).Float32()
    91  					dashes := make([]float32, array.Len())
    92  					for i := range dashes {
    93  						dashes[i] = array.Index(i).Float32()
    94  					}
    95  					c.SetDash(dashes, phase)
    96  				case "CA":
    97  					c.SetStrokeAlpha(v.Float32())
    98  				case "ca":
    99  					c.SetFillAlpha(v.Float32())
   100  				case "BM":
   101  					if v.Name() != "Normal" {
   102  						fmt.Printf("Unsupported blend mode: %v\n", v)
   103  					}
   104  				default:
   105  					fmt.Printf("Unsupported graphics state parameter %v = %v\n", k, v)
   106  				}
   107  			}
   108  		case "h":
   109  			c.ClosePath()
   110  		case "J":
   111  			c.SetLineCap(args[0].Int())
   112  		case "j":
   113  			c.SetLineJoin(args[0].Int())
   114  		case "l":
   115  			c.LineTo(args[0].Float32(), args[1].Float32())
   116  		case "m":
   117  			c.MoveTo(args[0].Float32(), args[1].Float32())
   118  		case "n":
   119  			c.NoOpPaint()
   120  		case "Q":
   121  			c.Restore()
   122  		case "q":
   123  			c.Save()
   124  		case "re":
   125  			x := args[0].Float32()
   126  			y := args[1].Float32()
   127  			width := args[2].Float32()
   128  			height := args[3].Float32()
   129  			c.Rectangle(x, y, width, height)
   130  		case "RG":
   131  			c.SetRGBStrokeColor(args[0].Float32(), args[1].Float32(), args[2].Float32())
   132  		case "rg":
   133  			c.SetRGBFillColor(args[0].Float32(), args[1].Float32(), args[2].Float32())
   134  		case "S":
   135  			c.Stroke()
   136  		case "Td":
   137  			c.TextMove(args[0].Float32(), args[1].Float32())
   138  		case "Tf":
   139  			fd := page.Font(args[0].Name())
   140  			if fd.V.IsNull() {
   141  				fmt.Printf("Font resource missing: %v\n", args[0])
   142  				continue
   143  			}
   144  			f, err := importPDFFont(fd)
   145  			if err != nil {
   146  				fmt.Println("Error importing font:", err)
   147  				continue
   148  			}
   149  			c.SetFont(f, args[1].Float32())
   150  		case "TJ":
   151  			if c.font == nil {
   152  				// TODO: remove
   153  				continue
   154  			}
   155  			a := args[0]
   156  			for i := 0; i < a.Len(); i++ {
   157  				v := a.Index(i)
   158  				switch v.Kind() {
   159  				case pdf.Real, pdf.Integer:
   160  					c.Kern(v.Float32())
   161  				case pdf.String:
   162  					c.ShowText(v.RawString())
   163  				}
   164  			}
   165  		case "Tj":
   166  			c.ShowText(args[0].RawString())
   167  		case "Tm":
   168  			c.SetTextMatrix(args[0].Float32(), args[1].Float32(), args[2].Float32(), args[3].Float32(), args[4].Float32(), args[5].Float32())
   169  		case "Tr":
   170  			c.SetTextRendering(args[0].Int())
   171  		case "Tz":
   172  			c.SetHScale(args[0].Float32())
   173  		case "v":
   174  			c.CurveV(args[0].Float32(), args[1].Float32(), args[2].Float32(), args[3].Float32())
   175  		case "W", "W*":
   176  			c.Clip()
   177  		case "w":
   178  			c.SetLineWidth(args[0].Float32())
   179  		}
   180  	}
   181  
   182  	return nil
   183  }