Overview
In this guide, I will detail the process of generating a PDF document by converting HTML content on SAP Cloud Platform Integration (CPI). The use case involves the following steps:
Fetch Terminated Employee Data: Retrieve data for terminated employees from SAP SuccessFactors.Use a Document Template: Leverage an HTML document template built in SuccessFactors.Replace Placeholders: Populate the template with dynamic data specific to each employee.Generate a PDF File: Convert the populated HTML to a PDF document.Send PDF via Email: Attach the generated PDF to an email and send it to the terminated employee.
Considerations for PDF Generation
After researching available libraries, two primary solutions were identified:
iText’s Html2Pdf:Some licensing limitations.Preferred for cases requiring advanced formatting and multilingual text (e.g., English and Arabic).Reference: SpringHow – iText PDF.OpenHtml2Pdf:Complex implementation.No apparent licensing limitations.Reference: OpenHtml2Pdf Integration Guide.
Given the need to support bilingual text (English and Arabic) with specific fonts, iText’s Html2Pdf was selected for this implementation. I will present an implementation with OpenHtml2Pdf on a second article.
Data Requirements
The HTML template is populated using the following dynamic fields:
Employee Name (English and Arabic)Nationality (English and Arabic)Employee IDJob Title (English and Arabic)Job Start and End DatesCompany Name (English and Arabic)Company Logo, Footer, and StampEmployee’s Personal Email Address
Fetching Employee Data
Data is retrieved from the EmpEmploymentTermination entity in SuccessFactors using a GET request. Key paths include:
Field
Path
Full Name (English)
/EmpEmploymentTermination/personNav/PerPerson/personalInfoNav/PerPersonal/firstName
Full Name (Arabic)
/EmpEmploymentTermination/personNav/PerPerson/personalInfoNav/PerPersonal/firstNameAlt1
Nationality
has a custom path
Nationality Arabic
has a custom path
Employee ID
/EmpEmploymentTermination/userId
Job Title (English)
/EmpEmploymentTermination/jobInfoNav/EmpJob/positionNav/Position/externalName_en_GB
Job Title (Arabic)
/EmpEmploymentTermination/jobInfoNav/EmpJob/positionNav/Position/externalName_ar_SA
Personal Email
/EmpEmploymentTermination/EmpEmploymentTermination/personNav/PerPerson/emailNav/PerEmail – emailAddress and filter by emailType (check the corresponding optionId to the Personal type on object definition)
Job Start Date
/EmpEmploymentTermination/EmpEmploymentTermination/jobInfoNav/EmpJob/hireDate
Job End Date
/EmpEmploymentTermination/EmpEmploymentTermination/endDate
Company Name (English)
/EmpEmploymentTermination/EmpEmploymentTermination/jobInfoNav/EmpJob/companyNav/FOCompany/name_en_GB
Company Name (Arabic)
/EmpEmploymentTermination/EmpEmploymentTermination/jobInfoNav/EmpJob/companyNav/FOCompany/name_ar_SA
Company Code
/EmpEmploymentTermination/EmpEmploymentTermination/jobInfoNav/EmpJob/companyNav/FOCompany/externalCode
Note: Ensure that filters for termination codes and dates are properly defined to fetch relevant records. The query should include parameters such as TerminationCode, CurrentRunDate, and PreviousRunDate.
Implementation in SAP CPI
The process is divided into three main components:
1. Main Process
Timer Configuration:Scheduled based on client preferences. For manual runs, use the “Run Once” option.Define Parameters:
Set external parameters such as personIdExternal, TerminationCode, and PreviousRunDate_Ext for filtering and query building. Query Builder:Using script to determine the “PreviousRunDate” value, used for a delta logic.personIdExternal externalized parameter used for extra optional personIdExternal filter, built in the script.
import com.sap.gateway.ip.core.customdev.util.Message
import java.util.HashMap;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
def Message buildQuery(Message message) {
prop = message.getProperties();
head = message.getHeaders();
StringBuffer debugLog = new StringBuffer();
String modDate=””;
Date today = new Date();
DateFormat dateFormat= new SimpleDateFormat(“yyyy-MM-dd’T’HH:mm:ssZ”);
String todayInString = dateFormat.format(today);
String personIdExternal = prop.get(‘personIdExternal’);
// use fixed RunDate as the initial delta timestamp
if(!(prop.get(“PreviousRunDate_Ext”).trim().isEmpty()))
{
modDate = prop.get(“PreviousRunDate_Ext”);
message.setProperty(“PreviousRunDatePersisted_Value”,modDate);
}
// use previously persisted RunDate as the initial delta timestamp
else if(!(prop.get(“PreviousRunDate”).trim().isEmpty()))
{
modDate = prop.get(“PreviousRunDate”);
message.setProperty(“PreviousRunDatePersisted_Value”,modDate);
}
// use the CurrentDate as the initial delta timestamp
else
{
modDate=todayInString;
message.setProperty(“PreviousRunDatePersisted_Value”,modDate);
}
message.setProperty(“PreviousRunDate”, modDate);
// create extra query parameter for a fixed employee id
if(!(personIdExternal.trim().isEmpty())){
message.setProperty(“personIdExternalQuery”, ” and personIdExternal eq ‘”+personIdExternal + “‘”);
}else{
message.setProperty(“personIdExternalQuery”, “”);
}
return message;}
Get Terminated EmployeesSplitterSplit by XPath expression “/EmpEmploymentTermination/ EmpEmploymentTermination/”Process each employee record individually
2. Get Terminated Employees
Use query builder to choose the needed fields (mentioned previously)Use “Custom Query Options” to define filters such as:
$filter=jobInfoNav/eventNav/externalCode eq ‘${property.TerminationCode}’ and lastModifiedOn le datetime’${property.CurrentRunDate}’ and lastModifiedOn ge datetime’${property.PreviousRunDate}’ ${property.personIdExternalQuery}
Ensure proper handling of delta requests to fetch updated records.
3. Handle Each Employee
Message Mapping:Transform data into a simple XML structure for debugging and processing.
Assign specific logos, footers, and stamps to each company code using a Value Mapping.
Build PDF Text:Populate the HTML template with employee-specific details.Ensure correct URL paths for logos and stamps.
PDF Generation
import com.sap.gateway.ip.core.customdev.util.Message
import org.apache.camel.impl.DefaultAttachment
import javax.mail.util.ByteArrayDataSource
import javax.activation.DataHandler
import javax.activation.DataSource
import javax.activation.FileDataSource
import javax.mail.util.ByteArrayDataSource
import java.io.ByteArrayOutputStream
import com.itextpdf.html2pdf.HtmlConverter
import com.itextpdf.html2pdf.ConverterProperties
import com.itextpdf.layout.font.FontProvider
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider
def Message convertHtmlToPdf(Message message) {
// Get HTML Body
String htmlContent = message.getBody(String.class);
OutputStream pdfOutput = new ByteArrayOutputStream();
FontProvider fontProvider = new DefaultFontProvider(true, true, true);
ConverterProperties properties = new ConverterProperties();
properties.setCharset(“UTF-8”);
properties.setFontProvider(fontProvider);
// Convert from HTML to pdf (in byte array format)
HtmlConverter.convertToPdf(htmlContent, pdfOutput, properties);
// Close the OutputStream if it’s no longer needed
pdfOutput.close()
// Convert OutputStream to byte array
byte[] pdfBytes = pdfOutput.toByteArray()
// Create a DataSource from the byte array
DataSource dataSource = new ByteArrayDataSource(pdfBytes, “application/pdf”)
// Create a DataHandler from the DataSource
DataHandler dataHandler = new DataHandler(dataSource)
// Construct a DefaultAttachment object
def attachment = new DefaultAttachment(dataHandler);
// Attach the document
message.addAttachmentObject(“Attachment.pdf”, attachment);
return message;
}
HTML to PDF Conversion:Use OutputStream to write data to a destination.Use HTML Converter to convert our HTML content to pdf.Specify properties such as FontProvider to keep the correct format of the document.Construct a DefaultAttachment object using the ByteArrayDataSource fed with the pdfOutput’s byte array. This object encapsulates the attachment data and is ready to be added to the email with addAttachmentObject();
Adding External Libraries
Before executing the iFlow, it is essential to include the required external libraries in the project references. The primary library for HTML-to-PDF conversion, such as html2pdf, has several dependencies that must also be included.
These libraries can be downloaded from Maven repositories, such as:
Maven Repository for iText PDF.
Important Considerations:
While the html2pdf library is available for free, its usage is subject to specific licensing conditions. Ensure that you review and comply with these terms before implementation.OpenHtml2Pdf requires a more complex implementation, but it may be a good choice due to no apparent licensing limitations.
Final Notes
While this implementation meets the requirements, it is not an ideal architectural solution. A more robust approach would involve:
Using SAP CPI for data retrieval and HTML preparation.Delegating PDF generation and email dispatch to a custom-built API.
This approach reduces dependency on external libraries and aligns with best practices in enterprise architecture.
Ensure compliance with licensing terms for any third-party libraries used.
OverviewIn this guide, I will detail the process of generating a PDF document by converting HTML content on SAP Cloud Platform Integration (CPI). The use case involves the following steps:Fetch Terminated Employee Data: Retrieve data for terminated employees from SAP SuccessFactors.Use a Document Template: Leverage an HTML document template built in SuccessFactors.Replace Placeholders: Populate the template with dynamic data specific to each employee.Generate a PDF File: Convert the populated HTML to a PDF document.Send PDF via Email: Attach the generated PDF to an email and send it to the terminated employee.Considerations for PDF GenerationAfter researching available libraries, two primary solutions were identified:iText’s Html2Pdf:Some licensing limitations.Preferred for cases requiring advanced formatting and multilingual text (e.g., English and Arabic).Reference: SpringHow – iText PDF.OpenHtml2Pdf:Complex implementation.No apparent licensing limitations.Reference: OpenHtml2Pdf Integration Guide.Given the need to support bilingual text (English and Arabic) with specific fonts, iText’s Html2Pdf was selected for this implementation. I will present an implementation with OpenHtml2Pdf on a second article.Data RequirementsThe HTML template is populated using the following dynamic fields:Employee Name (English and Arabic)Nationality (English and Arabic)Employee IDJob Title (English and Arabic)Job Start and End DatesCompany Name (English and Arabic)Company Logo, Footer, and StampEmployee’s Personal Email AddressFetching Employee DataData is retrieved from the EmpEmploymentTermination entity in SuccessFactors using a GET request. Key paths include:FieldPathFull Name (English)/EmpEmploymentTermination/personNav/PerPerson/personalInfoNav/PerPersonal/firstNameFull Name (Arabic)/EmpEmploymentTermination/personNav/PerPerson/personalInfoNav/PerPersonal/firstNameAlt1Nationalityhas a custom pathNationality Arabichas a custom pathEmployee ID/EmpEmploymentTermination/userIdJob Title (English)/EmpEmploymentTermination/jobInfoNav/EmpJob/positionNav/Position/externalName_en_GBJob Title (Arabic)/EmpEmploymentTermination/jobInfoNav/EmpJob/positionNav/Position/externalName_ar_SAPersonal Email/EmpEmploymentTermination/EmpEmploymentTermination/personNav/PerPerson/emailNav/PerEmail – emailAddress and filter by emailType (check the corresponding optionId to the Personal type on object definition)Job Start Date/EmpEmploymentTermination/EmpEmploymentTermination/jobInfoNav/EmpJob/hireDateJob End Date/EmpEmploymentTermination/EmpEmploymentTermination/endDateCompany Name (English)/EmpEmploymentTermination/EmpEmploymentTermination/jobInfoNav/EmpJob/companyNav/FOCompany/name_en_GB Company Name (Arabic)/EmpEmploymentTermination/EmpEmploymentTermination/jobInfoNav/EmpJob/companyNav/FOCompany/name_ar_SACompany Code/EmpEmploymentTermination/EmpEmploymentTermination/jobInfoNav/EmpJob/companyNav/FOCompany/externalCodeNote: Ensure that filters for termination codes and dates are properly defined to fetch relevant records. The query should include parameters such as TerminationCode, CurrentRunDate, and PreviousRunDate. Implementation in SAP CPIThe process is divided into three main components:1. Main ProcessMain ProcessTimer Configuration:Scheduled based on client preferences. For manual runs, use the “Run Once” option.Define Parameters:Define Initial ParametersSet external parameters such as personIdExternal, TerminationCode, and PreviousRunDate_Ext for filtering and query building. Query Builder:Using script to determine the “PreviousRunDate” value, used for a delta logic.personIdExternal externalized parameter used for extra optional personIdExternal filter, built in the script. import com.sap.gateway.ip.core.customdev.util.Message
import java.util.HashMap;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
def Message buildQuery(Message message) {
prop = message.getProperties();
head = message.getHeaders();
StringBuffer debugLog = new StringBuffer();
String modDate=””;
Date today = new Date();
DateFormat dateFormat= new SimpleDateFormat(“yyyy-MM-dd’T’HH:mm:ssZ”);
String todayInString = dateFormat.format(today);
String personIdExternal = prop.get(‘personIdExternal’);
// use fixed RunDate as the initial delta timestamp
if(!(prop.get(“PreviousRunDate_Ext”).trim().isEmpty()))
{
modDate = prop.get(“PreviousRunDate_Ext”);
message.setProperty(“PreviousRunDatePersisted_Value”,modDate);
}
// use previously persisted RunDate as the initial delta timestamp
else if(!(prop.get(“PreviousRunDate”).trim().isEmpty()))
{
modDate = prop.get(“PreviousRunDate”);
message.setProperty(“PreviousRunDatePersisted_Value”,modDate);
}
// use the CurrentDate as the initial delta timestamp
else
{
modDate=todayInString;
message.setProperty(“PreviousRunDatePersisted_Value”,modDate);
}
message.setProperty(“PreviousRunDate”, modDate);
// create extra query parameter for a fixed employee id
if(!(personIdExternal.trim().isEmpty())){
message.setProperty(“personIdExternalQuery”, ” and personIdExternal eq ‘”+personIdExternal + “‘”);
}else{
message.setProperty(“personIdExternalQuery”, “”);
}
return message;} Get Terminated EmployeesSplitterSplit by XPath expression “/EmpEmploymentTermination/ EmpEmploymentTermination/”Process each employee record individually2. Get Terminated EmployeesGet Terminated EmployeesUse query builder to choose the needed fields (mentioned previously)Use “Custom Query Options” to define filters such as:$filter=jobInfoNav/eventNav/externalCode eq ‘${property.TerminationCode}’ and lastModifiedOn le datetime’${property.CurrentRunDate}’ and lastModifiedOn ge datetime’${property.PreviousRunDate}’ ${property.personIdExternalQuery}Ensure proper handling of delta requests to fetch updated records.3. Handle Each EmployeeHandle Each EmployeeMessage Mapping:Transform data into a simple XML structure for debugging and processing.Message Mapping to Simple StructureAssign specific logos, footers, and stamps to each company code using a Value Mapping.Value Mapping operation in Message Mapping Value Mapping from Company CodeBuild PDF Text:Populate the HTML template with employee-specific details.Ensure correct URL paths for logos and stamps. PDF Generation import com.sap.gateway.ip.core.customdev.util.Message
import org.apache.camel.impl.DefaultAttachment
import javax.mail.util.ByteArrayDataSource
import javax.activation.DataHandler
import javax.activation.DataSource
import javax.activation.FileDataSource
import javax.mail.util.ByteArrayDataSource
import java.io.ByteArrayOutputStream
import com.itextpdf.html2pdf.HtmlConverter
import com.itextpdf.html2pdf.ConverterProperties
import com.itextpdf.layout.font.FontProvider
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider
def Message convertHtmlToPdf(Message message) {
// Get HTML Body
String htmlContent = message.getBody(String.class);
OutputStream pdfOutput = new ByteArrayOutputStream();
FontProvider fontProvider = new DefaultFontProvider(true, true, true);
ConverterProperties properties = new ConverterProperties();
properties.setCharset(“UTF-8”);
properties.setFontProvider(fontProvider);
// Convert from HTML to pdf (in byte array format)
HtmlConverter.convertToPdf(htmlContent, pdfOutput, properties);
// Close the OutputStream if it’s no longer needed
pdfOutput.close()
// Convert OutputStream to byte array
byte[] pdfBytes = pdfOutput.toByteArray()
// Create a DataSource from the byte array
DataSource dataSource = new ByteArrayDataSource(pdfBytes, “application/pdf”)
// Create a DataHandler from the DataSource
DataHandler dataHandler = new DataHandler(dataSource)
// Construct a DefaultAttachment object
def attachment = new DefaultAttachment(dataHandler);
// Attach the document
message.addAttachmentObject(“Attachment.pdf”, attachment);
return message;
} HTML to PDF Conversion:Use OutputStream to write data to a destination.Use HTML Converter to convert our HTML content to pdf.Specify properties such as FontProvider to keep the correct format of the document.Construct a DefaultAttachment object using the ByteArrayDataSource fed with the pdfOutput’s byte array. This object encapsulates the attachment data and is ready to be added to the email with addAttachmentObject();Adding External LibrariesBefore executing the iFlow, it is essential to include the required external libraries in the project references. The primary library for HTML-to-PDF conversion, such as html2pdf, has several dependencies that must also be included.These libraries can be downloaded from Maven repositories, such as:Maven Repository for iText PDF. Important Considerations:While the html2pdf library is available for free, its usage is subject to specific licensing conditions. Ensure that you review and comply with these terms before implementation.OpenHtml2Pdf requires a more complex implementation, but it may be a good choice due to no apparent licensing limitations. Final NotesWhile this implementation meets the requirements, it is not an ideal architectural solution. A more robust approach would involve:Using SAP CPI for data retrieval and HTML preparation.Delegating PDF generation and email dispatch to a custom-built API.This approach reduces dependency on external libraries and aligns with best practices in enterprise architecture.Ensure compliance with licensing terms for any third-party libraries used. Read More Technology Blogs by SAP articles
#SAP
#SAPTechnologyblog