codeberg.org/go-pdf/fpdf@v0.11.1/svgwrite.go (about)

     1  // Copyright ©2023 The go-pdf Authors. All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6   * Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung)
     7   *
     8   * Permission to use, copy, modify, and distribute this software for any
     9   * purpose with or without fee is hereby granted, provided that the above
    10   * copyright notice and this permission notice appear in all copies.
    11   *
    12   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    13   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    14   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    15   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    16   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    17   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    18   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    19   */
    20  
    21  package fpdf
    22  
    23  // SVGBasicWrite renders the paths encoded in the basic SVG image specified by
    24  // sb. The scale value is used to convert the coordinates in the path to the
    25  // unit of measure specified in New(). If scale is 0, SVGBasicWrite automatically adapts the SVG document
    26  // to the PDF document unit. The current position (as set with a call
    27  // to SetXY()) is used as the origin of the image. The current line cap style
    28  // (as set with SetLineCapStyle()), line width (as set with SetLineWidth()),
    29  // and draw color (as set with SetDrawColor()) are used in drawing the image
    30  // paths.
    31  func (f *Fpdf) SVGBasicWrite(sb *SVGBasicType, scale float64) {
    32  	originX, originY := f.GetXY()
    33  	var x, y, newX, newY float64
    34  	var cx0, cy0, cx1, cy1 float64
    35  	var path []SVGBasicSegmentType
    36  	var seg SVGBasicSegmentType
    37  	var startX, startY float64
    38  	if scale == 0.0 {
    39  		scale = 1.0 / f.k
    40  	}
    41  	sval := func(origin float64, arg int) float64 {
    42  		return origin + scale*seg.Arg[arg]
    43  	}
    44  	xval := func(arg int) float64 {
    45  		return sval(originX, arg)
    46  	}
    47  	yval := func(arg int) float64 {
    48  		return sval(originY, arg)
    49  	}
    50  	val := func(arg int) (float64, float64) {
    51  		return xval(arg), yval(arg + 1)
    52  	}
    53  	for j := 0; j < len(sb.Segments) && f.Ok(); j++ {
    54  		path = sb.Segments[j]
    55  		for k := 0; k < len(path) && f.Ok(); k++ {
    56  			seg = path[k]
    57  			switch seg.Cmd {
    58  			case 'M':
    59  				x, y = val(0)
    60  				startX, startY = x, y
    61  				f.SetXY(x, y)
    62  			case 'L':
    63  				newX, newY = val(0)
    64  				f.Line(x, y, newX, newY)
    65  				x, y = newX, newY
    66  			case 'C':
    67  				cx0, cy0 = val(0)
    68  				cx1, cy1 = val(2)
    69  				newX, newY = val(4)
    70  				f.CurveCubic(x, y, cx0, cy0, newX, newY, cx1, cy1, "D")
    71  				x, y = newX, newY
    72  			case 'Q':
    73  				cx0, cy0 = val(0)
    74  				newX, newY = val(2)
    75  				f.Curve(x, y, cx0, cy0, newX, newY, "D")
    76  				x, y = newX, newY
    77  			case 'H':
    78  				newX = xval(0)
    79  				f.Line(x, y, newX, y)
    80  				x = newX
    81  			case 'V':
    82  				newY = yval(0)
    83  				f.Line(x, y, x, newY)
    84  				y = newY
    85  			case 'Z':
    86  				f.Line(x, y, startX, startY)
    87  				x, y = startX, startY
    88  			default:
    89  				f.SetErrorf("Unexpected path command '%c'", seg.Cmd)
    90  			}
    91  		}
    92  	}
    93  }
    94  
    95  // SVGBasicDraw renders the paths in the provided SVGBasicType, but each SVG shape is written
    96  // as a path that can be filled.
    97  //
    98  // styleStr can be "F" for filled, "D" for outlined only, or "DF" or
    99  // "FD" for outlined and filled. An empty string will be replaced with
   100  // "D". Drawing uses the current draw color and line width centered on
   101  // the ellipse's perimeter. Filling uses the current fill color.
   102  func (f *Fpdf) SVGBasicDraw(sb *SVGBasicType, scale float64, styleStr string) {
   103  	originX, originY := f.GetXY()
   104  	var newX, newY float64
   105  	var cx0, cy0, cx1, cy1 float64
   106  	var path []SVGBasicSegmentType
   107  	var seg SVGBasicSegmentType
   108  	var startX, startY float64
   109  	if scale == 0.0 {
   110  		scale = 1.0 / f.k
   111  	}
   112  	sval := func(origin float64, arg int) float64 {
   113  		return origin + scale*seg.Arg[arg]
   114  	}
   115  	xval := func(arg int) float64 {
   116  		return sval(originX, arg)
   117  	}
   118  	yval := func(arg int) float64 {
   119  		return sval(originY, arg)
   120  	}
   121  	val := func(arg int) (float64, float64) {
   122  		return xval(arg), yval(arg + 1)
   123  	}
   124  	for j := 0; j < len(sb.Segments) && f.Ok(); j++ {
   125  		path = sb.Segments[j]
   126  		for k := 0; k < len(path) && f.Ok(); k++ {
   127  			seg = path[k]
   128  			switch seg.Cmd {
   129  			case 'M':
   130  				startX, startY = val(0)
   131  				f.MoveTo(startX, startY)
   132  			case 'L':
   133  				newX, newY = val(0)
   134  				f.LineTo(newX, newY)
   135  			case 'C':
   136  				cx0, cy0 = val(0)
   137  				cx1, cy1 = val(2)
   138  				newX, newY = val(4)
   139  				f.CurveBezierCubicTo(cx0, cy0, cx1, cy1, newX, newY)
   140  			case 'Q':
   141  				cx0, cy0 = val(0)
   142  				newX, newY = val(2)
   143  				f.CurveTo(cx0, cy0, newX, newY)
   144  			case 'H':
   145  				newX = xval(0)
   146  				f.LineTo(newX, f.GetY())
   147  			case 'V':
   148  				newY = yval(0)
   149  				f.LineTo(f.GetX(), newY)
   150  			case 'Z':
   151  				f.ClosePath()
   152  				f.DrawPath(styleStr)
   153  			default:
   154  				f.SetErrorf("Unexpected path command '%c'", seg.Cmd)
   155  			}
   156  		}
   157  	}
   158  }