// Copyright (C) 2003-2026, Foxit Software Inc..
// All Rights Reserved.
//
// http://www.foxitsoftware.com
//
// The following code is copyrighted and contains proprietary information and trade secrets of Foxit Software Inc..
// You cannot distribute any part of Foxit PDF SDK to any third party or general public,
// unless there is a separate license agreement with Foxit Software Inc. which explicitly grants you such rights.
//
// This file contains an example to demonstrate how to use Foxit PDF SDK to associate files with PDF.

#include "FSPDFObjC.h"

NSString *input_path = @"../input_files/";
NSString *output_path = @"../output_files/";

NSString *sn = @"bPxThFlBAYifO3yiW0C51bKbjPap1zHaf5aG5sX378TjFvB7Q0802A==";
NSString *key = @"8f3g1sGNtR8NAgfSNv+BvaJWy2fnrw36NZvzh8GUxYXvs+/YczAk4e34yqlOnmL4asgxGMO7+rlOcSHUSSKagx5g4G7VEXfkYLXnDDx77FOOedZPrFFcOCu1vT4+wOKzy1zkORq9cKtiFf44byH5sfRqf+OvELjep2teT9eF9yPCsSw2vRKljXlPy40DMP9XX1O5OTe+mSG3RvwKUMb0+RpG3YobM87j+/ynTDlJMMXw4vgwuTb1ZuAtlvkTdFz7HDLV81C6TYnn73ddHeGD3qYWBVsD4uJkDwyyH/SWr7QX0HSgVg8HSZqnab1VGdAUYxKY2vGWeOTJdu6qDGb5ZEb9O3kZZg+9LYAmUKzDfCYwfIsYZ8Y9LPC4wMx70kIq7JpNnzPvigrdshV8/ew1F0+qP/P5Z1EB53vDVrp2LVSuL873Wv4g3PzjZlNH+1qgT87hC+Buto4qsSUfP5Y2z0uApu+c/TfttCXbeMLSsw9TY1HQD3mkrZExMaQqRrAuXAcZWdvBnVuG3DCZmm8q0DLxJiCfYfViAU/AkbeKMIGaTJdrvIXBcssfde4nmU6qr0M4x4+Hs2ESddtY2QRtLAUBq/7h7GJlJHkEZr8ueaUy/6D/2vvAWz6wMLgmjlPbDTLqzfnCJylHCPtcJmu1SPPT9v1VreBX3+zNWcIT8HueyHEiUniqzwWkRKaotrnNb/zhnHLPpgaBSwKjyi8rOzzVwu/nY84G9ye4jsVjmkIRItKDrW7z22iS+QQuTwfqLVuMB0OD4jMkPCC2slzPfEeZnuh4+Rewln2qLRJVgI72zJ20OBWAelGSpO1Ii2Y3gOvojdmtUwx6R3Z1qFNWTTB25o9ytcux8BLOtTCkA8T8xaT4N339OTCsu/TmfahIDixKvr8cDa6F2WkBT8SOfv64W9PgiNv5B/BMOD2VWYaAl0N7yD0NkYYg/cDWRC4dfpdeip1jrr0k3mND0oSpIkxC+FicZbaYYwRdtJ9OSVWJ5OE0voLv8HgLze8mw6s4TrN3z+a2NQCveMa2VRN7YcEF7agPrvb/qU+otMwHae/wM7+f+HFAaOW+WTfzfxIVFB8/A1DN0NiEX+em4G7nO5F5ZcvfYPpaaioe8Nn33BbVLtMjXqsOBeM8AXiVfpD6YO75j7BqnBUPnivvD8BxJaaIQZvVSuQmvGm0ynt03IYwE8Rq+jYsunvsZ86jh5BiUR+Nm7sK0Qe9BdZn/7jdqohMv9czfMGUAscX+xBMunFbDpcRbWVpUADcm209L8Ndek2XY7YlCMxPgPVNvfWRF2jxehib9Tpd7zB5ZIDfAEH9zrpLfp29DLvCh2Yx8mH7xxdKG1O9Q0GdHphYHhej6smAcPm68R0GUQaTOLfXqNpueGJC1GAOI3Y4swjyt7jMTWcibnKLzwukWE5rnEDhn5n64JnyUM0ZFkRmtOiV";

