// 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 render all PDF layers of a PDF,
// and add layers in a PDF document.


#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";

NSString *output_directory = [output_path stringByAppendingString:@"pdflayer/"];

void renderPageToImage(FSPDFPage* page, FSLayerContext* layer_content, NSString* save_bitmap_path) {
    if (![page isParsed]) {
        // Parse page.
        [page startParse:FSPDFPageParsePageNormal pause:nil is_reparse:NO];
    }
    
    int width = [page getWidth];
    int height = [page getHeight];
    FSMatrix2D* matrix = [page getDisplayMatrix:0 top:0 width:width height:height rotate:[page getRotation]];
    
    // Prepare a bitmap for rendering.
    FSBitmap* bitmap = [[FSBitmap alloc] initWithWidth:width height:height format:FSBitmapDIBArgb buffer:nil pitch:0];
    [bitmap fillRect:0xFFFFFFFF rect:nil];
    
    // Render page.
    FSRenderer* render = [[FSRenderer alloc] initWithBitmap:bitmap is_rgb_order:NO];
    
    if (![layer_content isEmpty]) {
        [render setLayerContext:layer_content];
    }
    [render startRender:page matrix:matrix pause:nil];;
    FSImage* image = [FSImage new];
    [image addFrame:bitmap];
    [image saveAs:save_bitmap_path];
}

NSString* usgaeCodeToString(FSLayerTreeUsageState state) {
    switch (state) {
        case FSLayerTreeStateON:
            return @"ON";
        case FSLayerTreeStateOFF:
            return @"OFF";
        case FSLayerTreeStateUnchanged:
            return @"Unchanged";
        case FSLayerTreeStateUndefined:
            return @"Undefined";
    }
    return @"Unknown";
}

NSString* generateStringWithMultipleTab(int count) {
    NSString* tabs_string = @"";
    for (int i=0; i<count; i++) {
        tabs_string = [tabs_string stringByAppendingString:@"\t"];
    }
    return tabs_string;
}

void getAllLayerNodesInformation(FSLayerNode* layer_node, int depth, NSFileHandle* txt_file_handle) {
    NSString* record_content = [NSString new];
    if (depth >= 0) {
        record_content = [NSString stringWithFormat:@"%@%@", generateStringWithMultipleTab(depth), [layer_node getName]];
        
        if ([layer_node hasLayer]) {
            FSLayerTreeUsageState state = [layer_node getViewUsage];
            
            NSString* temp_content = [NSString stringWithFormat:@" %@\r\n", (state == FSLayerTreeStateON) ? @"[*]" : @"[ ]"];
            record_content = [record_content stringByAppendingString:temp_content];
            
            temp_content = [NSString stringWithFormat:@"%@View usage state:\t%@\r\n",
            generateStringWithMultipleTab(depth), usgaeCodeToString(state)];
            record_content = [record_content stringByAppendingString:temp_content];
            
            temp_content = [NSString stringWithFormat:@"%@Export usage state:\t%@\r\n",
                              generateStringWithMultipleTab(depth), usgaeCodeToString([layer_node getExportUsage])];
            record_content = [record_content stringByAppendingString:temp_content];
            
            FSLayerPrintData* print_data = [layer_node getPrintUsage];
            temp_content = [NSString stringWithFormat:@"%@Print usage state:\t%@, subtype: %@\r\n",
                              generateStringWithMultipleTab(depth), usgaeCodeToString(print_data.print_state), print_data.subtype];
            record_content = [record_content stringByAppendingString:temp_content];
            
            FSLayerZoomData* zoom_data = [layer_node getZoomUsage];
            temp_content = [NSString stringWithFormat:@"%@Zoom usage:\tmin_factor = %.4f max_factor = %.4f\r\n\r\n", generateStringWithMultipleTab(depth), zoom_data.min_factor, zoom_data.max_factor];
            record_content = [record_content stringByAppendingString:temp_content];
        } else {
            record_content = [record_content stringByAppendingString:@"\r\n"];
        }
    }
    NSData* content_data = [record_content dataUsingEncoding:NSUTF8StringEncoding];
    [txt_file_handle writeData:content_data];
    depth++;
    int count = [layer_node getChildrenCount];
    for (int i = 0; i < count; i++) {
        FSLayerNode* child = [layer_node getChild:i];
        getAllLayerNodesInformation(child, depth, txt_file_handle);
    }
}

