com.adobe.platform.operation.pdfops.CreatePDFOperation Maven / Gradle / Ivy
/*
* Copyright 2019 Adobe
* All Rights Reserved.
*
* NOTICE: Adobe permits you to use, modify, and distribute this file in
* accordance with the terms of the Adobe license agreement accompanying
* it. If you have received this file from a source other than Adobe,
* then your use, modification, or distribution of it requires the prior
* written permission of Adobe.
*/
package com.adobe.platform.operation.pdfops;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.adobe.platform.operation.ExecutionContext;
import com.adobe.platform.operation.internal.MediaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.platform.operation.Operation;
import com.adobe.platform.operation.exception.ServiceApiException;
import com.adobe.platform.operation.exception.ServiceUsageException;
import com.adobe.platform.operation.internal.ExtensionMediaTypeMapping;
import com.adobe.platform.operation.internal.FileRefImpl;
import com.adobe.platform.operation.internal.InternalExecutionContext;
import com.adobe.platform.operation.internal.cpf.dto.response.ContentAnalyzerResponse;
import com.adobe.platform.operation.internal.service.CreatePDFService;
import com.adobe.platform.operation.internal.api.FileDownloadApi;
import com.adobe.platform.operation.internal.exception.OperationException;
import com.adobe.platform.operation.internal.util.FileUtil;
import com.adobe.platform.operation.internal.util.PathUtil;
import com.adobe.platform.operation.internal.util.StringUtil;
import com.adobe.platform.operation.internal.util.ValidationUtil;
import com.adobe.platform.operation.io.FileRef;
import com.adobe.platform.operation.pdfops.options.createpdf.CreatePDFFromHTMLOptions;
import com.adobe.platform.operation.pdfops.options.createpdf.CreatePDFOptions;
/**
* An operation that converts a non-PDF file to a PDF file. Some source formats may have associated conversion parameters
* which can be set in the {@link CreatePDFOperation#setOptions(CreatePDFOptions)} method.
*
* The supported source media types are listed here. The {@link SupportedSourceFormat} enum can be used to map file
* extensions to their corresponding media types when creating FileRef instances for the source files.
*
* - application/msword
* - application/vnd.ms-excel
* - application/vnd.ms-powerpoint
* - application/vnd.openxmlformats-officedocument.presentationml.presentation
* - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
* - application/vnd.openxmlformats-officedocument.wordprocessingml.document
* - application/zip (see "Special handling for HTML inputs" below)
* - image/bmp
* - image/gif
* - image/jpeg
* - image/png
* - image/tiff
* - text/plain
* - text/rtf
*
*
*
* Sample Usage:
*
{@code CreatePDFOperation createPdfOperation = CreatePDFOperation.createNew();
* createPdfOperation.setInput(FileRef.createFromLocalFile("~/Documents/createPdfInput.docx",
* CreatePDFOperation.SupportedSourceFormat.DOCX.getMediaType()));
* Credentials credentials = Credentials.serviceAccountCredentialsBuilder().fromFile("pdftools-api-credentials.json").build();
* FileRef result = createPdfOperation.execute(ExecutionContext.create(credentials));
* result.saveAs("output/CreatePDFOutput.pdf");
* }
*
* Special handling for HTML inputs:
*
* An HTML input can only be provided as a local zip archive.
* When creating the corresponding FileRef instance, the media type must be "application/zip" for the local zip archive.
* Zip archive must have the following structure:
*
* - The main HTML file must be named "index.html".
* - "index.html" must exist at the top level of zip archive, not in a folder.
*
*
* Sample layout:
* html_files.zip
* |__index.html
* |__referenced_file_1.css
* |__referenced_file_2.jpeg
* |__subfolder_1
* |_____referenced_file_3.jpeg
*
*/
public class CreatePDFOperation implements Operation {
private static final Logger LOGGER = LoggerFactory.getLogger(CreatePDFOperation.class);
/**
* Supported media types for this operation
*/
private static final Set SUPPORTED_SOURCE_MEDIA_TYPES = Arrays.stream(SupportedSourceFormat.values())
.map(SupportedSourceFormat::getMediaType).collect(Collectors.toSet());
/**
* Field representing the extension of the operation result
*/
private static final String TARGET_FILE_EXTENSION = ExtensionMediaTypeMapping.PDF.getExtension();
/**
* Map which maintains the options instance type of different input media types.
*/
private static final Map mediaTypeOptionClassMap = new HashMap<>();
static {
mediaTypeOptionClassMap.put(ExtensionMediaTypeMapping.ZIP, CreatePDFFromHTMLOptions.class);
mediaTypeOptionClassMap.put(ExtensionMediaTypeMapping.HTML, CreatePDFFromHTMLOptions.class);
}
/**
* Variable to check if the operation instance was invoked more than once
*/
private boolean isInvoked = false;
private FileRefImpl sourceFileRef;
private CreatePDFOptions createPDFOptions;
private CreatePDFOperation() {
}
/**
* Constructs a {@code CreatePDFOperation} instance.
*
* @return a new {@code CreatePDFOperation} instance
*/
public static CreatePDFOperation createNew() {
return new CreatePDFOperation();
}
/**
* Sets an input file.
*
* @param sourceFileRef an input file
*/
public void setInput(FileRef sourceFileRef) {
Objects.requireNonNull(sourceFileRef, "No input was set for operation");
this.sourceFileRef = (FileRefImpl) sourceFileRef;
}
/**
* Sets the conversion parameters for this operation. See {@link CreatePDFOptions} for how to specify the
* conversion parameters for the different source media types.
*
* @param createPdfOptions conversion parameters; use null for default values
*/
public void setOptions(CreatePDFOptions createPdfOptions) {
this.createPDFOptions = createPdfOptions;
}
/**
* Executes this operation synchronously using the supplied context and returns a new FileRef instance for the resulting PDF file.
*
* The resulting file may be stored in the system temporary directory (per java.io.tmpdir System property).
* See {@link FileRef} for how temporary resources are cleaned up.
*
* @param context the context in which to execute the operation
* @return the resulting PDF file
* @throws ServiceApiException if an API call results in an error response
* @throws IOException if there is an error in reading either the input source or the resulting PDF file
* @throws ServiceUsageException if service usage limits have been reached or credentials quota has been exhausted.
*/
public FileRef execute(ExecutionContext context) throws ServiceApiException, IOException, ServiceUsageException {
validateInvocationCount();
InternalExecutionContext internalExecutionContext = (InternalExecutionContext) context;
this.validate(internalExecutionContext);
try {
LOGGER.info("All validations successfully done. Beginning CreatePDF operation execution");
long startTimeMs = System.currentTimeMillis();
String location = CreatePDFService.convertToPdf(internalExecutionContext,
sourceFileRef, createPDFOptions, this.getClass().getSimpleName());
String targetFileName = FileUtil.getRandomFileName(TARGET_FILE_EXTENSION);
String temporaryDestinationPath = PathUtil.getTemporaryDestinationPath(targetFileName, TARGET_FILE_EXTENSION);
FileDownloadApi.downloadAndSave(internalExecutionContext, location, temporaryDestinationPath, ContentAnalyzerResponse.class);
LOGGER.info("Operation successfully completed. Stored created PDF at {}", temporaryDestinationPath);
LOGGER.debug("Operation Success Info - Request ID: {}, Latency(ms): {}",
StringUtil.getRequestIdFromLocation(location), System.currentTimeMillis() - startTimeMs);
isInvoked = true;
return FileRef.createFromLocalFile(temporaryDestinationPath);
} catch (OperationException oe) {
throw new ServiceApiException(oe.getErrorMessage(), oe.getRequestTrackingId(), oe.getStatusCode());
}
}
private void validateInvocationCount() {
if (isInvoked) {
LOGGER.error("Operation instance must only be invoked once");
throw new IllegalStateException("Operation instance must not be reused, can only be invoked once");
}
}
private void validate(InternalExecutionContext context) {
if (sourceFileRef == null) {
throw new IllegalArgumentException("No input was set for operation");
}
ValidationUtil.validateExecutionContext(context);
ValidationUtil.validateMediaType(SUPPORTED_SOURCE_MEDIA_TYPES, this.sourceFileRef.getMediaType());
if (createPDFOptions != null) {
ValidationUtil.validateOptionInstanceType(mediaTypeOptionClassMap, sourceFileRef.getMediaType(), createPDFOptions);
ValidationUtil.validateOperationOptions(createPDFOptions);
}
}
/**
* Supported source file formats for {@link CreatePDFOperation}.
*/
public enum SupportedSourceFormat implements MediaType {
/**
* Represents "image/bmp" media type
*/
BMP,
/**
* Represents "application/msword" media type
*/
DOC,
/**
* Represents "application/vnd.openxmlformats-officedocument.wordprocessingml.document" media type
*/
DOCX,
/**
* Represents "image/gif" media type
*/
GIF,
/**
* Represents "image/jpeg" media type
*/
JPEG,
/**
* Represents "image/jpeg" media type
*/
JPG,
/**
* Represents "image/png" media type
*/
PNG,
/**
* Represents "application/vnd.ms-powerpoint" media type
*/
PPT,
/**
* Represents "application/vnd.openxmlformats-officedocument.presentationml.presentation" media type
*/
PPTX,
/**
* Represents "text/rtf" media type
*/
RTF,
/**
* Represents "image/tiff" media type
*/
TIF,
/**
* Represents "image/tiff" media type
*/
TIFF,
/**
* Represents "text/plain" media type
*/
TXT,
/**
* Represents "application/vnd.ms-excel" media type
*/
XLS,
/**
* Represents "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" media type
*/
XLSX,
/**
* Represents "application/zip" media type
*/
ZIP;
/**
* Returns the corresponding media type for this format, intended to be used for {@code mediaType} parameter in
* {@link FileRef} methods.
*
* @return the corresponding media type
*/
public String getMediaType() {
return ExtensionMediaTypeMapping.valueOf(name()).getMediaType().toLowerCase();
}
}
}