int main(int argc, const char *argv[]) {
    @autoreleasepool {
        NSString *output_directory = [output_path stringByAppendingString:@"associated_files/"];
        NSString *input_file = [input_path stringByAppendingString:@"AF_Catalog_Page_Annot.pdf"];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL isDir = NO;
        if ([fileManager fileExistsAtPath:output_directory isDirectory:&isDir]) {
            if (!isDir) {
                NSError *error = nil;
                if (![fileManager removeItemAtPath:output_directory error:&error] || error) {
                    return -1;
                }
                if (![fileManager createDirectoryAtPath:output_directory withIntermediateDirectories:YES attributes:nil error:&error] || error) {
                    return -1;
                }
            }
        } else {
            NSError *error = nil;
            if (![fileManager createDirectoryAtPath:output_directory withIntermediateDirectories:YES attributes:nil error:&error] || error) {
                return -1;
            }
        }

        // Initialize library
        FSErrorCode errorCode = [FSLibrary initialize:sn key:key];
        if (errorCode != FSErrSuccess) {
            NSLog(@"Library Initialize Error: %ld", errorCode);
            return -1;
        }

        @try {
            FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
            errorCode = [doc load:@""];
            if (errorCode != FSErrSuccess) {
                NSLog(@"The Doc [%@] Error: %ld\n", input_file, errorCode);
                return -1;
            }

            FSAssociatedFiles *associated_files = [[FSAssociatedFiles alloc] initWithPdf_doc:doc];

            FSPDFDictionary *doc_catalog_dict = [doc getCatalog];
            int count = [associated_files getAssociatedFilesCount:doc_catalog_dict];
            NSLog(@"The catalog dictionary of \"AF_Catalog_Page_Annot.pdf\" has %d associated files.\n", count);
            FSFileSpec *filespec = [associated_files getAssociatedFile:doc_catalog_dict index:0];
            NSLog(@"The file name is \"%@\".\n\n", filespec.fileName);
            [filespec exportToFile:[[NSString alloc] initWithFormat:@"%@af_1.txt", output_directory]];

            FSPDFPage *page = [doc getPage:0];
            FSPDFDictionary *page_dict = [page getDict];
            count = [associated_files getAssociatedFilesCount:page_dict];
            NSLog(@"The page dictionary of \"AF_Catalog_Page_Annot.pdf\" has %d associated files.\n", count);
            filespec = [associated_files getAssociatedFile:page_dict index:0];
            NSLog(@"The file name is \"%@\".\n\n", filespec.fileName);
            [filespec exportToFile:[[NSString alloc] initWithFormat:@"%@af_2.txt", output_directory]];

            FSAnnot *annot = [page getAnnot:0];
            FSPDFDictionary *annot_dict = [annot getDict];
            count = [associated_files getAssociatedFilesCount:annot_dict];
            NSLog(@"The annot dictionary of \"AF_Catalog_Page_Annot.pdf\" has %d associated files.\n", count);
            filespec = [associated_files getAssociatedFile:annot_dict index:0];
            NSLog(@"The file name is \"%@\".\n\n", filespec.fileName);
            [filespec exportToFile:[[NSString alloc] initWithFormat:@"%@af_4.txt", output_directory]];
        }
        @catch (NSException *e) {
            NSLog(@"Unknown Exception, %@", e);
        }

        input_file = [input_path stringByAppendingString:@"AF_ImageXObject_FormXObject.pdf"];
        @try {
            FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
            errorCode = [doc load:@""];
            if (errorCode != FSErrSuccess) {
                NSLog(@"The Doc [%@] Error: %ld\n", input_file, errorCode);
                return -1;
            }

            FSAssociatedFiles *associated_files = [[FSAssociatedFiles alloc] initWithPdf_doc:doc];

            FSPDFPage *page = [doc getPage:0];
            [page startParse:FSPDFPageParsePageNormal pause:nil is_reparse:NO];

            long pos = [page getFirstGraphicsObjectPosition:FSGraphicsObjectTypeImage];
            FSGraphicsObject *image_x_object = [page getGraphicsObject:pos];
            FSPDFDictionary *image_x_object_dict = [[[image_x_object getImageObject] getStream] getDictionary];
            int count = [associated_files getAssociatedFilesCount:image_x_object_dict];
            NSLog(@"The image x object of \"AF_ImageXObject_FormXObject.pdf\" has %d associated files.\n", count);
            FSFileSpec *filespec = [associated_files getAssociatedFile:image_x_object_dict index:0];
            NSLog(@"The file name is \"%@\".\n\n", filespec.fileName);
            [filespec exportToFile:[[NSString alloc] initWithFormat:@"%@2.jpg", output_directory]];

            pos = [page getFirstGraphicsObjectPosition:FSGraphicsObjectTypeFormXObject];
            FSGraphicsObject *form_x_object = [page getGraphicsObject:pos];
            FSPDFDictionary *form_x_object_dict = [[[form_x_object getFormXObject] getStream] getDictionary];
            count = [associated_files getAssociatedFilesCount:form_x_object_dict];
            NSLog(@"The form x object of \"AF_ImageXObject_FormXObject.pdf\" has %d associated files.\n", count);
            filespec = [associated_files getAssociatedFile:form_x_object_dict index:0];
            NSLog(@"The file name is \"%@\".\n\n", filespec.fileName);
            [filespec exportToFile:[[NSString alloc] initWithFormat:@"%@af_6.txt", output_directory]];
        }
        @catch (NSException *e) {
            NSLog(@"Unknown Exception, %@", e);
        }

        input_file = [input_path stringByAppendingString:@"AF_MarkedContent.pdf"];
        @try {
            FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
            errorCode = [doc load:@""];
            if (errorCode != FSErrSuccess) {
                NSLog(@"The Doc [%@] Error: %ld\n", input_file, errorCode);
                return -1;
            }

            FSAssociatedFiles *associated_files = [[FSAssociatedFiles alloc] initWithPdf_doc:doc];

            FSPDFPage *page = [doc getPage:0];
            [page startParse:FSPDFPageParsePageNormal pause:nil is_reparse:NO];

            long pos = [page getFirstGraphicsObjectPosition:FSGraphicsObjectTypeText];
            FSGraphicsObject *text_object = [page getGraphicsObject:pos];
            int count = [associated_files getAssociatedFilesCountWithGraphicsObject:text_object];
            NSLog(@"The text object of \"AF_MarkedContent.pdf\" has %d associated files.\n", count);
            FSFileSpec *filespec = [associated_files getAssociatedFileWithGraphicsObject:text_object index:0];
            NSLog(@"The first file name is \"%@\".\n\n", filespec.fileName);
            [filespec exportToFile:[[NSString alloc] initWithFormat:@"%@bitmap.bmp", output_directory]];

            filespec = [associated_files getAssociatedFileWithGraphicsObject:text_object index:1];
            NSLog(@"The second file name is \"%@\".\n\n", filespec.fileName);
            [filespec exportToFile:[[NSString alloc] initWithFormat:@"%@text.txt", output_directory]];
        }
        @catch (NSException *e) {
            NSLog(@"Unknown Exception, %@", e);
        }

        // Associate files with objects in PDF documents.
        input_file = [input_path stringByAppendingString:@"AssociateTestFile.pdf"];
        @try {
            FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
            errorCode = [doc load:@""];
            if (errorCode != FSErrSuccess) {
                NSLog(@"The Doc [%@] Error: %ld\n", input_file, errorCode);
                return -1;
            }

            FSAssociatedFiles *associated_files = [[FSAssociatedFiles alloc] initWithPdf_doc:doc];

            // Create filespec with 1.txt
            FSFileSpec *filespec_txt = [[FSFileSpec alloc] initWithDocument:doc];
            filespec_txt.associteFileRelationship = FSAssociatedFilesRelationshipSource;
            filespec_txt.fileName = @"1.txt";
            FSDateTime *dateTime = [[FSDateTime alloc] initWithYear:2017 month:9 day:15 hour:17 minute:20 second:20 milliseconds:0 utc_hour_offset:8 utc_minute_offset:0];
            filespec_txt.description = @"text";
            [filespec_txt embed:[input_path stringByAppendingString:@"1.txt"]];
            filespec_txt.creationDateTime = dateTime;
            filespec_txt.modifiedDateTime = dateTime;

            // Associate 1.txt with catalog dictionary.
            {
                FSPDFObject *catalog_dict = [doc getCatalog];
                [associated_files associateFile:catalog_dict file_spec:filespec_txt];
                NSLog(@"Associate a text file with catalog dictionary.\n");
                FSFileSpec *filespec_catalog = [associated_files getAssociatedFile:catalog_dict index:0];
                [filespec_catalog exportToFile:[[NSString alloc] initWithFormat:@"%@catalog.txt", output_directory]];
            }

            FSPDFPage *page = [doc getPage:0];
            [page startParse:FSPDFPageParsePageNormal pause:nil is_reparse:NO];

            // Associate 1.txt with page dictionary.
            {
                [associated_files associateFileWithPDFPage:page file_spec:filespec_txt];
                NSLog(@"Associate a text file with page.\n");

                FSFileSpec *filespec_page = [associated_files getAssociatedFile:[page getDict] index:0];
                [filespec_page exportToFile:[[NSString alloc] initWithFormat:@"%@page.txt", output_directory]];
            }

            // Associate 1.txt with annotation dictionary.
            {
                FSRectF *rect = [[FSRectF alloc] initWithLeft1:100 bottom1:50 right1:220 top1:100];
                FSAnnot *annot = [page addAnnot:FSAnnotNote rect:rect];
                [associated_files associateFileWithAnnot:annot file_spec:filespec_txt];
                NSLog(@"Associate a text file with annotation.\n");
                FSFileSpec *filespec_annot = [associated_files getAssociatedFile:[annot getDict] index:0];
                [filespec_annot exportToFile:[[NSString alloc] initWithFormat:@"%@annotation.txt", output_directory]];
            }

            // Create filespec with 2.jpg image.
            FSFileSpec *filespec_jpg = [[FSFileSpec alloc] initWithDocument:doc];
            filespec_jpg.associteFileRelationship = FSAssociatedFilesRelationshipSource;
            filespec_jpg.fileName = @"2.jpg";
            filespec_jpg.description = @"jpeg";
            [filespec_jpg embed:[input_path stringByAppendingString:@"2.jpg"]];
            filespec_jpg.creationDateTime = dateTime;
            filespec_jpg.modifiedDateTime = dateTime;

            // Associate 2.jpg with marked content.
            {
                FSPDFDictionary *page_dict = [page getDict];
                FSPDFDictionary *resource_dict = (FSPDFDictionary *) [page_dict getElement:@"Resources"];
                if (resource_dict == nil)
                    return 1;

                long position = [page getFirstGraphicsObjectPosition:FSGraphicsObjectTypeText];
                FSGraphicsObject *text_object = [page getGraphicsObject:position];
                if (text_object != nil) {
                    FSMarkedContent *markcontent = [text_object getMarkedContent];
                    if (markcontent == nil)
                        return 1;
                    if ([markcontent getItemCount] == 0)
                        [markcontent addItem:@"Associated" property_dict:NULL];
                    [associated_files associateFileWithGraphicsObject:text_object mark_content_item_index:0 current_resource:resource_dict property_name:@"textobject" file_spec:filespec_jpg];
                    [page generateContent];

                    NSLog(@"Associate a jpeg file with markcontent.\n");

                    FSFileSpec *filespec_text_object = [associated_files getAssociatedFileWithGraphicsObject:text_object index:0];
                    [filespec_text_object exportToFile:[[NSString alloc] initWithFormat:@"%@textobject.jpg", output_directory]];
                }
            }

            // Associate 2.jpg with image object.
            {
                long position = [page getFirstGraphicsObjectPosition:FSGraphicsObjectTypeImage];
                FSImageObject *image_object = [[page getGraphicsObject:position] getImageObject];
                if (image_object != nil) {
                    [associated_files associateFileWithImageObject:image_object file_spec:filespec_jpg];
                    NSLog(@"Associate a jpeg file with image xobject.\n");

                    FSFileSpec *filespec_image_object = [associated_files getAssociatedFile:[[image_object getStream] getDictionary] index:0];
                    [filespec_image_object exportToFile:[[NSString alloc] initWithFormat:@"%@image_x_object.jpg", output_directory]];
                }
            }

            // Associate 2.jpg with form XObject.
            {
                long position = [page getFirstGraphicsObjectPosition:FSGraphicsObjectTypeFormXObject];
                FSFormXObject *form_x_object = [[page getGraphicsObject:position] getFormXObject];
                if (form_x_object != nil) {
                    [associated_files associateFileWithFormXObject:form_x_object file_spec:filespec_jpg];
                    NSLog(@"Associate a jpeg file with form xobject.\n");

                    FSPDFStream *stream = [form_x_object getStream];
                    FSPDFDictionary *dictionary = [stream getDictionary];
                    FSFileSpec *filespec_form_x_object = [associated_files getAssociatedFile:dictionary index:0];
                    [filespec_form_x_object exportToFile:[[NSString alloc] initWithFormat:@"%@form_x_object.jpg", output_directory]];
                }
            }

            // Save PDF file
            [doc saveAs:[output_directory stringByAppendingString:@"associated_files.pdf"] save_flags:FSPDFDocSaveFlagNoOriginal];
        }
        @catch (NSException *e) {
            NSLog(@"Unknown Exception, %@", e);
        }        
    }
    [FSLibrary destroy];
    return 0;
}
