'
' Copyright (c) 2001-2025 by Apryse Software Inc. All Rights Reserved.
'

Imports System

Imports pdftron
Imports pdftron.Common
Imports pdftron.Filters
Imports pdftron.SDF
Imports PDFTRON.PDF
Imports PDFTRON.PDF.OCG


'-----------------------------------------------------------------------------------
' This sample demonstrates how to create layers in PDF.
' The sample also shows how to extract and render PDF layers in documents 
' that contain optional content groups (OCGs)
'
' With the introduction of PDF version 1.5 came the concept of Layers. 
' Layers, or as they are more formally known Optional Content Groups (OCGs),
' refer to sections of content in a PDF document that can be selectively 
' viewed or hidden by document authors or consumers. This capability is useful 
' in CAD drawings, layered artwork, maps, multi-language documents etc.
'
' Notes: 
' ---------------------------------------
' - This sample is using CreateLayer() utility method to create new OCGs. 
'   CreateLayer() is relatively basic, however it can be extended to set 
'   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
'   a complete listing of possible entries in OC dictionary please refer to 
'   section 4.10 'Optional Content' in the PDF Reference Manual.
' - The sample is grouping all layer content into separate Form XObjects. 
'   Although using PDFNet is is also possible to specify Optional Content in 
'   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
'   XObjects results in PDFs that are cleaner, less-error prone, and faster 
'   to process.
'-----------------------------------------------------------------------------------

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

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

	Sub Main()

		PDFNet.Initialize(PDFTronLicense.Key)

		Try
			Using doc As PDFDoc = New PDFDoc
				' Create three layers...
				Dim image_layer As Group = CreateLayer(doc, "Image Layer")
				Dim text_layer As Group = CreateLayer(doc, "Text Layer")
				Dim vector_layer As Group = CreateLayer(doc, "Vector Layer")

				' Start a new page ------------------------------------
				Dim page As Page = doc.PageCreate()

				Using builder As ElementBuilder = New ElementBuilder			' ElementBuilder is used to build new Element objects
					Using writer As ElementWriter = New ElementWriter		  ' ElementWriter is used to write Elements to the page	
						writer.Begin(page)			 ' begin writing to this page           

						' Add new content to the page and associate it with one of the layers.
						Dim element As Element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj()))
						writer.WriteElement(element)

						element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj()))
						writer.WriteElement(element)

						element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj()))
						writer.WriteElement(element)

						' Add some content to the page that does not belong to any layer...
						' In this case this is a rectangle representing the page border.
						element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight())
						element.SetPathFill(False)
						element.SetPathStroke(True)
						element.GetGState().SetLineWidth(40)
						writer.WriteElement(element)

						writer.End()			  ' save changes to the current page
						doc.PagePushBack(page)

						doc.Save(output_path + "pdf_layers.pdf", SDF.SDFDoc.SaveOptions.e_linearized)

					End Using
				End Using

			End Using
			Console.WriteLine("Done.")

		Catch ex As PDFNetException
			Console.WriteLine(ex.Message)
		Catch ex As Exception
			MsgBox(ex.Message)
		End Try


		' The following is a code snippet shows how to selectively render 
		' and export PDF layers.
		Try
			Using doc As PDFDoc = New PDFDoc(output_path + "pdf_layers.pdf")
				doc.InitSecurityHandler()

				If doc.HasOC() = False Then
					Console.WriteLine("The document does not contain 'Optional Content'")
				Else
					Dim init_cfg As Config = doc.GetOCGConfig()
					Dim ctx As Context = New Context(init_cfg)

					Using pdfdraw As PDFDraw = New PDFDraw
						pdfdraw.SetImageSize(1000, 1000)
						pdfdraw.SetOCGContext(ctx)			 ' Render the page using the given OCG context.

						Dim page As Page = doc.GetPage(1)			 ' Get the first page in the document.
						pdfdraw.Export(page, output_path + "pdf_layers_default.png")

						' Disable drawing of content that is not optional (i.e. is not part of any layer).
						ctx.SetNonOCDrawing(False)

						' Now render each layer in the input document to a separate image.
						Dim ocgs As Obj = doc.GetOCGs()			 ' Get the array of all OCGs in the document.
						If Not ocgs Is Nothing Then
							Dim i As Integer = 0
							Dim sz As Integer = ocgs.Size()
							While (i < sz)
								Dim ocg As Group = New Group(ocgs.GetAt(i))
								ctx.ResetStates(False)
								ctx.SetState(ocg, True)
								Dim fname As String = "pdf_layers_" + ocg.GetName() + ".png"
								Console.WriteLine(fname)
								pdfdraw.Export(page, output_path + fname)
								i = i + 1
							End While
						End If

						' Now draw content that is not part of any layer...
						ctx.SetNonOCDrawing(True)
						ctx.SetOCDrawMode(Context.OCDrawMode.e_NoOC)
						pdfdraw.Export(page, output_path + "pdf_layers_non_oc.png")
					End Using
					Console.WriteLine("Done.")
				End If
			End Using
		Catch ex As PDFNetException
			Console.WriteLine(ex.Message)
		Catch ex As Exception
			MsgBox(ex.Message)
		End Try
		PDFNet.Terminate()
	End Sub

	' A utility function used to add new Content Groups (Layers) to the document.
	Function CreateLayer(ByRef doc As PDFDoc, ByVal layer_name As String) As Group

		Dim grp As Group = Group.Create(doc, layer_name)
		Dim cfg As Config = doc.GetOCGConfig()
		If cfg Is Nothing Then
			cfg = Config.Create(doc, True)
			cfg.SetName("Default")
		End If

		' Add the new OCG to the list of layers that should appear in PDF viewer GUI.
		Dim layer_order_array As Obj = cfg.GetOrder()
		If layer_order_array Is Nothing Then
			layer_order_array = doc.CreateIndirectArray()
			cfg.SetOrder(layer_order_array)
		End If
		layer_order_array.PushBack(grp.GetSDFObj())

		Return grp
	End Function

	' Creates some content (3 images) and associate them with the image layer
	Function CreateGroup1(ByRef doc As PDFDoc, ByRef layer As Obj) As Obj

		Using writer As ElementWriter = New ElementWriter
			writer.Begin(doc.GetSDFDoc())

			' Create an Image that can be reused in the document or on the same page.		
			Dim img As Image = Image.Create(doc.GetSDFDoc(), (input_path + "peppers.jpg"))

			Using builder As ElementBuilder = New ElementBuilder

				Dim element As Element = builder.CreateImage(img, New Matrix2D(img.GetImageWidth() / 2, -145, 20, img.GetImageHeight() / 2, 200, 150))
				writer.WritePlacedElement(element)

				Dim gstate As GState = element.GetGState()		  ' use the same image (just change its matrix)
				gstate.SetTransform(200, 0, 0, 300, 50, 450)
				writer.WritePlacedElement(element)

				' use the same image again (just change its matrix).
				writer.WritePlacedElement(builder.CreateImage(img, 300, 600, 200, -150))

				Dim grp_obj As Obj = writer.End()

				' Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype", "Form")
				grp_obj.Put("OC", layer)
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000)		' Set the clip box for the content.

				Return grp_obj
			End Using
		End Using
	End Function

	' Creates some content (a path in the shape of a heart) and associate it with the vector layer
	Function CreateGroup2(ByRef doc As PDFDoc, ByRef layer As Obj) As Obj

		Using writer As ElementWriter = New ElementWriter
			writer.Begin(doc.GetSDFDoc())

			Using builder As ElementBuilder = New ElementBuilder
				' Create a path object in the shape of a heart.
				builder.PathBegin()		' start constructing the path
				builder.MoveTo(306, 396)
				builder.CurveTo(681, 771, 399.75, 864.75, 306, 771)
				builder.CurveTo(212.25, 864.75, -69, 771, 306, 396)
				builder.ClosePath()
				Dim element As Element = builder.PathEnd()		  ' the path geometry is now specified.

				' Set the path FILL color space and color.
				element.SetPathFill(True)
				Dim gstate As GState = element.GetGState()
				gstate.SetFillColorSpace(ColorSpace.CreateDeviceCMYK())
				gstate.SetFillColor(New ColorPt(1, 0, 0, 0))		' cyan

				' Set the path STROKE color space and color.
				element.SetPathStroke(True)
				gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB())
				gstate.SetStrokeColor(New ColorPt(1, 0, 0))		' red
				gstate.SetLineWidth(20)

				gstate.SetTransform(0.5, 0, 0, 0.5, 280, 300)

				writer.WriteElement(element)

				Dim grp_obj As Obj = writer.End()

				' Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype", "Form")
				grp_obj.Put("OC", layer)
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000)		' Set the clip box for the content.

				Return grp_obj
			End Using
		End Using
	End Function

	' Creates some text and associate it with the text layer
	Function CreateGroup3(ByRef doc As PDFDoc, ByRef layer As Obj) As Obj

		Using writer As ElementWriter = New ElementWriter
			writer.Begin(doc.GetSDFDoc())

			Using builder As ElementBuilder = New ElementBuilder

				' Begin writing a block of text
				Dim element As Element = builder.CreateTextBegin(Font.Create(doc, Font.StandardType1Font.e_times_roman), 120)
				writer.WriteElement(element)

				element = builder.CreateTextRun("A text layer!")

				' Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
				Dim transform As Matrix2D = Matrix2D.RotationMatrix(-45 * (3.1415 / 180.0))
				transform.Concat(1, 0, 0, 1, 180, 100)
				element.SetTextMatrix(transform)

				writer.WriteElement(element)
				writer.WriteElement(builder.CreateTextEnd())

				Dim grp_obj As Obj = writer.End()

				' Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype", "Form")
				grp_obj.Put("OC", layer)
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000)		' Set the clip box for the content.

				Return grp_obj
			End Using
		End Using
	End Function

End Module
