Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Sharadha1
Active Contributor
14,399
Recently we had a requirement for generating invoice PDFs from a cloud application. We had the entire application deployed in the cloud and did not want to use the Adobe Document Services(ADS) deployed in the  on-premise Netweaver system to generate the PDF forms.

We tried the Adobe service offered in SAP Cloud platform - 'SAP Forms by Adobe'. Though the official document covers most of the steps, it does not provide an end-to-end solution. This blog will cover all the steps we performed to implement the solution in the trial landscape.

You can use the SAP Forms by Adobe in two different scenarios

  1. From an application running on an ABAP or Java backend

  2. From an application through the SAP Forms by Adobe REST API via HTTP


The use case in question falls under the second scenario. In order to call the Rest API to generate the PDF forms, we need the following:

a. Form Template - PDF Layout

b. Data to be displayed in the form - Invoice data.

Pass these two parameters while calling the Rest API and get the PDF form back. I have used a simple Java application deployed in the SAP Cloud Platform to call the Adobe forms API.

Some steps are well documented here by SAP. So, I will be covering the additional steps in a more detailed manner.

  1. Enable the 'SAP Forms by Adobe' in your trial subaccount. Refer link

  2. Assign roles to users and update the destination. Refer link

  3. Before we can start with the Java application, register an oAuth client in the cloud platform subaccount. We need this to authenticate the java application to call the Adobe service. Refer link


Do not forget to note down the Client ID and the secret which was entered in Step 3. This is required to authenticate the Java application.

All the steps required in the SAP Cloud Platform to use the Adobe form services are complete. Now we need to design the layout of the PDF to be generated and prepare the data to be displayed in the PDF.

  1. Form Template


We will use the Adobe LiveCycle Designer to design the form template. For information about how and where to download this tool, along with licensing information, refer this link.

I have designed a very simple form layout for the blog. This tool can be used to design both Interactive and Non-interactive forms. You can refer the steps in the link to design and create data connections for the form template.



After the layout is complete, save the form template as .xdp file. The file will look something like this. This file has all the layout information required to render the PDF, which is used by the Adobe services.



  1. Data to be displayed.


Once you have the form template ready, you can generate the XML data file with sample data. Go to File->Form Properties. Choose Preview.



Click ‘Generate Preview Data’ and enter a location and name for the XML file to be generated.



Click on ‘Generate’. The XML file with some sample data will be generated and placed in the location. The XML file with sample data looks like this.



This is the exact format which needs to be passed to the Adobe service for it to pick up the data and place it in the exact location as designed in the form template.

Now that we have both the files which are required to make the REST API call, let us proceed with the java application.

Note that the application which calls the REST APIs to generate the PDF should create and send both the files to the Adobe services on cloud to get the PDF file as an output.

6. Java application

We perform the following steps in the java application.

  1. Retrieve Authorisation Token – oAuth token from SCP.

  2. Call the REST API using the oAuth token, the form template (.xdp file) and the data (.xml file).



  1. Retrieve oAuth token