void setAllLayerNodesInformation(FSLayerNode* layer_node) {
    if ([layer_node hasLayer]) {
        [layer_node setDefaultVisible:YES];
        [layer_node setExportUsage:FSLayerTreeStateUndefined];
        [layer_node setViewUsage:FSLayerTreeStateOFF];
        FSLayerPrintData* print_data = [[FSLayerPrintData alloc] initWithSubtype:@"subtype_print" print_state:FSLayerTreeStateON];
        [layer_node setPrintUsage:print_data];
        FSLayerZoomData* zoom_data = [[FSLayerZoomData alloc] initWithMin_factor:1 max_factor:10];
        [layer_node setZoomUsage:zoom_data];
        NSString* new_name = [NSString stringWithFormat:@"%@%@",@"[View_OFF_Print_ON_Export_Undefined]", [layer_node getName]];
        [layer_node setName:new_name];
    }
    int count = [layer_node getChildrenCount];
    for (int i = 0; i < count; i++) {
        FSLayerNode* child = [layer_node getChild:i];
        setAllLayerNodesInformation(child);
    }
}

void setAllLayerNodesVisible(FSLayerContext* context, FSLayerNode* layer_node, bool visible) {
    if ([layer_node hasLayer]) {
        [context setVisible:layer_node is_visible:NO];
    }
    int count = [layer_node getChildrenCount];
    for (int i = 0; i < count; i++) {
        FSLayerNode* child = [layer_node getChild:i];
        setAllLayerNodesVisible(context, child, visible);
    }
}

void renderAllLayerNodes(FSPDFDoc* doc, FSLayerContext* context, FSLayerNode* layer_node) {
    if ([layer_node hasLayer]) {
        [context setVisible:layer_node is_visible:YES];
        int page_count = [doc getPageCount];
        for (int i=0; i<page_count; i++) {
            FSPDFPage* page = [doc getPage:i];
            
            NSString* layer_name = [layer_node getName];
            layer_name = [layer_name stringByReplacingOccurrencesOfString:@":" withString:@""];
            layer_name = [layer_name stringByReplacingOccurrencesOfString:@">" withString:@""];
            layer_name = [layer_name stringByReplacingOccurrencesOfString:@"<" withString:@""];
            layer_name = [layer_name stringByReplacingOccurrencesOfString:@"=" withString:@""];
            NSString* file_name = [NSString stringWithFormat:@"%@page_%d_layer_node_%@.bmp", output_directory, i, layer_name];
            renderPageToImage(page, context, file_name);
        }
    }
    
    int count = [layer_node getChildrenCount];
    for (int i = 0; i < count; i++) {
        FSLayerNode* child = [layer_node getChild:i];
        renderAllLayerNodes(doc,context,child);
    }
    if ([layer_node hasLayer]) {
        // The visibility of parent layer will affect the children layers,
        // so reset the visibility of parent layer after children have been rendered.
        [context setVisible:layer_node is_visible:NO];
    }
}

