github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/creator/filled_curve.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 creator
     7  
     8  import (
     9  	pdfcontent "github.com/unidoc/unidoc/pdf/contentstream"
    10  	"github.com/unidoc/unidoc/pdf/contentstream/draw"
    11  	pdfcore "github.com/unidoc/unidoc/pdf/core"
    12  	pdf "github.com/unidoc/unidoc/pdf/model"
    13  )
    14  
    15  // FilledCurve represents a closed path of Bezier curves with a border and fill.
    16  type FilledCurve struct {
    17  	curves        []draw.CubicBezierCurve
    18  	FillEnabled   bool // Show fill?
    19  	fillColor     *pdf.PdfColorDeviceRGB
    20  	BorderEnabled bool // Show border?
    21  	BorderWidth   float64
    22  	borderColor   *pdf.PdfColorDeviceRGB
    23  }
    24  
    25  // NewFilledCurve returns a instance of filled curve.
    26  func NewFilledCurve() *FilledCurve {
    27  	curve := FilledCurve{}
    28  	curve.curves = []draw.CubicBezierCurve{}
    29  	return &curve
    30  }
    31  
    32  // AppendCurve appends a Bezier curve to the filled curve.
    33  func (fc *FilledCurve) AppendCurve(curve draw.CubicBezierCurve) *FilledCurve {
    34  	fc.curves = append(fc.curves, curve)
    35  	return fc
    36  }
    37  
    38  // SetFillColor sets the fill color for the path.
    39  func (fc *FilledCurve) SetFillColor(color Color) {
    40  	fc.fillColor = pdf.NewPdfColorDeviceRGB(color.ToRGB())
    41  }
    42  
    43  // SetBorderColor sets the border color for the path.
    44  func (fc *FilledCurve) SetBorderColor(color Color) {
    45  	fc.borderColor = pdf.NewPdfColorDeviceRGB(color.ToRGB())
    46  }
    47  
    48  // draw draws the filled curve. Can specify a graphics state (gsName) for setting opacity etc. Otherwise leave empty ("").
    49  // Returns the content stream as a byte array, the bounding box and an error on failure.
    50  func (fc *FilledCurve) draw(gsName string) ([]byte, *pdf.PdfRectangle, error) {
    51  	bpath := draw.NewCubicBezierPath()
    52  	for _, c := range fc.curves {
    53  		bpath = bpath.AppendCurve(c)
    54  	}
    55  
    56  	creator := pdfcontent.NewContentCreator()
    57  	creator.Add_q()
    58  
    59  	if fc.FillEnabled {
    60  		creator.Add_rg(fc.fillColor.R(), fc.fillColor.G(), fc.fillColor.B())
    61  	}
    62  	if fc.BorderEnabled {
    63  		creator.Add_RG(fc.borderColor.R(), fc.borderColor.G(), fc.borderColor.B())
    64  		creator.Add_w(fc.BorderWidth)
    65  	}
    66  	if len(gsName) > 1 {
    67  		// If a graphics state is provided, use it. (can support transparency).
    68  		creator.Add_gs(pdfcore.PdfObjectName(gsName))
    69  	}
    70  
    71  	draw.DrawBezierPathWithCreator(bpath, creator)
    72  	creator.Add_h() // Close the path.
    73  
    74  	if fc.FillEnabled && fc.BorderEnabled {
    75  		creator.Add_B() // fill and stroke.
    76  	} else if fc.FillEnabled {
    77  		creator.Add_f() // Fill.
    78  	} else if fc.BorderEnabled {
    79  		creator.Add_S() // Stroke.
    80  	}
    81  	creator.Add_Q()
    82  
    83  	// Get bounding box.
    84  	pathBbox := bpath.GetBoundingBox()
    85  	if fc.BorderEnabled {
    86  		// Account for stroke width.
    87  		pathBbox.Height += fc.BorderWidth
    88  		pathBbox.Width += fc.BorderWidth
    89  		pathBbox.X -= fc.BorderWidth / 2
    90  		pathBbox.Y -= fc.BorderWidth / 2
    91  	}
    92  
    93  	// Bounding box - global coordinate system.
    94  	bbox := &pdf.PdfRectangle{}
    95  	bbox.Llx = pathBbox.X
    96  	bbox.Lly = pathBbox.Y
    97  	bbox.Urx = pathBbox.X + pathBbox.Width
    98  	bbox.Ury = pathBbox.Y + pathBbox.Height
    99  	return creator.Bytes(), bbox, nil
   100  }
   101  
   102  // GeneratePageBlocks draws the filled curve on page blocks.
   103  func (fc *FilledCurve) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
   104  	block := NewBlock(ctx.PageWidth, ctx.PageHeight)
   105  
   106  	contents, _, err := fc.draw("")
   107  	err = block.addContentsByString(string(contents))
   108  	if err != nil {
   109  		return nil, ctx, err
   110  	}
   111  	return []*Block{block}, ctx, nil
   112  }