File Coverage

src/pdfmake_render_path.c
Criterion Covered Total %
statement 0 142 0.0
branch 0 72 0.0
condition n/a
subroutine n/a
pod n/a
total 0 214 0.0


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_render_path.c - Path construction and management
3             *
4             * Implements path creation, manipulation and destruction for 2D graphics.
5             */
6              
7             #include "pdfmake_render.h"
8             #include
9             #include
10              
11             #define INITIAL_SEG_CAPACITY 16
12              
13             /*
14             * Create a new empty path
15             */
16 0           pdfmake_path_t *pdfmake_path_create(void) {
17 0           pdfmake_path_t *path = calloc(1, sizeof(pdfmake_path_t));
18 0 0         if (!path) {
19 0           return NULL;
20             }
21            
22 0           path->segs = malloc(INITIAL_SEG_CAPACITY * sizeof(pdfmake_path_seg_t));
23 0 0         if (!path->segs) {
24 0           free(path);
25 0           return NULL;
26             }
27            
28 0           path->seg_count = 0;
29 0           path->seg_cap = INITIAL_SEG_CAPACITY;
30 0           path->current.x = 0;
31 0           path->current.y = 0;
32 0           path->has_current = 0;
33 0           path->subpath_start.x = 0;
34 0           path->subpath_start.y = 0;
35 0           path->has_subpath = 0;
36            
37 0           return path;
38             }
39              
40             /*
41             * Destroy a path and free all resources
42             */
43 0           void pdfmake_path_destroy(pdfmake_path_t *path) {
44 0 0         if (!path) {
45 0           return;
46             }
47            
48 0           free(path->segs);
49 0           free(path);
50             }
51              
52             /*
53             * Clear all segments from a path, keeping capacity
54             */
55 0           void pdfmake_path_clear(pdfmake_path_t *path) {
56 0 0         if (!path) {
57 0           return;
58             }
59            
60 0           path->seg_count = 0;
61 0           path->current.x = 0;
62 0           path->current.y = 0;
63 0           path->has_current = 0;
64 0           path->subpath_start.x = 0;
65 0           path->subpath_start.y = 0;
66 0           path->has_subpath = 0;
67             }
68              
69             /*
70             * Ensure capacity for at least one more segment
71             */
72 0           static int path_ensure_capacity(pdfmake_path_t *path) {
73 0 0         if (path->seg_count >= path->seg_cap) {
74 0           size_t new_cap = path->seg_cap * 2;
75 0           pdfmake_path_seg_t *new_segs = realloc(path->segs,
76             new_cap * sizeof(pdfmake_path_seg_t));
77 0 0         if (!new_segs) {
78 0           return -1;
79             }
80 0           path->segs = new_segs;
81 0           path->seg_cap = new_cap;
82             }
83 0           return 0;
84             }
85              
86             /*
87             * Move to a new point (start new subpath)
88             */
89 0           pdfmake_render_err_t pdfmake_path_move_to(pdfmake_path_t *path, double x, double y) {
90             pdfmake_path_seg_t *seg;
91 0 0         if (!path) {
92 0           return PDFMAKE_RENDER_ERR_INVALID;
93             }
94            
95 0 0         if (path_ensure_capacity(path) < 0) {
96 0           return PDFMAKE_RENDER_ERR_MEMORY;
97             }
98            
99 0           seg = &path->segs[path->seg_count++];
100 0           seg->op = PDFMAKE_PATH_MOVE;
101 0           seg->pts[0].x = x;
102 0           seg->pts[0].y = y;
103            
104 0           path->current.x = x;
105 0           path->current.y = y;
106 0           path->has_current = 1;
107 0           path->subpath_start.x = x;
108 0           path->subpath_start.y = y;
109 0           path->has_subpath = 1;
110            
111 0           return PDFMAKE_RENDER_OK;
112             }
113              
114             /*
115             * Draw a line to a point
116             */
117 0           pdfmake_render_err_t pdfmake_path_line_to(pdfmake_path_t *path, double x, double y) {
118             pdfmake_path_seg_t *seg;
119 0 0         if (!path) {
120 0           return PDFMAKE_RENDER_ERR_INVALID;
121             }
122            
123             /* If no current point, treat as move_to */
124 0 0         if (!path->has_current) {
125 0           return pdfmake_path_move_to(path, x, y);
126             }
127            
128 0 0         if (path_ensure_capacity(path) < 0) {
129 0           return PDFMAKE_RENDER_ERR_MEMORY;
130             }
131            
132 0           seg = &path->segs[path->seg_count++];
133 0           seg->op = PDFMAKE_PATH_LINE;
134 0           seg->pts[0].x = x;
135 0           seg->pts[0].y = y;
136            
137 0           path->current.x = x;
138 0           path->current.y = y;
139            
140 0           return PDFMAKE_RENDER_OK;
141             }
142              
143             /*
144             * Draw a cubic Bezier curve
145             */
146 0           pdfmake_render_err_t pdfmake_path_curve_to(pdfmake_path_t *path,
147             double x1, double y1, double x2, double y2, double x3, double y3)
148             {
149             pdfmake_path_seg_t *seg;
150 0 0         if (!path) {
151 0           return PDFMAKE_RENDER_ERR_INVALID;
152             }
153            
154             /* If no current point, move to first control point */
155 0 0         if (!path->has_current) {
156 0           pdfmake_render_err_t err = pdfmake_path_move_to(path, x1, y1);
157 0 0         if (err != PDFMAKE_RENDER_OK) {
158 0           return err;
159             }
160             }
161            
162 0 0         if (path_ensure_capacity(path) < 0) {
163 0           return PDFMAKE_RENDER_ERR_MEMORY;
164             }
165            
166 0           seg = &path->segs[path->seg_count++];
167 0           seg->op = PDFMAKE_PATH_CURVE;
168 0           seg->pts[0].x = x1;
169 0           seg->pts[0].y = y1;
170 0           seg->pts[1].x = x2;
171 0           seg->pts[1].y = y2;
172 0           seg->pts[2].x = x3;
173 0           seg->pts[2].y = y3;
174            
175 0           path->current.x = x3;
176 0           path->current.y = y3;
177            
178 0           return PDFMAKE_RENDER_OK;
179             }
180              
181             /*
182             * Close current subpath
183             */
184 0           pdfmake_render_err_t pdfmake_path_close(pdfmake_path_t *path) {
185             pdfmake_path_seg_t *seg;
186 0 0         if (!path) {
187 0           return PDFMAKE_RENDER_ERR_INVALID;
188             }
189            
190 0 0         if (!path->has_subpath) {
191 0           return PDFMAKE_RENDER_OK; /* No subpath to close */
192             }
193            
194 0 0         if (path_ensure_capacity(path) < 0) {
195 0           return PDFMAKE_RENDER_ERR_MEMORY;
196             }
197            
198 0           seg = &path->segs[path->seg_count++];
199 0           seg->op = PDFMAKE_PATH_CLOSE;
200            
201 0           path->current = path->subpath_start;
202            
203 0           return PDFMAKE_RENDER_OK;
204             }
205              
206             /*
207             * Add a rectangle to the path
208             */
209 0           pdfmake_render_err_t pdfmake_path_rect(pdfmake_path_t *path,
210             double x, double y, double w, double h)
211             {
212             pdfmake_render_err_t err;
213            
214 0           err = pdfmake_path_move_to(path, x, y);
215 0 0         if (err != PDFMAKE_RENDER_OK) return err;
216            
217 0           err = pdfmake_path_line_to(path, x + w, y);
218 0 0         if (err != PDFMAKE_RENDER_OK) return err;
219            
220 0           err = pdfmake_path_line_to(path, x + w, y + h);
221 0 0         if (err != PDFMAKE_RENDER_OK) return err;
222            
223 0           err = pdfmake_path_line_to(path, x, y + h);
224 0 0         if (err != PDFMAKE_RENDER_OK) return err;
225            
226 0           return pdfmake_path_close(path);
227             }
228              
229             /*
230             * Check if path is empty
231             */
232 0           int pdfmake_path_is_empty(pdfmake_path_t *path) {
233 0 0         return !path || path->seg_count == 0;
    0          
234             }
235              
236             /*
237             * Get path bounding box
238             */
239 0           pdfmake_render_err_t pdfmake_path_get_bounds(pdfmake_path_t *path,
240             double *min_x, double *min_y, double *max_x, double *max_y)
241             {
242             double x_min, y_min;
243             double x_max, y_max;
244             size_t i;
245             int j;
246              
247 0 0         if (!path || path->seg_count == 0) {
    0          
248 0           return PDFMAKE_RENDER_ERR_INVALID;
249             }
250            
251 0           x_min = 1e308; y_min = 1e308;
252 0           x_max = -1e308; y_max = -1e308;
253            
254 0 0         for (i = 0; i < path->seg_count; i++) {
255 0           pdfmake_path_seg_t *seg = &path->segs[i];
256 0           int n_pts = 0;
257            
258 0           switch (seg->op) {
259 0           case PDFMAKE_PATH_MOVE:
260             case PDFMAKE_PATH_LINE:
261 0           n_pts = 1;
262 0           break;
263 0           case PDFMAKE_PATH_CURVE:
264 0           n_pts = 3;
265 0           break;
266 0           case PDFMAKE_PATH_CLOSE:
267 0           n_pts = 0;
268 0           break;
269             }
270            
271 0 0         for (j = 0; j < n_pts; j++) {
272 0 0         if (seg->pts[j].x < x_min) x_min = seg->pts[j].x;
273 0 0         if (seg->pts[j].y < y_min) y_min = seg->pts[j].y;
274 0 0         if (seg->pts[j].x > x_max) x_max = seg->pts[j].x;
275 0 0         if (seg->pts[j].y > y_max) y_max = seg->pts[j].y;
276             }
277             }
278            
279 0 0         if (min_x) *min_x = x_min;
280 0 0         if (min_y) *min_y = y_min;
281 0 0         if (max_x) *max_x = x_max;
282 0 0         if (max_y) *max_y = y_max;
283            
284 0           return PDFMAKE_RENDER_OK;
285             }