Below is the java code I used to get the oAuth token. Make sure that you enter the Client ID and the client secret which was used in Step 3, when the oAuth client was registered.
/**
* Retrieve the auth token from Adobe service
* @return
*/
private String getoauthToken() {
String token = "";
String tokenEndpoint = "https://oauthasservices-sXXXXXXXtrial.hanatrial.ondemand.com/oauth2/api/v1/token";

String client_id = <<ClientID from step 3>>;
String client_secret = <<Client secret from step 3>>;

String grant_type = "client_credentials";
String scope = "generate-ads-output";

HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(tokenEndpoint);

String base64Credentials = Base64.getEncoder().encodeToString((client_id + ":" + client_secret).getBytes());
//1. Prepare the request headers
httpPost.addHeader("Authorization", "Basic " + base64Credentials);
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");

StringEntity input = null;
try {
input = new StringEntity("grant_type=" + grant_type + "&scope=" + scope);
httpPost.setEntity(input);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//2. Post the request
HttpResponse response = null;
try {
response = httpClient.execute(httpPost);
} catch (IOException e) {
e.printStackTrace();
}
//3. Retrieve the token from the response
try {
JSONObject tokenjson = new JSONObject(IOUtils.toString(response.getEntity().getContent(), "UTF-8"));
token = tokenjson.getString("access_token");
} catch (IOException e) {
e.printStackTrace();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return token;
}

2. Call the service

SAP Adobe forms on Cloud offers a number of APIs. All of them are documented here. I used the API ‘/adsRender/pdf - Render a PDF Form’ to render the PDF form.



As you can see in the API document, before the API is called, the template and the data files must be encoded as the REST API expects them to be sent as encoded strings.

I used the following code to encode the files.
 /**
* This method is used to encode the input files
* @param fileName
* @return
* @throws IOException
*/
private String encodeFileToBase64Binary(final String fileName) throws IOException {

String path = this.getClass().getClassLoader().getResource("").getPath();
String fullPath = URLDecoder.decode(path, "UTF-8");

File file = new File(fullPath + fileName);
return Base64.getEncoder().encodeToString(FileUtils.readFileToByteArray(file));

}

Now we have everything we need to call the service. Here is the code to call the service. I have documented the code in-line for your understanding.
/**
* This method is used to call the Adobe service
* @return
*/
private String callService(){
//1. Get the oAuth token
String token = getoauthToken();

//2. Prepare the request headers
String url = "https://adsrestapiformsprocessing-sXXXXXXXtrial.hanatrial.ondemand.com/ads.restapi/v1/adsRender/pdf";
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost request = new HttpPost(url);

request.addHeader("Authorization", "Bearer "+token);
request.addHeader("Content-Type", "application/json");

//3. Encode the form template file
String inputFileName = "adbforms\\Invoice.xdp";
String encxdp = "";

try {
encxdp = encodeFileToBase64Binary(inputFileName);
} catch (IOException e1) {
e1.printStackTrace();
}

//4. Encode the data xml file
inputFileName = "adbforms\\gendata.xml";
String encdata = "";
try {
encdata = encodeFileToBase64Binary(inputFileName);
} catch (IOException e1) {
e1.printStackTrace();
}

//5. Prepare the body of the request
String json = "{ "
+ "\"xdpTemplate\": \""+encxdp+"\", "
+ "\"xmlData\": \""+encdata+"\"}";

StringEntity input = null;
try
{
input = new StringEntity(json);
}catch(UnsupportedEncodingException e)
{
e.printStackTrace();
}
//6. Call the service and get the result
request.setEntity(input);
HttpResponse response = null;
try
{
response = httpClient.execute(request);
}catch(IOException e)
{
e.printStackTrace();
}

//7. Retrieve the file name and content from the response
String file = null;
String fileName = null;
try {
JSONObject tokenjson = new JSONObject(IOUtils.toString(response.getEntity().getContent(), "UTF-8"));
file = tokenjson.getString("fileContent");
fileName = tokenjson.getString("fileName");
//8. Decode and write the file.
writeUsingOutputStream(file, fileName);

} catch (IOException e) {
e.printStackTrace();
}catch(UnsupportedOperationException e)
{
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return file;
}

The rest API call gives the PDF file as an encoded string. I used the following piece of code to decode the string and write it as a PDF file.
/**
* This method is used to decode and the write the output PDF file.
* @param data
* @param fileName
*/
private void writeUsingOutputStream(String data, String fileName) {

fileName = "adbforms/test.pdf";
byte[] decoded = Base64.getDecoder().decode(data);
String path = this.getClass().getClassLoader().getResource("").getPath();
try {
String fullPath = URLDecoder.decode(path, "UTF-8");
File file = new File(fullPath + fileName);
FileUtils.writeByteArrayToFile(file, decoded);

} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

I ran the java application with the data XML file as shown below



Here is the output PDF response from the cloud service.



Ideally, in a real-world invoice application the xdp file will remain the same for all the calls as it holds the layout, but the data file (.xml) needs to be generated at runtime for each invoice before the Adobe API is called.

As always, please feel free to comment if you have any feedback or questions.
23 Comments
Former Member
0 Kudos
There are several tips on how to create pdf in the cloud sap forms by Adobe on the above blog-post, but if you need more added information about this, just check out fix epson printer error 0x69  and get the entire guidance on this.
mmcisme1
Active Contributor
0 Kudos
Wow - on this one I really think I have an advantage on-premise.  (At least from an ABAP point of view)  It's fairly easy to use the already created gateway service builder or create one yourself. (SEGW).  We basically create very little code.  There is a lot of the normal changes / moving things around on the form...  And that can easily be done in Adobe Lifecycle.  If you look at this blog you can see just how easy it is.  (It probably can be done on the cloud as well)

I'm guessing this blog covers one of the many way to do things.   And I imagine after you do it once or twice it will be easier.

Thank you for an interesting blog.
Sharadha1
Active Contributor
Michelle,

Thanks.

The scenario covered in the blog you have mentioned is completely different from the one this blog discusses about. We had the entire application on the cloud and no on-premise gateway servers involved.

If I am not wrong the referred blog details about how you can use odata to generate the XML template required by the Adobe tool to generate the form. In the cloud scenario which I have discussed we had to generate this XML template using java code. And of course depending on the technology you use to connect to the Adobe services on cloud the XML can be generated in a number of ways. All the Adobe services on cloud requires is the form template and the data XML to generate the PDF form.

For the scenario which involves ABAP backend, it might be true that it is easier to use the gateway services and the ADS on premise instead of calling the Adobe services on cloud.

 

-Sharadha

 

 

 
mmcisme1
Active Contributor
0 Kudos
Yes - I did see that it was in the cloud without a gateway service.  🙂  Sometimes my comments don't make a lot of sense.

What I was referring to, is that I think it is much easier to create the PDF on premise.  🙂   Reading this, it makes it one of the times that I'm happy to on-premise.
former_member614864
Discoverer
0 Kudos
Hi, I read your blog and executed the same step to generate pdf in SAP cloud.

I build the environment in SAP Cloud Cockpit, and I am success in uploading forms or templates, and also downloading the uploaded forms or templates.

But I filed to generate the pdf when I post the PDF rendering request, the response I got is given as follow:

{ "message": "Internal API Error", "results": "ADS Authentication Error", "trace": "Authentication Error: Could not redirect to ADS because could not get the RelayState value from the request. Destination credentials might be wrong.", "errorLevel": "D101" }

This message told me my ADS configuration is uncorrected, but if it is, why can I success in uploading and getting the forms or templates?
former_member396518
Discoverer
0 Kudos
Hi,

From which landscape are you calling the service? I assume it's hanatrial.ondemand.com/

Usually, this errors happens when the REST API destination is not configured correctly.
In your Cloud Cockpit please go to > services > SAP Forms by Adobe > REST API Roles & Destinations > 'ADS' Destination.
> Check if User and Password have been entered correctly. ('Check connection button' does not work)


Uploading/Downloading of forms and templates are independent of this connection.

Sven
former_member614864
Discoverer
0 Kudos
Hi,

Thanks for your kindly reply, it really helps me a lot. I figured it out now. Thank you.

Zhijiang Wan
0 Kudos

Hello,

I have a use case where I want to dynamically generate pdf(electronic proof of delivery) based on some configurational data.

For example – Each country will have a different template structure and different messages in the EPOD. In one country the disclaimer section is on top of pdf and in some other country disclaimer is at the bottom of pdf.

Is it possible to create a pdf template with such flexibility via the service?

Also, does the service support batch request of generating 1 to 500 PDFs?

My approach will be to call adobe service from a deployed JAVA application.

Sharadha1
Active Contributor
0 Kudos
Palash,

 

of course it is possible.  you need to create different templates for each country and pass it along with the pdf data.

The billing information for this cloud service can be found at - https://cloudplatform.sap.com/capabilities/product-info.SAP-Cloud-Platform-Forms-by-Adobe.5f0a2089-9...
0 Kudos
Thanks for the response Sharadha.

I can do it by maintaining different templates but can we create pdfs using xsl transformation?

Because I already have a service with me which does the xsl transformation. So what I would have to do is just upload my xsl to adobe forms and call it instead of my service.

Regards,

Palash
Sharadha1
Active Contributor
0 Kudos
Palash,

I do not think it is possible. You can store various template in the Template Store and use them for rendering the PDF but XSL transformation is not supported as per my knowledge. The cloud service expects the following to render the form.

  1. Template file - .xdp file

  2. XML data which matches the .xdp template structure


You can read more about template store here.

 

Sharadha
0 Kudos
Okay. Will look into the options. Thanks a lot for the response.

 

Regards,

Palash Kondekar
0 Kudos
Hello sharadha.k

great blog post on how to use the Adobe Forms Service. I followed along trying to do the same thing in an html5 app. So far I have my template in the cloud and converted my data from json to the base64 input.

I just wanted to ask if you may have a corresponding tutorial on how to implement everything in html5. I am stuck at downloading the PDF and calling the API.

 

Best Regards

Muhammed
Sharadha1
Active Contributor
0 Kudos
Hi Muhammed,

 

Can you tell me how you are calling the API from the UI5 application?

 

-Sharadha
0 Kudos
Hi,

Thanks for the blog...

How can we consume the REST api in a sapui5 application and display the xdp template and xml

data source as pdf?
Sharadha1
Active Contributor
0 Kudos
Hi Muhammad,

 

you can refer my new blog for this - https://blogs.sap.com/2019/10/16/consume-sap-forms-by-adobe-from-sap-ui5-applications/

 
Sharadha1
Active Contributor
0 Kudos
ankesh_jindal4
Participant
0 Kudos
@john.wan

Thorough postman I am getting same error, how did you resolve this ? what kind of error is this?
0 Kudos
Hi,

I followed the approach and while trying via POSTMAN, my get call to "forms" service works fine but when I do "post" call to render pdf I get the below ADS destination error.

Currently ADS destination authentication type is"None". Is it mandatory to make it "Basic" or "client certificate" ?

 

{
"message": "Internal API Error",
"results": "ADS Destination Error",
"trace": "Authentication type not supported. Please use Basic Authentication or ClientCertificate Authentication.",
"errorLevel": "D105"
}

Thanks,

Neha
Ananth_Charya
Explorer
0 Kudos
Hi,

Thanks for taking the time to draft this Technical Article. It's very thorough and helpful.

Our business client would like to have a "Adobe Interactive forms"  to be generated to present the data to the supplier and also collect the response within the same form. For example: display the purchase order and have the supplier to acknowledge the quantities and the delivery dates.

With that background, I have a couple of questions where I could use your expertise on this subject matter.

  • Going by this article, I think we should be able to use Cloud based ADS for this purpose, right!.


 

  • Client already have a .NET based site and would like to have the Interactive form presented to their Supplier on that site. For the Supplier to be able to edit the form, does SAP require them to purchase additional licensing?


 

  • Architecture that we are envisaging here is, to have the .NET site to call a service to pull the necessary data in the XML format and then pull the template from ADS with the reference to the data XML file. Do you think that would work?.


 

Appreciate your reply in advance.

Thanks,

Ananth

 

 
Sharadha1
Active Contributor
0 Kudos
Sharadha1
Active Contributor
Hi Ananth, Please find my answers below.

1. Yes you are correct.

2. Note that currently the service mentioned above is not available under the trial license. Also SAP is planning to release this service on Cloud foundry (Q1 2021). Pricing is based on number of requests. You can see pricing related details here -

https://discovery-center.cloud.sap/serviceCatalog/forms-service-by-adobe?service_plan=forms-by-adobe...

3. This cloud service exposes Rest APIs. So it can be consumed by any other cloud or on-premise application technically.
0 Kudos
Hi Sharadha,

I also referred to your blog and executed the same but I am getting the below error message when I post the rendering request.

{"message":"Internal API Error","results":"Template Store Error","trace":"Template Store Error: Storage entity cannot be found. Please review your request parameters.","errorLevel":"C110"}


 

Thanks!
Labels in this area