Example (Java)

The whole project may be fetched as a zip or  tape archive.

The code in the listing ViewThenSignAPdfWithPad may be used to view
and then to sign a PDF document with a StepOver signature pad such as the duraSign Pad Brilliance.
It uses code from the listing Common below. A resulting signed document is attached here.

ViewThenSignAPdfWithPad.java
package com.so.net.example;

import static com.so.net.DeviceApi.*;
import com.so.net.SignApi;
import static com.so.net.SignApi.Behaviour.*;
import static com.so.net.SignApi.Pdf.*;
import static com.so.net.example.Common.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public class ViewThenSignAPdfWithPad {

	public static void main(String[] args) throws Exception {
		// This requires
		// .NET5 with hostfxr,
		// libgdiplus on Linux and MacOS,
		// the PDF service,
		// and the native SignAPI.
		SignApi.load();

		// To connect to a pad on the remote computer "ThinClient"
		// Communication.useTCP(java.net.InetAddress.getByName("ThinClient"), 8888);
		// Search for signature device.
		final List<Device.Name> devices = enumerateDevices(Filter.StepOver);
		if (devices.isEmpty()) {
			System.out.println("Exit because no device was found.");
			return;
		}

		// Select the first device.
		Device.Name firstDevice = devices.get(0);
		setDevice(firstDevice);
		
		// See https://www.stepoverinfo.net/confluence/display/NETDEVAPI/Driver+XML+certificate
		setTheDriverXmlCertificate(Paths.get("DriverXmlCertificate.xml"));

		// Loads a PDF document from the file system.
		byte[] theDocument = Files.readAllBytes(Paths.get("TheDocument.pdf"));
		loadPdf(theDocument);

		// View the document on the pad
		SignApi.viewDocument(0, Zoom.Level.FitWidth, true);
		// wait until the start-signing-button is clicked
		// with a timeout of 10 minutes.
		waitForStartSignButton(600);

		// Performs an example signature with hardcoded
		// signature field position, signer info and signing behaviour.
		exampleSign();
		
		// If the device shall no longer show the document.
		// setCustomerLogoMode();
		
		// Download and save the signed document.
		byte[] signedDocument = downloadPdf();
		Files.write(Paths.get("SignedDocument.pdf"), signedDocument);

		// Frees unmanaged resources.
		close();
	}
}

The code in the listing ViewAPdfWithPad displays document pages and prints button clicks.

ViewAPdfWithPad
package com.so.net.example;

import static com.so.net.DeviceApi.*;
import com.so.net.SignApi;
import static com.so.net.SignApi.Pdf.*;
import static com.so.net.example.Common.*;
import static java.lang.Math.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;

public class ViewAPdfWithPad {

	public static void main(String[] args) throws Exception {
		// This requires
		// .NET5 with hostfxr,
		// libgdiplus on Linux and MacOS,
		// the PDF service,
		// and the native SignAPI.
		SignApi.load();

		final List<Device.Name> devices = enumerateDevices(Filter.StepOver);
		if (devices.isEmpty()) {
			System.out.println("Exit because no device was found.");
			return;
		}

		// Select the first device.
		Device.Name firstDevice = devices.get(0);
		setDevice(firstDevice);

		// Loads a PDF document from the file system.
		byte[] theDocument = Files.readAllBytes(Paths.get("TheDocument.pdf"));
		loadPdf(theDocument);

		// View the document on the pad
		// wait and print button clicks
		// and stop if the start signature button was clicked
		// or stop if no button is clicked for more than 10 minutes.
		showAndEnableAllButtonsInTheDocViewMode();
		printButtonClicksInDocViewMode(600, 0);

		// Frees unmanaged resources.
		close();
	}

	static void printButtonClicksInDocViewMode(int timeout, int page) {
		int numberOfPages = getNumberOfPages().orElse(0);

		final int pageInRange = max(0, min(numberOfPages - 1, page));
		renderPage(pageInRange, 96)
			.ifPresent(pageImage -> DocumentViewing.start(pageImage, 1 + pageInRange, numberOfPages));

		Optional<Button.Kind> buttonKind;
		try {
			buttonKind = waitForButton(timeout);
		} catch (InterruptedException ex) {
			return;
		}

		if (buttonKind.isPresent()) {
			Button.Kind kind = buttonKind.get();

			printButton(kind);
			int nextPage = pageInRange;
			if (Button.Kind.Next == kind) {
				nextPage++;
			} else if (Button.Kind.Prev == kind) {
				nextPage--;
			} else if (Button.Kind.StartSignature == kind) {
				System.out.println("Good bye.");
				return;
			}

			printButtonClicksInDocViewMode(timeout, nextPage);
		}
	}

	static void printButton(Button.Kind buttonKind) {
		System.out.println("Button " + buttonKind.toString() + " was clicked.");
	}
}

Common functions.

Common.java with exampleSign.
package com.so.net.example;

import com.so.net.DeviceApi;
import com.so.net.DeviceApi.*;
import com.so.net.SignApi;
import static com.so.net.SignApi.Pdf.*;
import java.awt.Color;
import java.util.Optional;
import static com.so.net.SignApi.*;
import com.so.net.SignApi.Behaviour;
import static java.lang.Math.*;
import java.nio.file.Path;

public class Common {

	static class ClickedButton {

		Optional<Button.Kind> kind;
	}

