codeberg.org/go-pdf/fpdf@v0.11.1/spotcolor.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  // Copyright (c) Kurt Jung (Gmail: kurt.w.jung)
     6  //
     7  // Permission to use, copy, modify, and distribute this software for any
     8  // purpose with or without fee is hereby granted, provided that the above
     9  // copyright notice and this permission notice appear in all copies.
    10  //
    11  // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    12  // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    13  // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
    14  // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    15  // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
    16  // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
    17  // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    18  
    19  // Adapted from http://www.fpdf.org/en/script/script89.php by Olivier PLATHEY
    20  
    21  package fpdf
    22  
    23  import (
    24  	"fmt"
    25  	"strings"
    26  )
    27  
    28  func byteBound(v byte) byte {
    29  	if v > 100 {
    30  		return 100
    31  	}
    32  	return v
    33  }
    34  
    35  // AddSpotColor adds an ink-based CMYK color to the gofpdf instance and
    36  // associates it with the specified name. The individual components specify
    37  // percentages ranging from 0 to 100. Values above this are quietly capped to
    38  // 100. An error occurs if the specified name is already associated with a
    39  // color.
    40  func (f *Fpdf) AddSpotColor(nameStr string, c, m, y, k byte) {
    41  	if f.err == nil {
    42  		_, ok := f.spotColorMap[nameStr]
    43  		if !ok {
    44  			id := len(f.spotColorMap) + 1
    45  			f.spotColorMap[nameStr] = spotColorType{
    46  				id: id,
    47  				val: cmykColorType{
    48  					c: byteBound(c),
    49  					m: byteBound(m),
    50  					y: byteBound(y),
    51  					k: byteBound(k),
    52  				},
    53  			}
    54  		} else {
    55  			f.err = fmt.Errorf("name \"%s\" is already associated with a spot color", nameStr)
    56  		}
    57  	}
    58  }
    59  
    60  func (f *Fpdf) getSpotColor(nameStr string) (clr spotColorType, ok bool) {
    61  	if f.err == nil {
    62  		clr, ok = f.spotColorMap[nameStr]
    63  		if !ok {
    64  			f.err = fmt.Errorf("spot color name \"%s\" is not registered", nameStr)
    65  		}
    66  	}
    67  	return
    68  }
    69  
    70  // SetDrawSpotColor sets the current draw color to the spot color associated
    71  // with nameStr. An error occurs if the name is not associated with a color.
    72  // The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
    73  // is quietly bounded to this range.
    74  func (f *Fpdf) SetDrawSpotColor(nameStr string, tint byte) {
    75  	var clr spotColorType
    76  	var ok bool
    77  
    78  	clr, ok = f.getSpotColor(nameStr)
    79  	if ok {
    80  		f.color.draw.mode = colorModeSpot
    81  		f.color.draw.spotStr = nameStr
    82  		f.color.draw.str = sprintf("/CS%d CS %.3f SCN", clr.id, float64(byteBound(tint))/100)
    83  		if f.page > 0 {
    84  			f.out(f.color.draw.str)
    85  		}
    86  	}
    87  }
    88  
    89  // SetFillSpotColor sets the current fill color to the spot color associated
    90  // with nameStr. An error occurs if the name is not associated with a color.
    91  // The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
    92  // is quietly bounded to this range.
    93  func (f *Fpdf) SetFillSpotColor(nameStr string, tint byte) {
    94  	var clr spotColorType
    95  	var ok bool
    96  
    97  	clr, ok = f.getSpotColor(nameStr)
    98  	if ok {
    99  		f.color.fill.mode = colorModeSpot
   100  		f.color.fill.spotStr = nameStr
   101  		f.color.fill.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100)
   102  		f.colorFlag = f.color.fill.str != f.color.text.str
   103  		if f.page > 0 {
   104  			f.out(f.color.fill.str)
   105  		}
   106  	}
   107  }
   108  
   109  // SetTextSpotColor sets the current text color to the spot color associated
   110  // with nameStr. An error occurs if the name is not associated with a color.
   111  // The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
   112  // is quietly bounded to this range.
   113  func (f *Fpdf) SetTextSpotColor(nameStr string, tint byte) {
   114  	var clr spotColorType
   115  	var ok bool
   116  
   117  	clr, ok = f.getSpotColor(nameStr)
   118  	if ok {
   119  		f.color.text.mode = colorModeSpot
   120  		f.color.text.spotStr = nameStr
   121  		f.color.text.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100)
   122  		f.colorFlag = f.color.fill.str != f.color.text.str
   123  	}
   124  }
   125  
   126  func (f *Fpdf) returnSpotColor(clr colorType) (name string, c, m, y, k byte) {
   127  	var spotClr spotColorType
   128  	var ok bool
   129  
   130  	name = clr.spotStr
   131  	if name != "" {
   132  		spotClr, ok = f.getSpotColor(name)
   133  		if ok {
   134  			c = spotClr.val.c
   135  			m = spotClr.val.m
   136  			y = spotClr.val.y
   137  			k = spotClr.val.k
   138  		}
   139  	}
   140  	return
   141  }
   142  
   143  // GetDrawSpotColor returns the most recently used spot color information for
   144  // drawing. This will not be the current drawing color if some other color type
   145  // such as RGB is active. If no spot color has been set for drawing, zero
   146  // values are returned.
   147  func (f *Fpdf) GetDrawSpotColor() (name string, c, m, y, k byte) {
   148  	return f.returnSpotColor(f.color.draw)
   149  }
   150  
   151  // GetTextSpotColor returns the most recently used spot color information for
   152  // text output. This will not be the current text color if some other color
   153  // type such as RGB is active. If no spot color has been set for text, zero
   154  // values are returned.
   155  func (f *Fpdf) GetTextSpotColor() (name string, c, m, y, k byte) {
   156  	return f.returnSpotColor(f.color.text)
   157  }
   158  
   159  // GetFillSpotColor returns the most recently used spot color information for
   160  // fill output. This will not be the current fill color if some other color
   161  // type such as RGB is active. If no fill spot color has been set, zero values
   162  // are returned.
   163  func (f *Fpdf) GetFillSpotColor() (name string, c, m, y, k byte) {
   164  	return f.returnSpotColor(f.color.fill)
   165  }
   166  
   167  func (f *Fpdf) putSpotColors() {
   168  	for k, v := range f.spotColorMap {
   169  		f.newobj()
   170  		f.outf("[/Separation /%s", strings.Replace(k, " ", "#20", -1))
   171  		f.out("/DeviceCMYK <<")
   172  		f.out("/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ")
   173  		f.outf("/C1 [%.3f %.3f %.3f %.3f] ", float64(v.val.c)/100, float64(v.val.m)/100,
   174  			float64(v.val.y)/100, float64(v.val.k)/100)
   175  		f.out("/FunctionType 2 /Domain [0 1] /N 1>>]")
   176  		f.out("endobj")
   177  		v.objID = f.n
   178  		f.spotColorMap[k] = v
   179  	}
   180  }
   181  
   182  func (f *Fpdf) spotColorPutResourceDict() {
   183  	f.out("/ColorSpace <<")
   184  	for _, clr := range f.spotColorMap {
   185  		f.outf("/CS%d %d 0 R", clr.id, clr.objID)
   186  	}
   187  	f.out(">>")
   188  }