Skip to Content
Technical Articles
Author's profile photo Sharadha Krishnamoorthy

Generate PDFs in the cloud – SAP Forms by Adobe

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.

Assigned Tags

      23 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo jack peter
      jack peter

      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.

      Author's profile photo Michelle Crapo
      Michelle Crapo

      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.

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      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

       

       

       

      Author's profile photo Michelle Crapo
      Michelle Crapo

      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.

      Author's profile photo Zhijiang Wan
      Zhijiang Wan

      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?

      Author's profile photo Sven Rullmann
      Sven Rullmann

      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

      Author's profile photo Zhijiang Wan
      Zhijiang Wan

      Hi,

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

      Zhijiang Wan

      Author's profile photo Ankesh Jindal
      Ankesh Jindal

      @Zhijiang Wan

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

      Author's profile photo Palash Kondekar
      Palash Kondekar

      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.

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      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-96b6-4b19-993b-fc254b006f60.html#capabilitiesPricing

      Author's profile photo Palash Kondekar
      Palash Kondekar

      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

      Author's profile photo Sharadha K
      Sharadha K

      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

      Author's profile photo Palash Kondekar
      Palash Kondekar

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

       

      Regards,

      Palash Kondekar

      Author's profile photo Muhammed Altuntas
      Muhammed Altuntas

      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

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      Hi Muhammed,

       

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

       

      -Sharadha

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      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/

       

      Author's profile photo Reshmaa Anu
      Reshmaa Anu

      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?

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      Hi Reshmaa,

       

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

       

      Author's profile photo Neha Goyal
      Neha Goyal

      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

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      Yes.. you need to have a valid authentication type. please refer https://help.sap.com/viewer/6d3eac5a9e3144a7b43932a1078c7628/Cloud/en-US/0a2e5a45d5494ec08318ead2019d54db.html

      Author's profile photo Ananth Charya
      Ananth Charya

      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

       

       

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      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&region=all&tab=service_plan

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

      Author's profile photo Rahul K
      Rahul K

      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!