Sample code in Swift and Obj-C for using Apryse iOS SDK to extract text, paths, and images from a PDF. The sample also shows how to do color conversion, image normalization, and process changes in the graphics state.
Learn more about our full PDF Data Extraction SDK Capabilities.
To start your free trial, get stated with iOS SDK.
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
9char m_buf[4000];
10
11void ProcessElements(PTElementReader *reader);
12
13void ProcessPath(PTElementReader *reader, PTElement *path)
14{
15    if ([path IsClippingPath])
16    {
17        NSLog(@"This is a clipping path");
18    }
19
20    PTPathData* pathData = [path GetPathData];
21    NSMutableArray* data = [pathData GetPoints];
22    NSData* opr = [pathData GetOperators];
23
24    NSUInteger opr_index = 0;
25    NSUInteger opr_end = opr.length;
26    NSUInteger data_index = 0;
27    NSUInteger data_end = data.count;
28    
29    double x1, y1, x2, y2, x3, y3;
30    NSString *str = @"";
31    
32    // Use path.GetCTM() if you are interested in CTM (current transformation matrix).
33
34    unsigned char* opr_data = (unsigned char*)opr.bytes;
35    str = [str stringByAppendingFormat: @" Path Data Points := \""];
36    for (; opr_index<opr_end; opr_index = opr_index + 1)
37    {
38        switch(opr_data[opr_index])
39        {
40        case e_ptmoveto:
41            x1 = [data[data_index] doubleValue]; ++data_index;
42            y1 = [data[data_index] doubleValue]; ++data_index;
43            sprintf(m_buf, "M%.5g %.5g", x1, y1);
44            str = [str stringByAppendingFormat: @"%s", m_buf];
45            break;
46        case e_ptlineto:
47            x1 = [data[data_index] doubleValue]; ++data_index;
48            y1 = [data[data_index] doubleValue]; ++data_index;
49            sprintf(m_buf, " L%.5g %.5g", x1, y1);
50            str = [str stringByAppendingFormat: @"%s", m_buf];
51            break;
52        case e_ptcubicto:
53            x1 = [data[data_index] doubleValue]; ++data_index;
54            y1 = [data[data_index] doubleValue]; ++data_index;
55            x2 = [data[data_index] doubleValue]; ++data_index;
56            y2 = [data[data_index] doubleValue]; ++data_index;
57            x3 = [data[data_index] doubleValue]; ++data_index;
58            y3 = [data[data_index] doubleValue]; ++data_index;
59            sprintf(m_buf, " C%.5g %.5g %.5g %.5g %.5g %.5g", x1, y1, x2, y2, x3, y3);
60            str = [str stringByAppendingFormat: @"%s", m_buf];
61            break;
62        case e_ptrect:
63            {
64                x1 = [data[data_index] doubleValue]; ++data_index;
65                y1 = [data[data_index] doubleValue]; ++data_index;
66                double w = [data[data_index] doubleValue]; ++data_index;
67                double h = [data[data_index] doubleValue]; ++data_index;
68                x2 = x1 + w;
69                y2 = y1;
70                x3 = x2;
71                y3 = y1 + h;
72                double x4 = x1; 
73                double y4 = y3;
74                sprintf(m_buf, "M%.5g %.5g L%.5g %.5g L%.5g %.5g L%.5g %.5g Z", 
75                    x1, y1, x2, y2, x3, y3, x4, y4);
76                str = [str stringByAppendingFormat: @"%s", m_buf];
77            }
78            break;
79        case e_ptclosepath:
80            str = [str stringByAppendingString: @" Close Path"];
81            break;
82        default: 
83            assert(false);
84            break;
85        }    
86    }
87
88    str = [str stringByAppendingString: @"\" "];
89
90    PTGState *gs = [path GetGState];
91
92    // Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
93    if ([path IsStroked]) 
94    {
95        str = [str stringByAppendingString: @"Stroke path\n"]; 
96
97        if ([[gs GetStrokeColorSpace] GetType] == e_ptpattern)
98        {
99            str = [str stringByAppendingString: @"Path has associated pattern"]; 
100        }
101        else
102        {
103            // Get stroke color (you can use PDFNet color conversion facilities)
104            // ColorPt rgb;
105            // gs.GetStrokeColorSpace().Convert2RGB(gs.GetStrokeColor(), rgb);
106        }
107    }
108    else 
109    {
110        // Do not stroke path
111    }
112
113    if ([path IsFilled])
114    {
115        str = [str stringByAppendingString: @"Fill path"]; 
116
117        if ([[gs GetFillColorSpace] GetType] == e_ptpattern)
118        {        
119            str = [str stringByAppendingString: @"Path has associated pattern"]; 
120        }
121        else
122        {
123            // PTColorPt *rgb = [[[PTColorPt alloc] init] autorelease];
124            // [[gs GetFillColorSpace] Convert2RGB: [gs GetFillColorWithColorPt: rgb]];
125        }        
126    }
127    else 
128    {
129        // Do not fill path
130    }
131
132    // Process any changes in graphics state  ---------------------------------
133
134    PTGSChangesIterator *gs_itr = [reader GetChangesIterator];
135    for (; [gs_itr HasNext]; [gs_itr Next]) 
136    {
137        switch([gs_itr Current])
138        {
139        case e_pttransform :
140            // Get transform matrix for this element. Unlike path.GetCTM() 
141            // that return full transformation matrix gs.GetTransform() return 
142            // only the transformation matrix that was installed for this element.
143            //
144            // gs.GetTransform();
145            break;
146        case e_ptline_width :
147            // gs.GetLineWidth();
148            break;
149        case e_ptline_cap :
150            // gs.GetLineCap();
151            break;
152        case e_ptline_join :
153            // gs.GetLineJoin();
154            break;
155        case e_ptflatness :
156            break;
157        case e_ptmiter_limit :
158            // gs.GetMiterLimit();
159            break;
160        case e_ptdash_pattern :
161            {
162                // std::vector<double> dashes;
163                // gs.GetDashes(dashes);
164                // gs.GetPhase()
165            }
166            break;
167        case e_ptfill_color:
168            {
169                if ( [[gs GetFillColorSpace] GetType] == e_ptpattern &&
170                     [[gs GetFillPattern] GetType] != e_ptshading )
171                {    
172                    //process the pattern data
173                    [reader PatternBegin: YES reset_ctm_tfm: NO];
174                    ProcessElements(reader);
175                    [reader End];
176                }
177            }
178            break;
179        default:
180            break;
181        }
182    }
183    [reader ClearChangeList];
184    NSLog(@"%@", str);
185}
186
187void ProcessText(PTElementReader* page_reader) 
188{
189    // Begin text element
190    NSLog(@"Begin Text Block:");
191
192    PTElement *element; 
193    while ((element = [page_reader Next]) != NULL) 
194    {
195        switch ([element GetType])
196        {
197            case e_pttext_end:
198                // Finish the text block
199                //str = [str stringByAppendingString: @"End Text Block.\n"];
200                    NSLog(@"End Text Block.");
201                return;
202
203            case e_pttext_obj:
204            {
205                PTGState *gs = [element GetGState];
206
207                PTColorSpace *cs_fill = [gs GetFillColorSpace];
208                PTColorPt *fill = [gs GetFillColor];
209                
210                PTColorPt *outColor = [cs_fill Convert2RGB: fill];
211
212                PTColorSpace *cs_stroke = [gs GetStrokeColorSpace];
213                PTColorPt *stroke = [gs GetStrokeColor];
214
215                PTFont *font = [gs GetFont];
216
217                NSLog(@"Font Name: %@\n", [font GetName]);
218                
219                // font.IsFixedWidth();
220                // font.IsSerif();
221                // font.IsSymbolic();
222                // font.IsItalic();
223                // ... 
224
225                // double font_size = gs.GetFontSize();
226                // double word_spacing = gs.GetWordSpacing();
227                // double char_spacing = gs.GetCharSpacing();
228                // const UString* txt = element.GetTextString();
229
230                if ( [font GetType] == e_ptType3 )
231                {
232                    //type 3 font, process its data
233                    PTCharIterator *itr;
234                    for (itr = [element GetCharIterator]; [itr HasNext]; [itr Next]) 
235                    {
236                        [page_reader Type3FontBegin: [itr Current] resource_dict: 0];
237                        ProcessElements(page_reader);
238                        [page_reader End];
239                    }
240                }
241
242                else
243                {    
244                    PTMatrix2D *text_mtx = [element GetTextMatrix];
245                    double x, y;
246                    unsigned int char_code;
247
248                    PTCharIterator *itr;
249                    NSString* str = @"";
250                    for (itr = [element GetCharIterator]; [itr HasNext]; [itr Next]) 
251                    {
252                        char_code = [[itr Current] getChar_code];
253                        if (char_code>=32 || char_code<=255) { // Print if in ASCII range...
254                            str = [str stringByAppendingFormat: @"%c", char_code];
255                        }
256
257                        x = [[itr Current] getX];        // character positioning information
258                        y = [[itr Current] getY];
259
260                        // Use element.GetCTM() if you are interested in the CTM 
261                        // (current transformation matrix).
262                        PTMatrix2D *ctm = [element GetCTM];
263
264                        // To get the exact character positioning information you need to 
265                        // concatenate current text matrix with CTM and then multiply 
266                        // relative positioning coordinates with the resulting matrix.
267                        PTMatrix2D *mtx = text_mtx;
268                        [mtx Concat: [ctm getM_a] b: [ctm getM_b] c: [ctm getM_c] d: [ctm getM_d] h: [ctm getM_h] v: [ctm getM_v]];
269                        [mtx Mult: [[PTPDFPoint alloc] initWithPx: x py: y]];
270
271                        // Get glyph path...
272                        //vector<UChar> oprs;
273                        //vector<double> glyph_data;
274                        //font.GetGlyphPath(char_code, oprs, glyph_data, false, 0);
275                    }
276                    NSLog(@"%@", str);
277                }
278
279                //str = [str stringByAppendingString: @"\n"];
280            }
281                break;
282            default:
283                break;
284        }
285    }
286}
287
288void ProcessImage(PTElement *image)  
289{
290    bool image_mask = [image IsImageMask];
291    bool interpolate = [image IsImageInterpolate];
292    int width = [image GetImageWidth];
293    int height = [image GetImageHeight];
294    int out_data_sz = width * height * 3;
295
296    NSLog(@"Image:  width=\"%d\" height=\"%d\"", width, height);
297
298    // Matrix2D& mtx = image->GetCTM(); // image matrix (page positioning info)
299
300    // You can use GetImageData to read the raw (decoded) image data
301    //image->GetBitsPerComponent();    
302    //image->GetImageData();    // get raw image data
303    // .... or use Image2RGB filter that converts every image to RGB format,
304    // This should save you time since you don't need to deal with color conversions, 
305    // image up-sampling, decoding etc.
306
307    PTImage2RGB *img_conv = [[PTImage2RGB alloc] initWithImage_element: image];    // Extract and convert image to RGB 8-bpc format
308    PTFilterReader *reader = [[PTFilterReader alloc] initWithFilter: img_conv];
309
310    // A buffer used to keep image data.
311    NSData *image_data_out = [reader Read: out_data_sz];
312    // &image_data_out.front() contains RGB image data.
313
314    // Note that you don't need to read a whole image at a time. Alternatively
315    // you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz) 
316    // until the function returns 0. 
317}
318
319void ProcessElements(PTElementReader *reader) 
320{
321    PTElement *element;
322    while ((element = [reader Next]) != NULL)     // Read page contents
323    {
324        switch ([element GetType])
325        {
326        case e_ptpath:                        // Process path data...
327            {
328                ProcessPath(reader, element);
329            }
330            break; 
331        case e_pttext_begin:                 // Process text block...
332            {
333                ProcessText(reader);
334            }
335            break;
336        case e_ptform:                        // Process form XObjects
337            {
338                [reader FormBegin]; 
339                ProcessElements(reader);
340                [reader End];
341            }
342            break; 
343        case e_ptimage:                        // Process Images
344            {
345                ProcessImage(element);
346            }    
347            break;
348                
349        default:
350            break;
351        }
352
353    }
354}
355
356int main(int argc, char *argv[])
357{
358    @autoreleasepool {
359        int ret = 0;
360        [PTPDFNet Initialize: 0];
361
362        @try    // Extract text data from all pages in the document
363        {
364            NSLog(@"__________________________________________________");
365            NSLog(@"Extract page element information from all ");
366            NSLog(@"pages in the document.");
367
368            PTPDFDoc *doc = [[PTPDFDoc alloc] initWithFilepath: @"../../TestFiles/newsletter.pdf"];
369            [doc InitSecurityHandler];
370
371            int pgnum = [doc GetPageCount];
372            PTPageIterator *page_begin = [doc GetPageIterator: 1];
373
374            PTElementReader *page_reader = [[PTElementReader alloc] init];
375
376            PTPageIterator *itr;
377            for (itr = page_begin; [itr HasNext]; [itr Next])        //  Read every page
378            {                
379                NSLog(@"Page %d----------------------------------------", [[itr Current] GetIndex]);
380                [page_reader Begin: [itr Current]];
381                ProcessElements(page_reader);
382                [page_reader End];
383            }
384
385            NSLog(@"Done.");
386        }
387        @catch(NSException *e)
388        {
389            NSLog(@"%@", e.reason);
390            ret = 1;
391        }
392        [PTPDFNet Terminate: 0];
393        return ret;
394    }
395    
396}
1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2019 by PDFTron Systems Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6import PDFNet
7import Foundation
8
9func ProcessPath(reader: PTElementReader, path: PTElement) {
10    if path.isClippingPath() {
11        print("This is a clipping path")
12    }
13    
14    let pathData: PTPathData = path.getPathData()
15    let data: NSMutableArray = pathData.getPoints()
16    let opr: Data = pathData.getOperators()
17    
18    var opr_index: Int = 0
19    let opr_end: Int = opr.count
20    var data_index: Int = 0
21    
22    var x1: Double = 0.0
23    var y1: Double = 0.0
24    var x2: Double = 0.0
25    var y2: Double = 0.0
26    var x3: Double = 0.0
27    var y3: Double = 0.0
28    var str = ""
29    
30    // Use path.GetCTM() if you are interested in CTM (current transformation matrix).
31    
32    str += (" Path Data Points := \"")
33    
34    while opr_index < opr_end {
35        switch PTPathSegmentType(rawValue: UInt32(opr[opr_index])) {
36        case e_ptmoveto:
37            x1 = data[data_index] as! Double
38            data_index += 1
39            y1 = data[data_index] as! Double
40            data_index += 1
41            str += String(format: "M%.5g %.5g", x1, y1)
42        case e_ptlineto:
43            x1 = data[data_index] as! Double
44            data_index += 1
45            y1 = data[data_index] as! Double
46            data_index += 1
47            str += String(format: " L%.5g %.5g", x1, y1)
48        case e_ptcubicto:
49            x1 = data[data_index] as! Double
50            data_index += 1
51            y1 = data[data_index] as! Double
52            data_index += 1
53            x2 = data[data_index] as! Double
54            data_index += 1
55            y2 = data[data_index] as! Double
56            data_index += 1
57            x3 = data[data_index] as! Double
58            data_index += 1
59            y3 = data[data_index] as! Double
60            data_index += 1
61            str += String(format: " C%.5g %.5g %.5g %.5g %.5g %.5g", x1, y1, x2, y2, x3, y3)
62        case e_ptrect:
63            x1 = data[data_index] as! Double
64            data_index += 1
65            y1 = data[data_index] as! Double
66            data_index += 1
67            let w = data[data_index] as! Double
68            data_index += 1
69            let h = data[data_index] as! Double
70            data_index += 1
71            x2 = x1 + w
72            y2 = y1
73            x3 = x2
74            y3 = y1 + h
75            let x4: Double = x1
76            let y4: Double = y3
77            str += String(format: "M%.5g %.5g L%.5g %.5g L%.5g %.5g L%.5g %.5g Z", x1, y1, x2, y2, x3, y3, x4, y4)
78        case e_ptclosepath:
79            str += (" Close Path")
80        default:
81            assert(false)
82        }
83        opr_index = opr_index + 1
84    }
85    
86    str += ("\" ")
87    
88    let gs: PTGState = path.getGState()
89    
90    // Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
91    if path.isStroked() {
92        str = str + ("Stroke path")
93        if gs.getStrokeColorSpace().getType() == e_ptpattern {
94            str = str + ("Path has associated pattern")
95        }
96        else {
97            // Get stroke color (you can use PDFNet color conversion facilities)
98            // let rgb: PTColorPt = gs.getStrokeColorSpace().convert2RGB(gs.getStrokeColor())
99        }
100    }
101    else {
102        // Do not stroke path
103    }
104    
105    if path.isFilled() {
106        str = str + ("Fill path")
107        if gs.getFillColorSpace().getType() == e_ptpattern {
108            str = str + ("Path has associated pattern")
109        }
110        else {
111            // let rgb: PTColorPt = gs.getFillColorSpace().convert2RGB(gs.getFillColor())
112        }
113    }
114    else {
115        // Do not fill path
116    }
117    
118    // Process any changes in graphics state  ---------------------------------
119    
120    let gs_itr: PTGSChangesIterator = reader.getChangesIterator()
121    
122    while gs_itr.hasNext() {
123        switch PTGStateAttribute(rawValue: UInt32(gs_itr.current())) {
124        case e_pttransform:
125            // Get transform matrix for this element. Unlike path.GetCTM()
126            // that return full transformation matrix gs.GetTransform() return
127            // only the transformation matrix that was installed for this element.
128            //
129            // gs.getTransform()
130            break
131        case e_ptline_width:
132            // gs.getLineWidth()
133            break
134        case e_ptline_cap:
135            // gs.getLineCap()
136            break
137        case e_ptline_join:
138            // gs.getLineJoin()
139            break
140        case e_ptflatness:
141            break
142        case e_ptmiter_limit:
143            // gs.GetmiterLimit()
144            break
145        case e_ptdash_pattern:
146            // let dashes: NSMutableArray = gs.getDashes()
147            // gs.getPhase()
148            break
149        case e_ptfill_color:
150            if gs.getFillColorSpace().getType() == e_ptpattern && gs.getFillPattern().getType() != e_ptshading {
151                //process the pattern data
152                reader.patternBegin(true, reset_ctm_tfm: false)
153                ProcessElements(reader: reader)
154                reader.end()
155            }
156        default:
157            break
158        }
159        gs_itr.next()
160    }
161    reader.clearChangeList()
162    print("\(str)")
163    
164}
165
166func ProcessText(page_reader: PTElementReader) {
167    // Begin text element
168    print("Begin Text Block:")
169    
170    while let element = page_reader.next() {
171        switch element.getType() {
172        case e_pttext_end:
173            // Finish the text block
174            print("End Text Block.")
175            return
176        case e_pttext_obj:
177            let gs: PTGState = element.getGState()
178            
179            let cs_fill: PTColorSpace = gs.getFillColorSpace()
180            let fill: PTColorPt = gs.getFillColor()
181            
182            let _: PTColorPt = cs_fill.convert2RGB(fill) // outColor
183            
184            let _: PTColorSpace = gs.getStrokeColorSpace()  // cs_stroke
185            let _: PTColorPt = gs.getStrokeColor() // stroke
186            
187            let font: PTFont = gs.getFont()
188            
189            print("Font Name: \(font.getName()!)")
190            
191            // font.IsFixedWidth();
192            // font.IsSerif();
193            // font.IsSymbolic();
194            // font.IsItalic();
195            // ...
196            
197            // double font_size = gs.GetFontSize();
198            // double word_spacing = gs.GetWordSpacing();
199            // double char_spacing = gs.GetCharSpacing();
200            // const UString* txt = element.GetTextString();
201            
202            if font.getType() == e_ptType3 {
203                //type 3 font, process its data
204                let itr: PTCharIterator = element.getCharIterator()
205                while itr.hasNext() {
206                    page_reader.type3FontBegin(itr.current(), resource_dict: nil)
207                    ProcessElements(reader: page_reader)
208                    page_reader.end()
209                    itr.next()
210                }
211            }
212            else {
213                let text_mtx: PTMatrix2D = element.getTextMatrix()
214                var x: Double
215                var y: Double
216                var char_code: UInt32
217                
218                var str = ""
219                let itr: PTCharIterator = element.getCharIterator()
220                while itr.hasNext() {
221                    char_code = itr.current().getChar_code()
222                    if char_code >= 32 || char_code <= 255 {
223                        // Print if in ASCII range...
224                        if let scalar = UnicodeScalar(char_code){
225                            str += ("\(Character(scalar))")
226                        }
227                    }
228                    
229                    x = itr.current().getX()   // character positioning information
230                    y = itr.current().getY()
231                    
232                    // Use element.getCTM() if you are interested in the CTM
233                    // (current transformation matrix).
234                    let ctm: PTMatrix2D = element.getCTM()
235                    
236                    // To get the exact character positioning information you need to
237                    // concatenate current text matrix with CTM and then multiply
238                    // relative positioning coordinates with the resulting matrix.
239                    let mtx: PTMatrix2D = text_mtx
240                    mtx.concat(ctm.getM_a(), b: ctm.getM_b(), c: ctm.getM_c(), d: ctm.getM_d(), h: ctm.getM_h(), v: ctm.getM_v())
241                    mtx.mult(PTPDFPoint(px: x, py: y))
242                    
243                    // Get glyph path...
244                    //vector<UChar> oprs;
245                    //vector<double> glyph_data;
246                    //font.GetGlyphPath(char_code, oprs, glyph_data, false, 0);
247                    itr.next()
248                }
249                print("\(str)")
250            }
251        default:
252            break
253        }
254    }
255}
256
257func ProcessImage(image: PTElement) {
258    let _: Bool = image.isImageMask()  // image_mask
259    let _: Bool = image.isImageInterpolate()  // interpolate
260    let width = image.getImageWidth()
261    let height = image.getImageHeight()
262    let out_data_sz = width * height * 3
263    
264    print("Image:  width=\"\(width)\" height=\"\(height)\"")
265    
266    //let mtx: PTMatrix2D = image.getCTM()    // image matrix (page positioning info)
267    // You can use GetImageData to read the raw (decoded) image data
268    //image.getBitsPerComponent()
269    //image.getImageData()    // get raw image data
270    // .... or use Image2RGB filter that converts every image to RGB format,
271    // This should save you time since you don't need to deal with color conversions,
272    // image up-sampling, decoding etc.
273    
274    let img_conv = PTImage2RGB(image_element: image)    // Extract and convert image to RGB 8-bpc format
275    let reader: PTFilterReader = PTFilterReader(filter: img_conv)
276    
277    // A buffer used to keep image data.
278    let _: Data = reader.read(UInt(out_data_sz)) // image_data_out
279    // &image_data_out.front() contains RGB image data.
280    
281    // Note that you don't need to read a whole image at a time. Alternatively
282    // you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz)
283    // until the function returns 0.
284}
285
286func ProcessElements(reader: PTElementReader) {
287    while let element = reader.next() {
288        switch element.getType() {
289        case e_ptpath:
290            // Process path data...
291            ProcessPath(reader: reader, path: element)
292        case e_pttext_begin:
293            // Process text block...
294            ProcessText(page_reader: reader)
295        case e_ptform:
296            // Process form XObjects
297            reader.formBegin()
298            ProcessElements(reader: reader)
299            reader.end()
300        case e_ptimage:
301            // Process Images
302            ProcessImage(image: element)
303        default:
304            break
305        }
306    }
307}
308
309func runElementReaderAdvTest() -> Int {
310    return autoreleasepool {
311        var ret: Int = 0
312        
313        
314        do {
315            try PTPDFNet.catchException {
316                // Extract text data from all pages in the document
317                print("__________________________________________________")
318                print("Extract page element information from all ")
319                print("pages in the document.")
320                
321                let doc: PTPDFDoc = PTPDFDoc(filepath: Bundle.main.path(forResource: "newsletter", ofType: "pdf"))
322                doc.initSecurityHandler()
323                
324                let page_begin: PTPageIterator = doc.getPageIterator(1)
325                
326                let page_reader: PTElementReader = PTElementReader()
327                
328                let itr: PTPageIterator = page_begin
329                while itr.hasNext() {
330                    print("Page \(itr.current().getIndex())----------------------------------------")
331                    page_reader.begin(itr.current())
332                    ProcessElements(reader: page_reader)
333                    page_reader.end()
334                    itr.next()
335                }
336                
337                print("Done.")
338            }
339        } catch let e as NSError {
340            print("\(e)")
341            ret = 1
342        }
343        
344        return ret
345    }
346}
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales