> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/core/get-started/samples/undoredotest.md).

# Undo/Redo PDF Edits - UndoRedoTest

Sample code for using Apryse SDK to take snapshots of edits and move between them using the API. Samples provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

The Apryse SDK has a low-level facility for undo and redo operations. It is a API that applies to any edits made to a particular document (not just annotations). This sample code (provided in Python, C++, C#, Java, Node.js, PHP, Ruby, Go and VB) shows how to use Apryse SDK to walk back and forth on a fully general, bit-exact list of document states. Saving changes in a mode that is not 'incremental' will wipe out the undo-redo state list; the API will not be able to access old snapshots anymore. See the [undoing and redoing guide](/core/pdf-editing/undoredo.md) for more information. Learn more about our [Server SDK](/core/get-started/get-started.md).

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.     
//---------------------------------------------------------------------------------------

using System;
using pdftron;
using pdftron.Common;
using pdftron.PDF;
using pdftron.SDF;

namespace UndoRedoTestCS
{
	/// <summary>
	//---------------------------------------------------------------------------------------
	// The following sample illustrates how to use the UndoRedo API.
	//---------------------------------------------------------------------------------------
	/// </summary>
	class Class1
	{
		private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
		static Class1() {}
		
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		static void Main(string[] args)
		{
			// The first step in every application using PDFNet is to initialize the 
			// library and set the path to common PDF resources. The library is usually 
			// initialized only once, but calling Initialize() multiple times is also fine.
			PDFNet.Initialize(PDFTronLicense.Key);

			// Relative path to the folder containing test files.
			string input_path =  "../../../../TestFiles/";
			string output_path = "../../../../TestFiles/Output/";

			try  
			{
				// Open the PDF document.
				using (PDFDoc doc = new PDFDoc(input_path + "newsletter.pdf"))
				using (ElementBuilder bld = new ElementBuilder())	// Used to build new Element objects
				using (ElementWriter writer = new ElementWriter())	// Used to write Elements to the page	
				{
					UndoManager undo_manager = doc.GetUndoManager();

					// Take a snapshot to which we can undo after making changes.
					ResultSnapshot snap0 = undo_manager.TakeSnapshot();

					DocSnapshot snap0_state = snap0.CurrentState();

					Page page = doc.PageCreate();	// Start a new page

						writer.Begin(page);		// Begin writing to this page

					// ----------------------------------------------------------
					// Add JPEG image to the file
					Image img = Image.Create(doc, input_path + "peppers.jpg");
					Element element = bld.CreateImage(img, new Matrix2D(200, 0, 0, 250, 50, 500));
					writer.WritePlacedElement(element);

					writer.End();	// Finish writing to the page
					doc.PagePushFront(page);

					// Take a snapshot after making changes, so that we can redo later (after undoing first).
					ResultSnapshot snap1 = undo_manager.TakeSnapshot();

					if (snap1.PreviousState().Equals(snap0_state))
					{
						Console.WriteLine("snap1 previous state equals snap0_state; previous state is correct");
					}

					DocSnapshot snap1_state = snap1.CurrentState();

					doc.Save(output_path + "addimage.pdf", SDFDoc.SaveOptions.e_incremental);

					if (undo_manager.CanUndo())
					{
						ResultSnapshot undo_snap = undo_manager.Undo();

						doc.Save(output_path + "addimage_undone.pdf", SDFDoc.SaveOptions.e_incremental);

						DocSnapshot undo_snap_state = undo_snap.CurrentState();

						if (undo_snap_state.Equals(snap0_state))
						{
							Console.WriteLine("undo_snap_state equals snap0_state; undo was successful");
						}

						if (undo_manager.CanRedo())
						{
							ResultSnapshot redo_snap = undo_manager.Redo();
							
							doc.Save(output_path + "addimage_redone.pdf", SDFDoc.SaveOptions.e_incremental);

							if (redo_snap.PreviousState().Equals(undo_snap_state))
							{
								Console.WriteLine("redo_snap previous state equals undo_snap_state; previous state is correct");
							}

							DocSnapshot redo_snap_state = redo_snap.CurrentState();

							if (redo_snap_state.Equals(snap1_state))
							{
								Console.WriteLine("Snap1 and redo_snap are equal; redo was successful");
							}
						}
						else
						{
							Console.WriteLine("Problem encountered - cannot redo.");
						}
					}
					else
					{
						Console.WriteLine("Problem encountered - cannot undo.");
					}
				}
			}
			catch (PDFNetException e) 
			{
				Console.WriteLine(e.Message);
			}
			PDFNet.Terminate();
		}
	}
}
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

#include <PDF/PDFNet.h>
#include <PDF/PDFDoc.h>
#include <string>
#include <iostream>
#include <PDF/Element.h>
#include <PDF/ElementBuilder.h>
#include <PDF/ElementWriter.h>
#include <PDF/Image.h>
#include <Common/Matrix2D.h>
#include "../../LicenseKey/CPP/LicenseKey.h"

using namespace std;
using namespace pdftron;
using namespace PDF;
using namespace SDF;
using namespace Common;

//---------------------------------------------------------------------------------------
// The following sample illustrates how to use the UndoRedo API.
//---------------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
	int ret = 0;
	try 
	{
		// The first step in every application using PDFNet is to initialize the 
		// library and set the path to common PDF resources. The library is usually 
		// initialized only once, but calling Initialize() multiple times is also fine.
		PDFNet::Initialize(LicenseKey);
		
		// Relative path to the folder containing test files.
		string input_path =  "../../TestFiles/";
		string output_path = "../../TestFiles/Output/";
		
		// Open the PDF document.
		PDFDoc doc((input_path + "newsletter.pdf").c_str());

		UndoManager undo_manager = doc.GetUndoManager();

		// Take a snapshot to which we can undo after making changes.
		ResultSnapshot snap0 = undo_manager.TakeSnapshot();

		DocSnapshot snap0_state = snap0.CurrentState();
		
		Page page = doc.PageCreate();	// Start a new page

		ElementBuilder bld;		// Used to build new Element objects
		ElementWriter writer;	// Used to write Elements to the page	
		writer.Begin(page);		// Begin writing to this page

		// ----------------------------------------------------------
		// Add JPEG image to the file
		PDF::Image img = PDF::Image::Create(doc, (input_path + "peppers.jpg").c_str());
		Element element = bld.CreateImage(img, Matrix2D(200, 0, 0, 250, 50, 500));
		writer.WritePlacedElement(element);

		writer.End();	// Finish writing to the page
		doc.PagePushFront(page);

		// Take a snapshot after making changes, so that we can redo later (after undoing first).
		ResultSnapshot snap1 = undo_manager.TakeSnapshot();

		if (snap1.PreviousState().Equals(snap0_state))
		{
			puts("snap1 previous state equals snap0_state; previous state is correct");
		}
		
		DocSnapshot snap1_state = snap1.CurrentState();

		doc.Save((output_path + "addimage.pdf").c_str(), SDFDoc::e_incremental, 0);

		if (undo_manager.CanUndo())
		{
			ResultSnapshot undo_snap = undo_manager.Undo();

			doc.Save((output_path + "addimage_undone.pdf").c_str(), SDFDoc::e_incremental, 0);

			DocSnapshot undo_snap_state = undo_snap.CurrentState();

			if (undo_snap_state.Equals(snap0_state))
			{
				puts("undo_snap_state equals snap0_state; undo was successful");
			}

			if (undo_manager.CanRedo())
			{
				ResultSnapshot redo_snap = undo_manager.Redo();

				doc.Save((output_path + "addimage_redone.pdf").c_str(), SDFDoc::e_incremental, 0);

				if (redo_snap.PreviousState().Equals(undo_snap_state))
				{
					puts("redo_snap previous state equals undo_snap_state; previous state is correct");
				}

				DocSnapshot redo_snap_state = redo_snap.CurrentState();

				if (redo_snap_state.Equals(snap1_state))
				{
					puts("Snap1 and redo_snap are equal; redo was successful");
				}
			}
			else
			{
				puts("Problem encountered - cannot redo.");
				ret = 1;
			}
		}
		else
		{
			puts("Problem encountered - cannot undo.");
			ret = 1;
		}
	}
	catch(Common::Exception& e)	
	{
		cout << e << endl;
		ret = 1;
	}
	catch (...) 
	{
		cout << "Unknown Exception" << endl;
		ret = 1;
	}

	PDFNet::Terminate();

	return ret;	
}
```

{% endcode %}
{% endtab %}

{% tab title="Go" %}
{% code lineNumbers="true" %}

```go
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2021 by PDFTron Systems Inc. All Rights Reserved.
// Consult LICENSE.txt regarding license information.
//---------------------------------------------------------------------------------------

package main
import (
    "fmt"
	. "pdftron"
)

import  "pdftron/Samples/LicenseKey/GO"

// Relative path to the folder containing test files.
var inputPath =  "../../TestFiles/"
var outputPath = "../../TestFiles/Output/"

//---------------------------------------------------------------------------------------
// The following sample illustrates how to use the UndoRedo API.
//---------------------------------------------------------------------------------------

func main(){
    // The first step in every application using PDFNet is to initialize the 
    // library and set the path to common PDF resources. The library is usually 
    // initialized only once, but calling Initialize() multiple times is also fine.
    PDFNetInitialize(PDFTronLicense.Key)
    
    // Open the PDF document.
    doc := NewPDFDoc(inputPath + "newsletter.pdf")

    undoManager := doc.GetUndoManager()

    // Take a snapshot to which we can undo after making changes.
    snap0 := undoManager.TakeSnapshot()

    snap0State := snap0.CurrentState()
    
    // Start a new page
    page := doc.PageCreate()

    bld := NewElementBuilder()          // Used to build new Element objects
    writer := NewElementWriter()        // Used to write Elements to the page
    writer.Begin(page)              // Begin writing to this page

    // ----------------------------------------------------------
    // Add JPEG image to the file
    img := ImageCreate(doc.GetSDFDoc(), inputPath + "peppers.jpg")

    element := bld.CreateImage(img, NewMatrix2D(200.0, 0.0, 0.0, 250.0, 50.0, 500.0))
    writer.WritePlacedElement(element)

    // Finish writing to the page
    writer.End()
    doc.PagePushFront(page)

    // Take a snapshot after making changes, so that we can redo later (after undoing first).
    snap1 := undoManager.TakeSnapshot()

    if snap1.PreviousState().Equals(snap0State){
        fmt.Println("snap1 previous state equals snap0State; previous state is correct")
    }    
    snap1State := snap1.CurrentState()

    doc.Save(outputPath + "addimage.pdf", uint(SDFDocE_incremental))

    if undoManager.CanUndo(){
        undoSnap := undoManager.Undo()

        doc.Save(outputPath + "addimage_undone.pdf", uint(SDFDocE_incremental))

        undoSnapState := undoSnap.CurrentState()

        if undoSnapState.Equals(snap0State){
            fmt.Println("undoSnapState equals snap0State; undo was successful")
        }

        if undoManager.CanRedo(){
            redoSnap := undoManager.Redo()

            doc.Save(outputPath + "addimage_redone.pdf", uint(SDFDocE_incremental))

            if redoSnap.PreviousState().Equals(undoSnapState){
                fmt.Println("redoSnap previous state equals undoSnapState; previous state is correct")
            }

            redoSnapState := redoSnap.CurrentState()
            
            if redoSnapState.Equals(snap1State){
                fmt.Println("Snap1 and redoSnap are equal; redo was successful")
            }
        }else{
            fmt.Println("Problem encountered - cannot redo.")
        }
    }else{
        fmt.Println("Problem encountered - cannot undo.")
    }
    PDFNetTerminate()
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

import com.pdftron.pdf.*;
import com.pdftron.sdf.Obj;
import com.pdftron.common.Matrix2D;
import com.pdftron.common.PDFNetException;
import com.pdftron.sdf.UndoManager;
import com.pdftron.sdf.ResultSnapshot;
import com.pdftron.sdf.DocSnapshot;
import com.pdftron.sdf.SDFDoc;

import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;

//---------------------------------------------------------------------------------------
// The following sample illustrates how to use the UndoRedo API.
//---------------------------------------------------------------------------------------
public class UndoRedoTest 
{
	public static void main(String[] args) 
	{
		try 
		{
			// The first step in every application using PDFNet is to initialize the
			// library and set the path to common PDF resources. The library is usually
			// initialized only once, but calling Initialize() multiple times is also fine.
			PDFNet.initialize(PDFTronLicense.Key());

			// Relative path to the folder containing test files.
			String input_path = "../../TestFiles/";
			String output_path = "../../TestFiles/Output/";

			// Open the PDF document.
			try (PDFDoc doc = new PDFDoc(input_path + "newsletter.pdf")) {

				UndoManager undo_manager = doc.getUndoManager();

				// Take a snapshot to which we can undo after making changes.
				ResultSnapshot snap0 = undo_manager.takeSnapshot();

				DocSnapshot snap0_state = snap0.currentState();
				
				Page page = doc.pageCreate();	// Start a new page

				ElementBuilder bld = new ElementBuilder();		// Used to build new Element objects
				ElementWriter writer = new ElementWriter();		// Used to write Elements to the page	
				writer.begin(page);		// Begin writing to this page

				// ----------------------------------------------------------
				// Add JPEG image to the file
				Image img = Image.create(doc, input_path + "peppers.jpg");
				Element element = bld.createImage(img, new Matrix2D(200, 0, 0, 250, 50, 500));
				writer.writePlacedElement(element);

				writer.end();	// Finish writing to the page
				doc.pagePushFront(page);

				// Take a snapshot after making changes, so that we can redo later (after undoing first).
				ResultSnapshot snap1 = undo_manager.takeSnapshot();

				if (snap1.previousState().equals(snap0_state))
				{
					System.out.println("snap1 previous state equals snap0_state; previous state is correct");
				}
				
				DocSnapshot snap1_state = snap1.currentState();

				doc.save(output_path + "addimage.pdf", SDFDoc.SaveMode.INCREMENTAL, null);

				if (undo_manager.canUndo())
				{
					ResultSnapshot undo_snap;
					undo_snap = undo_manager.undo();

					doc.save(output_path + "addimage_undone.pdf", SDFDoc.SaveMode.INCREMENTAL, null);

					DocSnapshot undo_snap_state = undo_snap.currentState();

					if (undo_snap_state.equals(snap0_state))
					{
						System.out.println("undo_snap_state equals snap0_state; undo was successful");
					}
					
					if (undo_manager.canRedo())
					{
						ResultSnapshot redo_snap = undo_manager.redo();

						doc.save(output_path + "addimage_redone.pdf", SDFDoc.SaveMode.INCREMENTAL, null);

						if (redo_snap.previousState().equals(undo_snap_state))
						{
							System.out.println("redo_snap previous state equals undo_snap_state; previous state is correct");
						}
						
						DocSnapshot redo_snap_state = redo_snap.currentState();
						
						if (redo_snap_state.equals(snap1_state))
						{
							System.out.println("Snap1 and redo_snap are equal; redo was successful");
						}
					}
					else
					{
						System.out.println("Problem encountered - cannot redo.");
					}
				}
				else
				{
					System.out.println("Problem encountered - cannot undo.");
				}
			}

			// Calling Terminate when PDFNet is no longer in use is a good practice, but
			// is not required.
			PDFNet.terminate();
		}
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}
}
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------
// The following sample illustrates how to use the UndoRedo API.
//---------------------------------------------------------------------------------------
const { PDFNet } = require('@pdftron/pdfnet-node');
const PDFTronLicense = require('../LicenseKey/LicenseKey');

((exports) => {

	exports.runUndoRedoTest = () => {

		const main = async () => {
			try {
				// Relative path to the folder containing test files.
				const inputPath = '../TestFiles/';
				const outputPath = inputPath + 'Output/';

				// Open the PDF document.
				const doc = await PDFNet.PDFDoc.createFromFilePath(inputPath + 'newsletter.pdf');

				const undo_manager = await doc.getUndoManager();

				// Take a snapshot to which we can undo after making changes.
				const snap0 = await undo_manager.takeSnapshot();

				const snap0_state = await snap0.currentState();

				const page = await doc.pageCreate();	// Start a new page

				const bld = await PDFNet.ElementBuilder.create();		// Used to build new Element objects
				const writer = await PDFNet.ElementWriter.create();	// Used to write Elements to the page	
				writer.beginOnPage(page);		// Begin writing to this page

				// ----------------------------------------------------------
				// Add JPEG image to the file
				const img = await PDFNet.Image.createFromFile(doc, inputPath + 'peppers.jpg');
				const element = await bld.createImageFromMatrix(img, await PDFNet.Matrix2D.create(200, 0, 0, 250, 50, 500));
				writer.writePlacedElement(element);

				await writer.end();	// Finish writing to the page
				await doc.pagePushFront(page);

				// Take a snapshot after making changes, so that we can redo later (after undoing first).
				const snap1 = await undo_manager.takeSnapshot();

				if (await (await snap1.previousState()).equals(snap0_state)) {
					console.log('snap1 previous state equals snap0_state; previous state is correct');
				}

				const snap1_state = await snap1.currentState();

				await doc.save(outputPath + 'addimage.pdf', PDFNet.SDFDoc.SaveOptions.e_incremental);

				if (await undo_manager.canUndo()) {
					const undo_snap = await undo_manager.undo();

					await doc.save(outputPath + 'addimage_undone.pdf', PDFNet.SDFDoc.SaveOptions.e_incremental);

					const undo_snap_state = await undo_snap.currentState();

					if (await undo_snap_state.equals(snap0_state)) {
						console.log('undo_snap_state equals snap0_state; undo was successful');
					}

					if (await undo_manager.canRedo()) {
						const redo_snap = await undo_manager.redo();

						await doc.save(outputPath + 'addimage_redone.pdf', PDFNet.SDFDoc.SaveOptions.e_incremental);

						if (await (await redo_snap.previousState()).equals(undo_snap_state)) {
							console.log('redo_snap previous state equals undo_snap_state; previous state is correct');
						}

						const redo_snap_state = await redo_snap.currentState();

						if (await redo_snap_state.equals(snap1_state)) {
							console.log('Snap1 and redo_snap are equal; redo was successful');
						}
					}
					else {
						console.log('Problem encountered - cannot redo.');
					}
				}
				else {
					console.log('Problem encountered - cannot undo.');
				}
			} catch (err) {
				console.log(err.stack);
			}
		};

		PDFNet.runWithCleanup(main, PDFTronLicense.Key).catch(function (error) { console.log('Error: ' + JSON.stringify(error)); }).then(function () { return PDFNet.shutdown(); });
	};
	exports.runUndoRedoTest();
})(exports);
// eslint-disable-next-line spaced-comment
//# sourceURL=UndoRedoTest.js
```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
<?php
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
// Consult LICENSE.txt regarding license information.
//---------------------------------------------------------------------------------------
if(file_exists("../../../PDFNetC/Lib/PDFNetPHP.php"))
include("../../../PDFNetC/Lib/PDFNetPHP.php");
include("../../LicenseKey/PHP/LicenseKey.php");

// Relative path to the folder containing the test files.
$input_path = getcwd()."/../../TestFiles/";
$output_path = $input_path."Output/";

//---------------------------------------------------------------------------------------
// The following sample illustrates how to use the UndoRedo API.
//---------------------------------------------------------------------------------------
	
	// The first step in every application using PDFNet is to initialize the 
	// library and set the path to common PDF resources. The library is usually 
	// initialized only once, but calling Initialize() multiple times is also fine.
	PDFNet::Initialize($LicenseKey);
	PDFNet::GetSystemFontList();    // Wait for fonts to be loaded if they haven't already. This is done because PHP can run into errors when shutting down if font loading is still in progress.
	
	// Open the PDF document.
	$doc = new PDFDoc($input_path."newsletter.pdf");
	
	$undo_manager = $doc->GetUndoManager();

	// Take a snapshot to which we can undo after making changes.
	$snap0 = $undo_manager->TakeSnapshot();

	$snap0_state = $snap0->CurrentState();
	
	// Start a new page
	$page = $doc->PageCreate();
	
	$builder = new ElementBuilder();	// Used to build new Element objects
	$writer = new ElementWriter();		// Used to write Elements to the page
	
	$page = $doc->PageCreate();		// Start a new page
	$writer->Begin($page);			// Begin writing to this page
	
	// ----------------------------------------------------------
	// Add JPEG image to the output file
	$img = Image::Create($doc->GetSDFDoc(), $input_path."peppers.jpg");
	
	$element = $builder->CreateImage($img, new Matrix2D(200.0,0.0,0.0,250.0,50.0,500.0));
	$writer->WritePlacedElement($element);
	
	// Finish writing to the page
	$writer->End();
	$doc->PagePushFront($page);
	
	// Take a snapshot after making changes, so that we can redo later (after undoing first).
	$snap1 = $undo_manager->TakeSnapshot();
	
	if ($snap1->PreviousState()->Equals($snap0_state))
	{
		echo(nl2br("snap1 previous state equals snap0_state; previous state is correct\n"));
	}

	$snap1_state = $snap1->CurrentState();

	$doc->Save($output_path."addimage.pdf", SDFDoc::e_incremental);

	if ($undo_manager->CanUndo())
	{
		$undo_snap = $undo_manager->Undo();

		$doc->Save($output_path."addimage_undone.pdf", SDFDoc::e_incremental);

		$undo_snap_state = $undo_snap->CurrentState();

		if ($undo_snap_state->Equals($snap0_state))
		{
			echo(nl2br("undo_snap_state equals snap0_state; undo was successful\n"));
		}
		
		if ($undo_manager->CanRedo())
		{
			$redo_snap = $undo_manager->Redo();

			$doc->Save($output_path."addimage_redone.pdf", SDFDoc::e_incremental);

			if ($redo_snap->PreviousState()->Equals($undo_snap_state))
			{
				echo(nl2br("redo_snap previous state equals undo_snap_state; previous state is correct\n"));
			}
			
			$redo_snap_state = $redo_snap->CurrentState();
			
			if ($redo_snap_state->Equals($snap1_state))
			{
				echo(nl2br("Snap1 and redo_snap are equal; redo was successful\n"));
			}
		}
		else
		{
			echo(nl2br("Problem encountered - cannot redo.\n"));
		}
	}
	else
	{
		echo(nl2br("Problem encountered - cannot undo.\n"));
	}
	PDFNet::Terminate();
?>
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
#---------------------------------------------------------------------------------------
# Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
# Consult LICENSE.txt regarding license information.
#---------------------------------------------------------------------------------------

import site
site.addsitedir("../../../PDFNetC/Lib")
import sys
from PDFNetPython import *

sys.path.append("../../LicenseKey/PYTHON")
from LicenseKey import *

# Relative path to the folder containing test files.
input_path =  "../../TestFiles/"
output_path = "../../TestFiles/Output/"

#---------------------------------------------------------------------------------------
# The following sample illustrates how to use the UndoRedo API.
#---------------------------------------------------------------------------------------

def main():
	# The first step in every application using PDFNet is to initialize the 
	# library and set the path to common PDF resources. The library is usually 
	# initialized only once, but calling Initialize() multiple times is also fine.
	PDFNet.Initialize(LicenseKey)
	
	# Open the PDF document.
	doc = PDFDoc(input_path + "newsletter.pdf")

	undo_manager = doc.GetUndoManager()

	# Take a snapshot to which we can undo after making changes.
	snap0 = undo_manager.TakeSnapshot()

	snap0_state = snap0.CurrentState()
	
	# Start a new page
	page = doc.PageCreate()

	bld = ElementBuilder()			# Used to build new Element objects
	writer = ElementWriter()		# Used to write Elements to the page
	writer.Begin(page)				# Begin writing to this page

	# ----------------------------------------------------------
	# Add JPEG image to the file
	img = Image.Create(doc.GetSDFDoc(), input_path + "peppers.jpg")

	element = bld.CreateImage(img, Matrix2D(200, 0, 0, 250, 50, 500))
	writer.WritePlacedElement(element)

	# Finish writing to the page
	writer.End()
	doc.PagePushFront(page)

	# Take a snapshot after making changes, so that we can redo later (after undoing first).
	snap1 = undo_manager.TakeSnapshot()

	if snap1.PreviousState().Equals(snap0_state):
		print("snap1 previous state equals snap0_state; previous state is correct")
		
	snap1_state = snap1.CurrentState()

	doc.Save(output_path + "addimage.pdf", SDFDoc.e_incremental)

	if undo_manager.CanUndo():
		undo_snap = undo_manager.Undo()

		doc.Save(output_path + "addimage_undone.pdf", SDFDoc.e_incremental)

		undo_snap_state = undo_snap.CurrentState()

		if undo_snap_state.Equals(snap0_state):
			print("undo_snap_state equals snap0_state; undo was successful")
		
		if undo_manager.CanRedo():
			redo_snap = undo_manager.Redo()

			doc.Save(output_path + "addimage_redone.pdf", SDFDoc.e_incremental)

			if redo_snap.PreviousState().Equals(undo_snap_state):
				print("redo_snap previous state equals undo_snap_state; previous state is correct")
			
			redo_snap_state = redo_snap.CurrentState()
			
			if redo_snap_state.Equals(snap1_state):
				print("Snap1 and redo_snap are equal; redo was successful")
		else:
			print("Problem encountered - cannot redo.")
	else:
		print("Problem encountered - cannot undo.")
	PDFNet.Terminate()
	
if __name__ == '__main__':
    main()
```

{% endcode %}
{% endtab %}

{% tab title="Ruby" %}
{% code lineNumbers="true" %}

```ruby
#---------------------------------------------------------------------------------------
# Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
# Consult LICENSE.txt regarding license information.
#---------------------------------------------------------------------------------------

require '../../../PDFNetC/Lib/PDFNetRuby'
include PDFNetRuby
require '../../LicenseKey/RUBY/LicenseKey'

$stdout.sync = true

#---------------------------------------------------------------------------------------
# The following sample illustrates how to use the UndoRedo API.
#---------------------------------------------------------------------------------------

	# The first step in every application using PDFNet is to initialize the 
	# library and set the path to common PDF resources. The library is usually 
	# initialized only once, but calling Initialize multiple times is also fine.
	PDFNet.Initialize(PDFTronLicense.Key)

	# Relative path to the folder containing test files.
	input_path = "../../TestFiles/"
	output_path = "../../TestFiles/Output/"

	# Open the PDF document.
	doc = PDFDoc.new(input_path + "newsletter.pdf")
	
	undo_manager = doc.GetUndoManager()

	# Take a snapshot to which we can undo after making changes.
	snap0 = undo_manager.TakeSnapshot()

	snap0_state = snap0.CurrentState()
	
	# Start a new page
	page = doc.PageCreate()

	builder = ElementBuilder.new()			# Used to build new Element objects
	writer = ElementWriter.new()			# Used to write Elements to the page
	writer.Begin(page)						# Begin writing to this page

	# ----------------------------------------------------------
	# Add JPEG image to the output file
	img = Image.Create(doc.GetSDFDoc(), input_path + "peppers.jpg")
	
	element = builder.CreateImage(img, Matrix2D.new(200, 0, 0, 250, 50, 500))
	writer.WritePlacedElement(element)
	
	# Finish writing to the page
	writer.End()    
	doc.PagePushFront(page)
	
	# Take a snapshot after making changes, so that we can redo later (after undoing first).
	snap1 = undo_manager.TakeSnapshot()
	
	if snap1.PreviousState().Equals(snap0_state)
		puts "snap1 previous state equals snap0_state; previous state is correct"
	end
	
	snap1_state = snap1.CurrentState()

	doc.Save(output_path + "addimage.pdf", SDFDoc::E_incremental)

	if undo_manager.CanUndo()
		undo_snap = undo_manager.Undo()

		doc.Save(output_path + "addimage_undone.pdf", SDFDoc::E_incremental)

		undo_snap_state = undo_snap.CurrentState()

		if undo_snap_state.Equals(snap0_state)
			puts "undo_snap_state equals snap0_state; undo was successful"
		end

		if undo_manager.CanRedo()
			redo_snap = undo_manager.Redo()

			doc.Save(output_path + "addimage_redone.pdf", SDFDoc::E_incremental)

			if redo_snap.PreviousState().Equals(undo_snap_state)
				puts "redo_snap previous state equals undo_snap_state; previous state is correct"
			end
			
			redo_snap_state = redo_snap.CurrentState()
			
			if redo_snap_state.Equals(snap1_state)
				puts "Snap1 and redo_snap are equal; redo was successful"
			end
		else
			puts "Problem encountered - cannot redo."
		end
	else
		puts "Problem encountered - cannot undo."
	end
	PDFNet.Terminate
```

{% endcode %}
{% endtab %}

{% tab title="VB" %}
{% code lineNumbers="true" %}

```vb
'---------------------------------------------------------------------------------------
' Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
' Consult legal.txt regarding legal and license information.     
'---------------------------------------------------------------------------------------
Imports System
Imports pdftron
Imports pdftron.Common
Imports pdftron.PDF
Imports pdftron.SDF

Module UndoRedoTestVB
	Dim pdfNetLoader As PDFNetLoader
	Sub New()
		pdfNetLoader = pdftron.PDFNetLoader.Instance()
	End Sub

	
	Sub Main()
		' The first step in every application using PDFNet is to initialize the 
		' library and set the path to common PDF resources. The library is usually 
		' initialized only once, but calling Initialize() multiple times is also fine.
		PDFNet.Initialize(PDFTronLicense.Key)
		
		' Relative path to the folder containing test files.
		Dim input_path As String = "../../../../TestFiles/"
		Dim output_path As String = "../../../../TestFiles/Output/"

		Try
			' Open the PDF document.
			Using doc As PDFDoc = New PDFDoc(input_path & "newsletter.pdf")
				Using bld As ElementBuilder = New ElementBuilder()       ' Used to build new Element objects
					Using writer As ElementWriter = New ElementWriter()      ' Used to write Elements to the page
						Dim undo_manager As UndoManager = doc.GetUndoManager()
						' Take a snapshot to which we can undo after making changes.
						Dim snap0 As ResultSnapshot = undo_manager.TakeSnapshot()
						Dim snap0_state As DocSnapshot = snap0.CurrentState()
						Dim page As Page = doc.PageCreate()	' Start a new page
						writer.Begin(page)		' Begin writing to this page
						' ----------------------------------------------------------
						' Add JPEG image to the file
						Dim img As Image = Image.Create(doc, input_path & "peppers.jpg")
						Dim element As Element = bld.CreateImage(img, New Matrix2D(200, 0, 0, 250, 50, 500))
						writer.WritePlacedElement(element)
						writer.End()	' Finish writing to the page
						doc.PagePushFront(page)
						
						' Take a snapshot after making changes, so that we can redo later (after undoing first).
						Dim snap1 As ResultSnapshot = undo_manager.TakeSnapshot()
						If snap1.PreviousState().Equals(snap0_state) Then
							Console.WriteLine("snap1 previous state equals snap0_state; previous state is correct")
						End If

						Dim snap1_state As DocSnapshot = snap1.CurrentState()
						doc.Save(output_path & "addimage.pdf", SDFDoc.SaveOptions.e_incremental)
						
						If undo_manager.CanUndo() Then
							Dim undo_snap As ResultSnapshot = undo_manager.Undo()
							doc.Save(output_path & "addimage_undone.pdf", SDFDoc.SaveOptions.e_incremental)
							
							Dim undo_snap_state As DocSnapshot = undo_snap.CurrentState()
							If undo_snap_state.Equals(snap0_state) Then
								Console.WriteLine("undo_snap_state equals snap0_state; undo was successful")
							End If

							If undo_manager.CanRedo() Then
								Dim redo_snap As ResultSnapshot = undo_manager.Redo()
								doc.Save(output_path & "addimage_redone.pdf", SDFDoc.SaveOptions.e_incremental)

								If redo_snap.PreviousState().Equals(undo_snap_state) Then
									Console.WriteLine("redo_snap previous state equals undo_snap_state; previous state is correct")
								End If

								Dim redo_snap_state As DocSnapshot = redo_snap.CurrentState()

								If redo_snap_state.Equals(snap1_state) Then
									Console.WriteLine("Snap1 and redo_snap are equal; redo was successful")
								End If
							Else
								Console.WriteLine("Problem encountered - cannot undo.")
							End If
						Else
							Console.WriteLine("Problem encountered - cannot undo.")
						End If
					End Using
				End Using
			End Using

		Catch e As PDFNetException
			Console.WriteLine(e.Message)
		End Try
		PDFNet.Terminate()
	End Sub
End Module
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/core/get-started/samples/undoredotest.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
