// Text takes an image and, using the freetype package, writes text in the // position specified on to the image. A color.Color, a font size and a font // must also be specified. // Finally, the (x, y) coordinate advanced by the text extents is returned. // // Note that the ParseFont helper function can be used to get a *truetype.Font // value without having to import freetype-go directly. // // If you need more control over the 'context' used to draw text (like the DPI), // then you'll need to ignore this convenience method and use your own. func (im *Image) Text(x, y int, clr color.Color, fontSize float64, font *truetype.Font, text string) (int, int, error) { // Create a solid color image textClr := image.NewUniform(clr) // Set up the freetype context... mostly boiler plate c := ftContext(font, fontSize) c.SetClip(im.Bounds()) c.SetDst(im) c.SetSrc(textClr) // Now let's actually draw the text... pt := freetype.Pt(x, y+int(c.PointToFix32(fontSize)>>8)) newpt, err := c.DrawString(text, pt) if err != nil { return 0, 0, err } return int(newpt.X / 256), int(newpt.Y / 256), nil }
func (s *diagram) png(w io.Writer) { aaLen, _ := s.g.Length.Int64() scale := (s.GraphicWidth - s.Padding*2) / float64(aaLen) aaSpace := int((20 * s.dpi / 72.0) / scale) img := image.NewRGBA(image.Rect(0, 0, int(s.GraphicWidth), int(s.GraphicHeight))) drawRectWH(img, 0, 0, s.GraphicWidth, s.GraphicHeight, color.White) fontContext.SetDst(img) fontContext.SetClip(img.Bounds()) fontContext.SetFontSize(10.0) ////// startY := s.startY poptop := startY + s.LollipopRadius popbot := poptop + s.LollipopHeight fontContext.SetSrc(&image.Uniform{color.Black}) firstLollipop := true for _, pop := range s.ticks { if !pop.isLollipop { continue } if firstLollipop { firstLollipop = false startY = popbot - (s.DomainHeight-s.BackboneHeight)/2 } c := color.RGBA{0xBA, 0xBD, 0xB6, 0xFF} thickvline(img, int(pop.x-s.dpi/144), int(pop.y), int(popbot), 2*s.dpi/72.0, c) drawCircle(img, int(pop.x+s.dpi/144), int(pop.y), int(pop.r), colorFromHex(pop.Col)) if s.ShowLabels { chg := pop.label if pop.Cnt > 1 { chg = fmt.Sprintf("%s (%d)", chg, pop.Cnt) } // FIXME: rotate label to match SVG output w, _, _ := fontContext.MeasureString(chg) fontContext.DrawString(chg, freetype.Pt( int(pop.x-(float64(freetype.Pixel(w))/2.0)), int(pop.y-(pop.r*1.5)), )) } } // draw the backbone drawRectWH(img, s.Padding, startY+(s.DomainHeight-s.BackboneHeight)/2, s.GraphicWidth-(s.Padding*2), s.BackboneHeight, color.RGBA{0xBA, 0xBD, 0xB6, 0xFF}) if !s.HideMotifs { disFill := color.RGBA{0, 0, 0, 38} // 15% opacity // draw transmembrane, signal peptide, coiled-coil, etc motifs for _, r := range s.g.Motifs { if r.Type == "pfamb" { continue } if r.Type == "disorder" && s.HideDisordered { continue } sstart, _ := r.Start.Float64() swidth, _ := r.End.Float64() sstart *= scale swidth = (swidth * scale) - sstart if r.Type == "disorder" { // draw disordered regions with a understated diagonal hatch pattern drawRectWH(img, s.Padding+sstart, startY+(s.DomainHeight-s.BackboneHeight)/2, swidth, s.BackboneHeight, disFill) } else { drawRectWHShadow(img, s.Padding+sstart, startY+(s.DomainHeight-s.MotifHeight)/2, swidth, s.MotifHeight, colorFromHex(BlendColorStrings(r.Color, "#FFFFFF")), 2*s.dpi/72.0) } } } fontContext.SetSrc(&image.Uniform{color.White}) fontContext.SetFontSize(12.0) // get font height in px assuming ~2pt descender fontH := float64(freetype.Pixel(fontContext.PointToFix32(10.0))) // draw the curated domains for ri, r := range s.g.Regions { sstart, _ := r.Start.Float64() swidth, _ := r.End.Float64() sstart *= scale swidth = (swidth * scale) - sstart drawRectWHShadow(img, s.Padding+sstart, startY, swidth, s.DomainHeight, colorFromHex(r.Color), 2*s.dpi/72.0) if swidth > 10 && s.domainLabels[ri] != "" { // center text at x w, _, _ := fontContext.MeasureString(s.domainLabels[ri]) fontContext.DrawString(s.domainLabels[ri], freetype.Pt( int(s.Padding+sstart+((swidth-float64(freetype.Pixel(w)))/2.0)), int(startY+s.DomainHeight/2+fontH/2), )) } } if !s.HideAxis { startY += s.DomainHeight + s.AxisPadding thickhline(img, int(s.Padding), int(s.GraphicWidth-s.Padding+s.dpi/36.0), int(startY), s.dpi/72.0, color.Gray{0xAA}) thickvline(img, int(s.Padding), int(startY), int(startY+(s.AxisHeight/3)), s.dpi/72.0, color.Gray{0xAA}) // set black 10px font fontContext.SetFontSize(10.0) fontContext.SetSrc(&image.Uniform{color.Black}) lastDrawn := 0 for i, t := range s.ticks { if lastDrawn > 0 && (t.Pos-lastDrawn) < aaSpace { continue } j := s.ticks.NextBetter(i, aaSpace) if i != j { continue } lastDrawn = t.Pos x := s.Padding + (float64(t.Pos) * scale) thickvline(img, int(x), int(startY), int(startY+(s.AxisHeight/3)), s.dpi/72.0, color.Gray{0xAA}) // center text at x spos := fmt.Sprint(t.Pos) w, _, _ := fontContext.MeasureString(spos) fontContext.DrawString(spos, freetype.Pt(int(x-float64(freetype.Pixel(w))/2.0), int(startY+s.AxisHeight))) } } png.Encode(w, img) }