ElementBuilder

Sample Obj-C code to use Apryse SDK's page writing API to generate new pages, embed fonts & images, and copy graphical elements from one page to another. Learn more about our iOS SDK and PDF Editing & Manipulation Library.

1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6#import <OBJC/PDFNetOBJC.h>
7#import <Foundation/Foundation.h>
8
9int main(int argc, char *argv[])
10{
11 @autoreleasepool {
12 int ret = 0;
13 [PTPDFNet Initialize: 0];
14
15 @try
16 {
17 PTPDFDoc *doc = [[PTPDFDoc alloc] init];
18
19 PTElementBuilder *eb = [[PTElementBuilder alloc] init]; // ElementBuilder is used to build new Element objects
20 PTElementWriter *writer = [[PTElementWriter alloc] init]; // ElementWriter is used to write Elements to the page
21
22 PTElement *element;
23 PTGState *gstate;
24
25 // Start a new page ------------------------------------
26 PTPage *page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 612 y2: 794]];
27
28 [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // begin writing to the page
29
30 // Create an Image that can be reused in the document or on the same page.
31 PTImage *img = [PTImage Create: [doc GetSDFDoc] filename: @"../../TestFiles/peppers.jpg"];
32
33 element = [eb CreateImageWithMatrix:img mtx: [[PTMatrix2D alloc] initWithA: [img GetImageWidth]/2 b: -145 c: 20 d: [img GetImageHeight]/2 h: 200 v: 150]];
34 [writer WritePlacedElement: element];
35
36 gstate = [element GetGState]; // use the same image (just change its matrix)
37 [gstate SetTransform: 200 b: 0 c: 0 d: 300 h: 50 v: 450];
38 [writer WritePlacedElement: element];
39
40 // use the same image again (just change its matrix).
41 [writer WritePlacedElement: [eb CreateImageWithCornerAndScale: img x: 300 y: 600 hscale: 200 vscale: -150]];
42
43 [writer End]; // save changes to the current page
44 [doc PagePushBack: page];
45
46 // Start a new page ------------------------------------
47 // Construct and draw a path object using different styles
48 page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 612 y2: 794]];
49
50 [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // begin writing to the page
51 [eb Reset: [[PTGState alloc] init]]; // Reset the GState to default
52
53 [eb PathBegin]; // start constructing the path
54 [eb MoveTo: 306 y: 396];
55 [eb CurveTo: 681 cy1: 771 cx2: 399.75 cy2: 864.75 x2: 306 y2: 771];
56 [eb CurveTo: 212.25 cy1: 864.75 cx2: -69 cy2: 771 x2: 306 y2: 396];
57 [eb ClosePath];
58 element = [eb PathEnd]; // the path is now finished
59 [element SetPathFill: YES]; // the path should be filled
60
61 // Set the path color space and color
62 gstate = [element GetGState];
63 [gstate SetFillColorSpace: [PTColorSpace CreateDeviceCMYK]];
64 [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w: 0]]; // cyan
65 [gstate SetTransform: 0.5 b: 0 c: 0 d: 0.5 h: -20 v: 300];
66 [writer WritePlacedElement: element];
67
68 // Draw the same path using a different stroke color
69 [element SetPathStroke: YES]; // this path is should be filled and stroked
70 [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 0 y: 0 z: 1 w: 0]]; // yellow
71 [gstate SetStrokeColorSpace: [PTColorSpace CreateDeviceRGB]];
72 [gstate SetStrokeColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w: 0]]; // red
73 [gstate SetTransform: 0.5 b: 0 c: 0 d: 0.5 h: 280 v: 300];
74 [gstate SetLineWidth: 20];
75 [writer WritePlacedElement: element];
76
77 // Draw the same path with with a given dash pattern
78 [element SetPathFill: NO]; // this path is should be only stroked
79 [gstate SetStrokeColorWithColorPt: [[PTColorPt alloc] initWithX: 0 y: 0 z: 1 w: 0]]; // blue
80 [gstate SetTransform: 0.5 b: 0 c: 0 d: 0.5 h: 280 v: 0];
81 NSMutableArray *dash_pattern = [[NSMutableArray alloc] init];
82 [dash_pattern addObject: @30.0];
83 [gstate SetDashPattern: dash_pattern phase: 0];
84 [writer WritePlacedElement: element];
85
86 // Use the path as a clipping path
87 [writer WriteElement: [eb CreateGroupBegin]]; // Save the graphics state
88 // Start constructing the new path (the old path was lost when we created
89 // a new Element using CreateGroupBegin()).
90 [eb PathBegin];
91 [eb MoveTo: 306 y: 396];
92 [eb CurveTo: 681 cy1: 771 cx2: 399.75 cy2: 864.75 x2: 306 y2: 771];
93 [eb CurveTo: 212.25 cy1: 864.75 cx2: -69 cy2: 771 x2: 306 y2: 396];
94 [eb ClosePath];
95 element = [eb PathEnd]; // path is now constructed
96 [element SetPathClip: YES]; // this path is a clipping path
97 [element SetPathStroke: YES]; // this path should be filled and stroked
98 gstate = [element GetGState];
99 [gstate SetTransform: 0.5 b: 0 c: 0 d: 0.5 h: -20 v: 0];
100
101 [writer WriteElement: element];
102
103 [writer WriteElement: [eb CreateImageWithCornerAndScale: img x: 100 y: 300 hscale: 400 vscale: 600]];
104
105 [writer WriteElement: [eb CreateGroupEnd]]; // Restore the graphics state
106
107 [writer End]; // save changes to the current page
108 [doc PagePushBack: page];
109
110
111 // Start a new page ------------------------------------
112 page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 612 y2: 794]];
113
114 [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // begin writing to this page
115 [eb Reset: [[PTGState alloc] init]]; // Reset the GState to default
116
117 // Begin writing a block of text
118 element = [eb CreateTextBeginWithFont: [PTFont Create: [doc GetSDFDoc] type: e_pttimes_roman embed: NO] font_sz: 12];
119 [writer WriteElement: element];
120
121 element = [eb CreateTextRun: @"Hello World!"];
122 [element SetTextMatrix: 10 b: 0 c: 0 d: 10 h: 0 v: 600];
123 [[element GetGState] SetLeading: 15]; // Set the spacing between lines
124 [writer WriteElement: element];
125
126 [writer WriteElement: [eb CreateTextNewLine]]; // New line
127
128 element = [eb CreateTextRun: @"Hello World!"];
129 gstate = [element GetGState];
130 [gstate SetTextRenderMode: e_ptstroke_text];
131 [gstate SetCharSpacing: -1.25];
132 [gstate SetWordSpacing: -1.25];
133 [writer WriteElement: element];
134
135 [writer WriteElement: [eb CreateTextNewLine]]; // New line
136
137 element = [eb CreateTextRun: @"Hello World!"];
138 gstate = [element GetGState];
139 [gstate SetCharSpacing: 0];
140 [gstate SetWordSpacing: 0];
141 [gstate SetLineWidth: 3];
142 [gstate SetTextRenderMode: e_ptfill_stroke_text];
143 [gstate SetStrokeColorSpace: [PTColorSpace CreateDeviceRGB]];
144 [gstate SetStrokeColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w: 0]]; // red
145 [gstate SetFillColorSpace: [PTColorSpace CreateDeviceCMYK]];
146 [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w: 0]];; // cyan
147 [writer WriteElement: element];
148
149
150 [writer WriteElement: [eb CreateTextNewLine]]; // New line
151
152 // Set text as a clipping path to the image.
153 element = [eb CreateTextRun: @"Hello World!"];
154 gstate = [element GetGState];
155 [gstate SetTextRenderMode: e_ptclip_text];
156 [writer WriteElement: element];
157
158 // Finish the block of text
159 [writer WriteElement: [eb CreateTextEnd]];
160
161 // Draw an image that will be clipped by the above text
162 [writer WriteElement: [eb CreateImageWithCornerAndScale: img x: 10 y: 100 hscale: 1300 vscale: 720]];
163
164 [writer End]; // save changes to the current page
165 [doc PagePushBack: page];
166
167 // Start a new page ------------------------------------
168 //
169 // The example illustrates how to embed the external font in a PDF document.
170 // The example also shows how ElementReader can be used to copy and modify
171 // Elements between pages.
172
173 PTElementReader *reader = [[PTElementReader alloc] init];
174
175 // Start reading Elements from the last page. We will copy all Elements to
176 // a new page but will modify the font associated with text.
177 [reader Begin: [doc GetPage: [doc GetPageCount]]];
178
179 page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 1300 y2: 794]];
180
181 [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // begin writing to this page
182 [eb Reset: [[PTGState alloc] init]]; // Reset the GState to default
183
184 // Embed an external font in the document.
185 PTFont *font = [PTFont CreateTrueTypeFont: [doc GetSDFDoc] font_path: @"../../TestFiles/font.ttf" embed: YES subset: YES];
186
187 while ((element = [reader Next]) != NULL) // Read page contents
188 {
189 if ([element GetType] == e_pttext)
190 {
191 [[element GetGState] SetFont: font font_sz: 12];
192 }
193
194 [writer WriteElement: element];
195 }
196
197 [reader End];
198 [writer End]; // save changes to the current page
199
200 [doc PagePushBack: page];
201
202
203 // Start a new page ------------------------------------
204 //
205 // The example illustrates how to embed the external font in a PDF document.
206 // The example also shows how ElementReader can be used to copy and modify
207 // Elements between pages.
208
209 // Start reading Elements from the last page. We will copy all Elements to
210 // a new page but will modify the font associated with text.
211 [reader Begin: [doc GetPage: [doc GetPageCount]]];
212
213 page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 1300 y2: 794]];
214
215 [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // begin writing to this page
216 [eb Reset: [[PTGState alloc] init]]; // Reset the GState to default
217
218 // Embed an external font in the document.
219 PTFont *font2 = [PTFont CreateType1Font: [doc GetSDFDoc] font_path: @"../../TestFiles/Misc-Fixed.pfa" embed: YES];
220
221 while ((element = [reader Next]) != NULL) // Read page contents
222 {
223 if ([element GetType] == e_pttext)
224 {
225 [[element GetGState] SetFont: font2 font_sz: 12];
226 }
227
228 [writer WriteElement: element];
229 }
230
231 [reader End];
232 [writer End]; // save changes to the current page
233 [doc PagePushBack: page];
234
235
236 // Start a new page ------------------------------------
237 page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 612 y2: 792]];
238
239 [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // begin writing to this page
240 [eb Reset: [[PTGState alloc] init]]; // Reset the GState to default
241
242 // Begin writing a block of text
243 element = [eb CreateTextBeginWithFont: [PTFont Create: [doc GetSDFDoc] type: e_pttimes_roman embed: NO] font_sz: 12];
244 [element SetTextMatrix: 1.5 b: 0 c: 0 d: 1.5 h: 50 v: 600];
245 [[element GetGState] SetLeading: 15]; // Set the spacing between lines
246 [writer WriteElement: element];
247
248 NSString* para = @"A PDF text object consists of operators that can show "
249 "text strings, move the text position, and set text state and certain "
250 "other parameters. In addition, there are three parameters that are "
251 "defined only within a text object and do not persist from one text "
252 "object to the next: Tm, the text matrix, Tlm, the text line matrix, "
253 "Trm, the text rendering matrix, actually just an intermediate result "
254 "that combines the effects of text state parameters, the text matrix "
255 "(Tm), and the current transformation matrix";
256
257 double para_end = para.length;
258 double text_run = 0;
259
260 double para_width = 300; // paragraph width is 300 units
261 double cur_width = 0;
262
263 while (text_run < para_end)
264 {
265 double text_run_end = [para rangeOfString: @" " options: NSLiteralSearch range: NSMakeRange(text_run, para.length-text_run)].location;
266
267 if (text_run_end == NSNotFound) text_run_end = para_end - 1;
268
269 NSString *str = [para substringWithRange: NSMakeRange(text_run, text_run_end-text_run+1)];
270 element = [eb CreateTextRun: str];
271 if (cur_width + [element GetTextLength] < para_width)
272 {
273 [writer WriteElement: element];
274 cur_width = cur_width + [element GetTextLength];
275 }
276 else
277 {
278 [writer WriteElement: [eb CreateTextNewLine]]; // New line
279 element = [eb CreateTextRun: str];
280 cur_width = [element GetTextLength];
281 [writer WriteElement: element];
282 }
283
284 text_run = text_run_end+1;
285 }
286
287 // -----------------------------------------------------------------------
288 // The following code snippet illustrates how to adjust spacing between
289 // characters (text runs).
290 element = [eb CreateTextNewLine];
291 [writer WriteElement: element]; // Skip 2 lines
292 [writer WriteElement: element];
293
294 [writer WriteElement: [eb CreateTextRun: @"An example of space adjustments between inter-characters:"]];
295 [writer WriteElement: [eb CreateTextNewLine]];
296
297 // Write string "AWAY" without space adjustments between characters.
298 element = [eb CreateTextRun: @"AWAY"];
299 [writer WriteElement: element];
300
301 [writer WriteElement: [eb CreateTextNewLine]];
302
303 // Write string "AWAY" with space adjustments between characters.
304 element = [eb CreateTextRun: @"A"];
305 [writer WriteElement: element];
306
307 element = [eb CreateTextRun: @"W"];
308 [element SetPosAdjustment: 140];
309 [writer WriteElement: element];
310
311 element = [eb CreateTextRun: @"A"];
312 [element SetPosAdjustment: 140];
313 [writer WriteElement: element];
314
315 element = [eb CreateTextRun: @"Y again"];
316 [element SetPosAdjustment: 115];
317 [writer WriteElement: element];
318
319 // Draw the same strings using direct content output...
320 [writer Flush]; // flush pending Element writing operations.
321
322 // You can also write page content directly to the content stream using
323 // ElementWriter.WriteString(...) and ElementWriter.WriteBuffer(...) methods.
324 // Note that if you are planning to use these functions you need to be familiar
325 // with PDF page content operators (see Appendix A in PDF Reference Manual).
326 // Because it is easy to make mistakes during direct output we recommend that
327 // you use ElementBuilder and Element interface instead.
328
329 [writer WriteString: @"T* T* "]; // Skip 2 lines
330 [writer WriteString: @"(Direct output to PDF page content stream:) Tj T* "];
331 [writer WriteString: @"(AWAY) Tj T* "];
332 [writer WriteString: @"[(A)140(W)140(A)115(Y again)] TJ "];
333 // Finish the block of text
334 [writer WriteElement: [eb CreateTextEnd]];
335
336 [writer End]; // save changes to the current page
337 [doc PagePushBack: page];
338
339 // Start a new page ------------------------------------
340
341 // Image Masks
342 //
343 // In the opaque imaging model, images mark all areas they occupy on the page as
344 // if with opaque paint. All portions of the image, whether black, white, gray,
345 // or color, completely obscure any marks that may previously have existed in the
346 // same place on the page.
347 // In the graphic arts industry and page layout applications, however, it is common
348 // to crop or 'mask out' the background of an image and then place the masked image
349 // on a different background, allowing the existing background to show through the
350 // masked areas. This sample illustrates how to use image masks.
351
352 page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 612 y2: 792]];
353 [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // begin writing to this page
354
355 // Create the Image Mask
356 PTMappedFile *imgf = [[PTMappedFile alloc] initWithFilename: @"../../TestFiles/imagemask.dat"];
357 PTFilterReader *mask_read = [[PTFilterReader alloc] initWithFilter: imgf];
358
359 PTColorSpace *device_gray = [PTColorSpace CreateDeviceGray];
360 PTImage *mask = [PTImage CreateWithStreamAndFormat: [doc GetSDFDoc] image_data: mask_read width: 64 height: 64 bpc: 1 color_space: device_gray input_format: e_ptascii_hex];
361
362 [[mask GetSDFObj] PutBool: @"ImageMask" value: YES];
363
364 element = [eb CreateRect: 0 y: 0 width: 612 height: 794];
365 [element SetPathStroke: NO];
366 [element SetPathFill: YES];
367 [[element GetGState] SetFillColorSpace: device_gray];
368 [[element GetGState] SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 0.8 y: 0 z: 0 w:0]];
369 [writer WritePlacedElement: element];
370
371 element = [eb CreateImageWithMatrix: mask mtx: [[PTMatrix2D alloc] initWithA: 200 b: 0 c: 0 d: -200 h: 40 v: 680]];
372 [[element GetGState] SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 0.1 y: 0 z: 0 w:0]];
373 [writer WritePlacedElement: element];
374
375 [[element GetGState] SetFillColorSpace: [PTColorSpace CreateDeviceRGB]];
376 [[element GetGState] SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w:0]];
377 element = [eb CreateImageWithMatrix: mask mtx: [[PTMatrix2D alloc] initWithA: 200 b: 0 c: 0 d: -200 h: 320 v: 680]];
378 [writer WritePlacedElement: element];
379
380 [[element GetGState] SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 0 y: 1 z: 0 w:0]];
381 element = [eb CreateImageWithMatrix: mask mtx: [[PTMatrix2D alloc] initWithA: 200 b: 0 c: 0 d: -200 h: 40 v: 380]];
382 [writer WritePlacedElement: element];
383
384 {
385 // This sample illustrates Explicit Masking.
386 PTImage *img = [PTImage CreateWithFile: [doc GetSDFDoc] filename: @"../../TestFiles/peppers.jpg" encoder_hints: [[PTObj alloc] init]];
387
388 // mask is the explicit mask for the primary (base) image
389 [img SetMaskWithImage: mask];
390
391 element = [eb CreateImageWithMatrix: img mtx: [[PTMatrix2D alloc] initWithA: 200 b: 0 c: 0 d: -200 h: 320 v: 380]];
392 [writer WritePlacedElement: element];
393 }
394
395 [writer End]; // save changes to the current page
396 [doc PagePushBack: page];
397
398 // Transparency sample ----------------------------------
399
400 // Start a new page -------------------------------------
401 page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 612 y2: 792]];
402 [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // begin writing to this page
403 [eb Reset: [[PTGState alloc] init]]; // Reset the GState to default
404
405 // Write some transparent text at the bottom of the page.
406 element = [eb CreateTextBeginWithFont: [PTFont Create: [doc GetSDFDoc] type: e_pttimes_roman embed: NO] font_sz: 100];
407
408 // Set the text knockout attribute. Text knockout must be set outside of
409 // the text group.
410 gstate = [element GetGState];
411 [gstate SetTextKnockout: NO];
412 [gstate SetBlendMode: e_ptbl_difference];
413 [writer WriteElement: element];
414
415 element = [eb CreateTextRun: @"Transparency"];
416 [element SetTextMatrix: 1 b: 0 c: 0 d: 1 h: 30 v: 30];
417 gstate = [element GetGState];
418 [gstate SetFillColorSpace: [PTColorSpace CreateDeviceCMYK]];
419 [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w:0]];
420
421 [gstate SetFillOpacity: 0.5];
422 [writer WriteElement: element];
423
424 // Write the same text on top the old; shifted by 3 points
425 [element SetTextMatrix: 1 b: 0 c: 0 d: 1 h: 33 v: 33];
426 [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 0 y: 1 z: 0 w:0]];
427 [gstate SetFillOpacity: 0.5];
428
429 [writer WriteElement: element];
430 [writer WriteElement: [eb CreateTextEnd]];
431
432 // Draw three overlapping transparent circles.
433 [eb PathBegin]; // start constructing the path
434 [eb MoveTo: 459.223 y: 505.646];
435 [eb CurveTo: 459.223 cy1: 415.841 cx2: 389.85 cy2: 343.04 x2: 304.273 y2: 343.04];
436 [eb CurveTo: 218.697 cy1: 343.04 cx2: 149.324 cy2: 415.841 x2: 149.324 y2: 505.646];
437 [eb CurveTo: 149.324 cy1: 595.45 cx2: 218.697 cy2: 668.25 x2: 304.273 y2: 668.25];
438 [eb CurveTo: 389.85 cy1: 668.25 cx2: 459.223 cy2: 595.45 x2: 459.223 y2: 505.646];
439 element = [eb PathEnd];
440 [element SetPathFill: YES];
441
442 gstate = [element GetGState];
443 [gstate SetFillColorSpace: [PTColorSpace CreateDeviceRGB]];
444 [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 0 y: 0 z: 1 w:0]]; // Blue Circle
445
446 [gstate SetBlendMode: e_ptbl_normal];
447 [gstate SetFillOpacity: 0.5];
448 [writer WriteElement: element];
449
450 // Translate relative to the Blue Circle
451 [gstate SetTransform: 1 b: 0 c: 0 d: 1 h: 113 v: -185];
452 [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 0 y: 1 z: 0 w:0]]; // Green Circle
453 [gstate SetFillOpacity: 0.5];
454 [writer WriteElement: element];
455
456 // Translate relative to the Green Circle
457 [gstate SetTransform: 1 b: 0 c: 0 d: 1 h: -220 v: 0];
458 [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w:0]]; // Red Circle
459 [gstate SetFillOpacity: 0.5];
460 [writer WriteElement: element];
461
462 [writer End]; // save changes to the current page
463 [doc PagePushBack: page];
464
465 // End page ------------------------------------
466
467 [doc SaveToFile: @"../../TestFiles/Output/element_builder.pdf" flags: e_ptremove_unused];
468 // doc.Save((output_path + "element_builder.pdf").c_str(), Doc::e_ptlinearized, NULL);
469 NSLog(@"Done. Result saved in element_builder.pdf...");
470 }
471 @catch(NSException *e)
472 {
473 NSLog(@"%@", e.reason);
474 ret = 1;
475 }
476 [PTPDFNet Terminate: 0];
477 return ret;
478 }
479}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales