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 }