int main(int argc, const char * argv[]) {
	bool is_run_success = false;
	@autoreleasepool {
        
        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 {
            NSString* input_pdf_name = @"OCTest_src.pdf";
            NSString* input_pdf_path = [input_path stringByAppendingPathComponent:input_pdf_name];
            FSPDFDoc* doc = [[FSPDFDoc alloc] initWithPath:input_pdf_path];
            FSErrorCode errorCode = [doc load:nil];
            if (errorCode != FSErrSuccess) {
                NSLog(@"The Doc [%@] Error: %ld", input_pdf_path, errorCode);
                return -1;
            }
            FSLayerTree* layertree = [[FSLayerTree alloc] initWithDocument:doc];
            FSLayerNode* root = [layertree getRootNode];
            if ([root isEmpty]) {
                NSLog(@"No layer information!\r\n");
                return -1;
            }
            
            // Get original information of all layer nodes.
            NSString* save_txt_path = [output_directory stringByAppendingPathComponent:@"original_layer_informations.txt"];
            NSFileManager* file_manager = [NSFileManager defaultManager];
            if (![file_manager createFileAtPath:save_txt_path contents:nil attributes:nil]) {
                NSLog(@"Fail to create txt file %@\r\n", save_txt_path);
                return -1;
            }
            NSFileHandle* file_handle = [NSFileHandle fileHandleForWritingAtPath:save_txt_path];
            getAllLayerNodesInformation(root, -1, file_handle);
            [file_handle closeFile];
            
            // Set new information.
            setAllLayerNodesInformation(root);
            
            // Get new information.
            save_txt_path = [output_directory stringByAppendingPathComponent:@"new_layer_informations.txt"];
            if (![file_manager createFileAtPath:save_txt_path contents:nil attributes:nil]) {
                NSLog(@"Fail to create txt file %@\r\n", save_txt_path);
                return -1;
            }
            file_handle = [NSFileHandle fileHandleForWritingAtPath:save_txt_path];
            getAllLayerNodesInformation(root, -1, file_handle);
            [file_handle closeFile];
            
            NSString* save_pdf_path = [output_directory stringByAppendingPathComponent:@"new_layers.pdf"];
            [doc saveAs:save_pdf_path save_flags:FSPDFDocSaveFlagNormal];
            // Render layer node.
            FSLayerContext* layer_context = [[FSLayerContext alloc] initWithDocument:doc usage_type:FSLayerContextUsageView];
            setAllLayerNodesVisible(layer_context, root, false);
            
            renderAllLayerNodes(doc, layer_context, root);

            // Edit layer tree
            {
                FSPDFDoc* doc = [[FSPDFDoc alloc] initWithPath:input_pdf_path];
                FSErrorCode errorCode = [doc load:nil];
                if (errorCode != FSErrSuccess) {
                    NSLog(@"The Doc [%@] Error: %ld", input_pdf_path, errorCode);
                    return -1;
                }
                FSLayerTree* layertree = [[FSLayerTree alloc] initWithDocument:doc];
                FSLayerNode* root = [layertree getRootNode];
                if ([root isEmpty]) {
                    NSLog(@"No layer information!\r\n");
                    return -1;
                }
                int children_count = [root getChildrenCount];
                [root removeChild:children_count-1];
                FSLayerNode* child = [root getChild:children_count-2];
                FSLayerNode* child0 = [root getChild:0];
                [child moveTo:child0 index:0];
                [child addChild:0 name:@"AddedLayerNode" has_Layer:YES];
                [child addChild:0 name:@"AddedNode" has_Layer:NO];
				save_txt_path = [output_directory stringByAppendingPathComponent:@"edit_layer_informations.txt"];
                if (![file_manager createFileAtPath:save_txt_path contents:nil attributes:nil]) {
                    NSLog(@"Fail to create txt file %@\r\n", save_txt_path);
                    return -1;
                }
                file_handle = [NSFileHandle fileHandleForWritingAtPath:save_txt_path];
                getAllLayerNodesInformation(root, -1, file_handle);
                [file_handle closeFile];
                
                NSString* output_file = [output_directory stringByAppendingPathComponent:@"edit_layer_tree.pdf"];
                [doc saveAs:output_file save_flags:FSPDFDocSaveFlagNormal];
            }
            
            // Create layer tree
            {
                NSString* input_pdf_path = [input_path stringByAppendingPathComponent:@"AboutFoxit.pdf"];
                FSPDFDoc* doc = [[FSPDFDoc alloc] initWithPath:input_pdf_path];
                FSErrorCode errorCode = [doc load:nil];
                if (errorCode != FSErrSuccess) {
                    NSLog(@"The Doc [%@] Error: %ld", input_pdf_path, errorCode);
                    return -1;
                }
                BOOL has_layer = [doc hasLayer];
                FSLayerTree* layertree = [[FSLayerTree alloc] initWithDocument:doc];
                has_layer = [doc hasLayer];
                FSLayerNode* root = [layertree getRootNode];
                if ([root isEmpty]) {
                    NSLog(@"No layer information!\r\n");
                    return -1;
                }
                int children_count = [root getChildrenCount];
                [root addChild:0 name:@"AddedLayerNode" has_Layer:YES];
                [root addChild:0 name:@"AddedNode" has_Layer:NO];
				save_txt_path = [output_directory stringByAppendingPathComponent:@"create_layer_informations.txt"];
                if (![file_manager createFileAtPath:save_txt_path contents:nil attributes:nil]) {
                    NSLog(@"Fail to create txt file %@\r\n", save_txt_path);
                    return -1;
                }
                file_handle = [NSFileHandle fileHandleForWritingAtPath:save_txt_path];
                getAllLayerNodesInformation(root, -1, file_handle);
                [file_handle closeFile];
                
                NSString* output_file = [output_directory stringByAppendingPathComponent:@"create_layer_tree.pdf"];
                [doc saveAs:output_file save_flags:FSPDFDocSaveFlagNormal];
            }

            //copy graphics objects from A PDF to B PDF.
            {
                FSPDFDoc* doc = [[FSPDFDoc alloc] initWithPath:input_pdf_path];
                FSErrorCode errorCode = [doc load:nil];
                if(errorCode != FSErrSuccess){
                    NSLog(@"The Doc [%@] Error: %ld", input_pdf_path, errorCode);
                    return -1;
                }
                FSPDFPage* page = [doc getPage:0];
                [page startParse:0 pause:nil is_reparse:false];
                NSString* input_new_path = [input_path stringByAppendingPathComponent:@"SamplePDF.pdf"];
                FSPDFDoc* newdoc = [[FSPDFDoc alloc] initWithPath:input_new_path];
                errorCode = [newdoc load:nil];
                if(errorCode != FSErrSuccess) {
                    NSLog(@"The Doc [%@] Error: %ld", input_new_path, errorCode);
                    return -1;
                }
                FSPDFPage* newpage = [newdoc getPage:0];
                [newpage startParse:0 pause:nil is_reparse:false];
                BOOL has_layer = [newdoc hasLayer];
                FSLayerTree* layertree = [[FSLayerTree alloc] initWithDocument:newdoc];
                has_layer = [newdoc hasLayer];
                FSLayerNode* root = [layertree getRootNode];
                if ([root isEmpty]) {
                    NSLog(@"No layer information!\r\n");
                    return -1;
                }
                FSLayerNode* child = [root addChild:0 name:@"AddedLayerNode" has_Layer:YES];
                
                for(int i = 0; i < 5; i++)
                {
                    FSGraphicsObject* graphics_object = [page getGraphicsObjectByIndex:i];
                    FSGraphicsObject* ngraphics_object = [graphics_object clone];
                    [newpage insertGraphicsObject:0 graphics_object:ngraphics_object];
                    [child addGraphicsObject:newpage graphicsobject:ngraphics_object];
                }
                [newpage generateContent];

                NSString* output_file = [output_directory stringByAppendingPathComponent:@"copy_graphics_objects.pdf"];
                [newdoc saveAs:output_file save_flags:FSPDFDocSaveFlagNormal];
            }

            NSLog(@"PDFLayer test..");
            is_run_success = true;
		}
		@catch (NSException *e) {
			NSLog(@"Unknown Exception, %@", e);
		}
	}
	[FSLibrary destroy];
    return is_run_success? 0 : 1;
}