	static Optional<Button.Kind> waitForButton(int timeoutSeconds) throws InterruptedException {
		ClickedButton obj = new ClickedButton();
		DeviceApi.Button.setCallback(e -> {
			obj.kind = Optional.of(e.kind);
			notify(obj);
		});
		wait(obj, timeoutSeconds);
		return obj.kind;
	}

	static void waitForStartSignButton(int timeoutSeconds) throws InterruptedException {
		Object obj = new Object();
		DeviceApi.Button.setCallback(e -> {
			if (DeviceApi.Button.Kind.StartSignature == e.kind) {
				notify(obj);
			}
		});
		wait(obj, timeoutSeconds);
	}

	static void wait(Object obj, int timeoutSeconds) throws InterruptedException {
		synchronized (obj) {
			obj.wait(timeoutSeconds * 1000);
		}
	}

	static void notify(Object obj) {
		synchronized (obj) {
			obj.notifyAll();
		}
	}

	static boolean exampleSign() {
		Behaviour behaviour = Behaviour.Builder.getDefault()
			.signInDocument(Behaviour.DocSign.Rectangle.Builder
				// The rectangle should be at least 40mm wide and 30mm high.
				// and place the signature rectangle on the device display
				// with a distance of 10mm to the lower and the left display edge.
				.fromBottomLeft(10, 10, 40, 30)
				// The document shall be shown on the pad while signing
				.withColor(Color.LIGHT_GRAY)
				// The rectangle shall be highlighted by a red border
				.withBorderColor(Color.RED)
				// which shall be 5 dots wide.
				.withBorderWidth(5))
			.build().get();

		// Choose a field name, field position and signature info.
		FieldName fieldName = FieldName.of(null);

		// Choose the position of the new signature field.
		int page = 0;
		FieldPosition pageDim = getPageDimensions().get(page);
		// The default length unit is inch/72.
		double width = 4 * 72; //4 inches wide
		double height = 2 * 72; //2 inches high
		double row0 = max(pageDim.getHeight() / 2 - height / 2, 0);
		double column0 = max(pageDim.getWidth() / 2 - width / 2, 0);
		double row1 = min(row0 + height / 2, pageDim.getHeight());
		double column1 = min(column0 + width / 2, pageDim.getWidth());
		FieldPosition fieldPosition = FieldPosition.of(row0, column0, row1, column1, page);

		// Adds a signature field with the given field name and position.
		addSignatureField(fieldName, fieldPosition);

		// This queries for the signature field just added.
		SignatureField signatureField = getSignatureField(fieldName).get();

		// A signature field can have these entries.
		// They are not mandatory. Pass an empty string or null to skip a field.
		final String name = "Signer's name.";
		final String reason = "Reason for signing.";
		final String location = "The location.";
		final String contactInfo = "The contact info.";
		SignatureInfo signatureInfo = SignatureInfo.of(name, reason, location, contactInfo);

		// Signs this signature field with the selected pad.
		return sign(signatureField, signatureInfo, behaviour);
	}

	static Optional<SignApi.Pdf.SignatureField> getSignatureField(SignApi.Pdf.FieldName fieldName) {
		return getAcroFormFields().stream()
			.filter(field -> fieldName.equals(field.getFieldName()))
			.filter(field -> field instanceof SignApi.Pdf.SignatureField)
			.map(field -> (SignApi.Pdf.SignatureField) field)
			.findFirst();
	}

	static void setTheDriverXmlCertificate(Path certPath) {
		boolean fileExists = certPath.toFile().exists();
		if (!fileExists) {
			System.out.println(certPath.toAbsolutePath().toString() + " does not exist.");
		}
		boolean success = DeviceApi.DriverXmlCertificate.set(certPath);
		if (!success) {
			System.out.println("Failed to set the driver XML certificate.");
		}
	}

	static void showAndEnableAllButtonsInTheDocViewMode() {
		// The device can show document pages in the DocView mode.
		DeviceApi.Button.Bar.get(DeviceApi.Button.Bar.Mode.DocView)
			.ifPresent(bar -> {
				bar.configs.forEach(Common::showAndEnable);
				bar.set();
			});
	}

	static void showAndEnable(Button.Config buttonConfig) {
		buttonConfig.setEnabled(true);
		buttonConfig.setVisible(true);
	}
}

The POM of the example project.

Example pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.so</groupId>
	<artifactId>net-example</artifactId>
	<version>0.0.2</version>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>8</maven.compiler.source>
		<maven.compiler.target>8</maven.compiler.target>
	</properties>
	
	<repositories>
		<!-- https://www.stepoverinfo.net/confluence/display/NETSIGNAPI/Install+with+Maven --> 
		<repository>
			<id>nexus-stepover-maven-public</id>
			<url>https://nexus.stepover.de:8043/repository/maven-public/</url>
		</repository>
	</repositories>
	
	<dependencies>
		<dependency>
			<groupId>com.so</groupId>
			<artifactId>net</artifactId>
			<version>1.0.0</version>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>	
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<configuration>
					<descriptorRefs>
						<descriptorRef>jar-with-dependencies</descriptorRef>
					</descriptorRefs>
					<archive>
						<manifest>
							<mainClass>com.so.net.example.ViewThenSignAPdfWithPad</mainClass>
						</manifest>
					</archive>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>