1package gofpdf
2
3import (
4	"fmt"
5	"math"
6)
7
8// Routines in this file are translated from the work of Moritz Wagner and
9// Andreas Würmser.
10
11// TransformMatrix is used for generalized transformations of text, drawings
12// and images.
13type TransformMatrix struct {
14	A, B, C, D, E, F float64
15}
16
17// TransformBegin sets up a transformation context for subsequent text,
18// drawings and images. The typical usage is to immediately follow a call to
19// this method with a call to one or more of the transformation methods such as
20// TransformScale(), TransformSkew(), etc. This is followed by text, drawing or
21// image output and finally a call to TransformEnd(). All transformation
22// contexts must be properly ended prior to outputting the document.
23func (f *Fpdf) TransformBegin() {
24	f.transformNest++
25	f.out("q")
26}
27
28// TransformScaleX scales the width of the following text, drawings and images.
29// scaleWd is the percentage scaling factor. (x, y) is center of scaling.
30//
31// The TransformBegin() example demonstrates this method.
32func (f *Fpdf) TransformScaleX(scaleWd, x, y float64) {
33	f.TransformScale(scaleWd, 100, x, y)
34}
35
36// TransformScaleY scales the height of the following text, drawings and
37// images. scaleHt is the percentage scaling factor. (x, y) is center of
38// scaling.
39//
40// The TransformBegin() example demonstrates this method.
41func (f *Fpdf) TransformScaleY(scaleHt, x, y float64) {
42	f.TransformScale(100, scaleHt, x, y)
43}
44
45// TransformScaleXY uniformly scales the width and height of the following
46// text, drawings and images. s is the percentage scaling factor for both width
47// and height. (x, y) is center of scaling.
48//
49// The TransformBegin() example demonstrates this method.
50func (f *Fpdf) TransformScaleXY(s, x, y float64) {
51	f.TransformScale(s, s, x, y)
52}
53
54// TransformScale generally scales the following text, drawings and images.
55// scaleWd and scaleHt are the percentage scaling factors for width and height.
56// (x, y) is center of scaling.
57//
58// The TransformBegin() example demonstrates this method.
59func (f *Fpdf) TransformScale(scaleWd, scaleHt, x, y float64) {
60	if scaleWd == 0 || scaleHt == 0 {
61		f.err = fmt.Errorf("scale factor cannot be zero")
62		return
63	}
64	y = (f.h - y) * f.k
65	x *= f.k
66	scaleWd /= 100
67	scaleHt /= 100
68	f.Transform(TransformMatrix{scaleWd, 0, 0,
69		scaleHt, x * (1 - scaleWd), y * (1 - scaleHt)})
70}
71
72// TransformMirrorHorizontal horizontally mirrors the following text, drawings
73// and images. x is the axis of reflection.
74//
75// The TransformBegin() example demonstrates this method.
76func (f *Fpdf) TransformMirrorHorizontal(x float64) {
77	f.TransformScale(-100, 100, x, f.y)
78}
79
80// TransformMirrorVertical vertically mirrors the following text, drawings and
81// images. y is the axis of reflection.
82//
83// The TransformBegin() example demonstrates this method.
84func (f *Fpdf) TransformMirrorVertical(y float64) {
85	f.TransformScale(100, -100, f.x, y)
86}
87
88// TransformMirrorPoint symmetrically mirrors the following text, drawings and
89// images on the point specified by (x, y).
90//
91// The TransformBegin() example demonstrates this method.
92func (f *Fpdf) TransformMirrorPoint(x, y float64) {
93	f.TransformScale(-100, -100, x, y)
94}
95
96// TransformMirrorLine symmetrically mirrors the following text, drawings and
97// images on the line defined by angle and the point (x, y). angles is
98// specified in degrees and measured counter-clockwise from the 3 o'clock
99// position.
100//
101// The TransformBegin() example demonstrates this method.
102func (f *Fpdf) TransformMirrorLine(angle, x, y float64) {
103	f.TransformScale(-100, 100, x, y)
104	f.TransformRotate(-2*(angle-90), x, y)
105}
106
107// TransformTranslateX moves the following text, drawings and images
108// horizontally by the amount specified by tx.
109//
110// The TransformBegin() example demonstrates this method.
111func (f *Fpdf) TransformTranslateX(tx float64) {
112	f.TransformTranslate(tx, 0)
113}
114
115// TransformTranslateY moves the following text, drawings and images vertically
116// by the amount specified by ty.
117//
118// The TransformBegin() example demonstrates this method.
119func (f *Fpdf) TransformTranslateY(ty float64) {
120	f.TransformTranslate(0, ty)
121}
122
123// TransformTranslate moves the following text, drawings and images
124// horizontally and vertically by the amounts specified by tx and ty.
125//
126// The TransformBegin() example demonstrates this method.
127func (f *Fpdf) TransformTranslate(tx, ty float64) {
128	f.Transform(TransformMatrix{1, 0, 0, 1, tx * f.k, -ty * f.k})
129}
130
131// TransformRotate rotates the following text, drawings and images around the
132// center point (x, y). angle is specified in degrees and measured
133// counter-clockwise from the 3 o'clock position.
134//
135// The TransformBegin() example demonstrates this method.
136func (f *Fpdf) TransformRotate(angle, x, y float64) {
137	y = (f.h - y) * f.k
138	x *= f.k
139	angle = angle * math.Pi / 180
140	var tm TransformMatrix
141	tm.A = math.Cos(angle)
142	tm.B = math.Sin(angle)
143	tm.C = -tm.B
144	tm.D = tm.A
145	tm.E = x + tm.B*y - tm.A*x
146	tm.F = y - tm.A*y - tm.B*x
147	f.Transform(tm)
148}
149
150// TransformSkewX horizontally skews the following text, drawings and images
151// keeping the point (x, y) stationary. angleX ranges from -90 degrees (skew to
152// the left) to 90 degrees (skew to the right).
153//
154// The TransformBegin() example demonstrates this method.
155func (f *Fpdf) TransformSkewX(angleX, x, y float64) {
156	f.TransformSkew(angleX, 0, x, y)
157}
158
159// TransformSkewY vertically skews the following text, drawings and images
160// keeping the point (x, y) stationary. angleY ranges from -90 degrees (skew to
161// the bottom) to 90 degrees (skew to the top).
162//
163// The TransformBegin() example demonstrates this method.
164func (f *Fpdf) TransformSkewY(angleY, x, y float64) {
165	f.TransformSkew(0, angleY, x, y)
166}
167
168// TransformSkew generally skews the following text, drawings and images
169// keeping the point (x, y) stationary. angleX ranges from -90 degrees (skew to
170// the left) to 90 degrees (skew to the right). angleY ranges from -90 degrees
171// (skew to the bottom) to 90 degrees (skew to the top).
172//
173// The TransformBegin() example demonstrates this method.
174func (f *Fpdf) TransformSkew(angleX, angleY, x, y float64) {
175	if angleX <= -90 || angleX >= 90 || angleY <= -90 || angleY >= 90 {
176		f.err = fmt.Errorf("skew values must be between -90° and 90°")
177		return
178	}
179	x *= f.k
180	y = (f.h - y) * f.k
181	var tm TransformMatrix
182	tm.A = 1
183	tm.B = math.Tan(angleY * math.Pi / 180)
184	tm.C = math.Tan(angleX * math.Pi / 180)
185	tm.D = 1
186	tm.E = -tm.C * y
187	tm.F = -tm.B * x
188	f.Transform(tm)
189}
190
191// Transform generally transforms the following text, drawings and images
192// according to the specified matrix. It is typically easier to use the various
193// methods such as TransformRotate() and TransformMirrorVertical() instead.
194func (f *Fpdf) Transform(tm TransformMatrix) {
195	if f.transformNest > 0 {
196		f.outf("%.5f %.5f %.5f %.5f %.5f %.5f cm",
197			tm.A, tm.B, tm.C, tm.D, tm.E, tm.F)
198	} else if f.err == nil {
199		f.err = fmt.Errorf("transformation context is not active")
200	}
201}
202
203// TransformEnd applies a transformation that was begun with a call to TransformBegin().
204//
205// The TransformBegin() example demonstrates this method.
206func (f *Fpdf) TransformEnd() {
207	if f.transformNest > 0 {
208		f.transformNest--
209		f.out("Q")
210	} else {
211		f.err = fmt.Errorf("error attempting to end transformation operation out of sequence")
212	}
213}
214