Skip to Content
Technical Articles

Consume ‘SAP Forms by Adobe’ from SAP UI5 applications

Edited on 19/11/2019 – Removed token generation from within the UI5 application and handled it in the destination.

 

After publishing the blog on how to generate PDFs in the cloud using Java application, I received few comments and a number of messages on how to consume the SAP Forms by Adobe service from a SAP UI5 application. This blog will cover the steps to do the same.

Please refer my other blog: https://blogs.sap.com/2018/11/08/generate-pdfs-in-the-cloud-sap-forms-by-adobe/#, on how this can be done using a Java application.

As I mentioned in my previous blog, SAP standard documentation covers most of the steps in detail and I will be referring to the official document directly for those steps.

In order to call the Rest API to generate the PDF forms from SAP UI5 application, 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.[For the sake of simplicity, I used the same PDF layout (Form Template) I used in the other blog].

SAP has introduced ‘Template Stores’ to store the Form templates. They can be referred easily when the PDF rendering API is called. Unlike the other blog in which I passed the XDP template in the API call, I have stored and referred the Form template from template store here.

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. Register an oAuth client in the cloud platform subaccount and create a destination pdf render API by following the steps in the link . Use URL as https://adsrestapiformsprocessing-s0008289464trial.hanatrial.ondemand.com/ads.restapi

Now we are all set to call the REST APIs from the ui5 application.

5. UI5 application

Maintain entries in Neo-app.json to access both the destinations configured above.

{
  "welcomeFile": "/webapp/index.html",
  "authenticationMethod": "saml",
  "routes": [
    {
      "path": "/resources",
      "target": {
        "type": "service",
        "name": "sapui5",
        "entryPath": "/resources"
      },
      "description": "SAPUI5 Resources"
    },
    {
      "path": "/test-resources",
      "target": {
        "type": "service",
        "name": "sapui5",
        "entryPath": "/test-resources"
      },
      "description": "SAPUI5 Resources"
    }
     {
      "path": "/render",
      "target": {
        "type": "destination",
        "name": "pdf_render_oAuth"
      },
      "description": "pdf_render_oAuth"
    },
    {
      "path": "/webapp/resources",
      "target": {
        "type": "service",
        "name": "sapui5",
        "entryPath": "/resources"
      },
      "description": "SAPUI5 Resources"
    },
    {
      "path": "/webapp/test-resources",
      "target": {
        "type": "service",
        "name": "sapui5",
        "entryPath": "/test-resources"
      },
      "description": "SAPUI5 Test Resources"
    }
  ],
  "sendWelcomeFileRedirect": true
}

When you have to call the print functionality, use the code below. I have tried to give comments in line for easy understanding.

To know more about the API calls, refer Rest API  documentation

	renderPDF: function () {
			//Generate the token
			var modeldata = this.getView().getModel().getData();
			//Build the xml data with the data from the model
			var xmldata =
				"<?xml version=\"1.0\" encoding=\"UTF-8\"?><form1><InvoiceNo>" + modeldata.Invoices[0].Invoiceno + "</InvoiceNo><InvoiceTo>" +
				modeldata.Invoices[0].Invoiceaddress + "</InvoiceTo><InvoiceTotal>" + modeldata.Invoices[0].Invoiceamount +
				"</InvoiceTotal></form1>";
			var encdata = btoa(xmldata);
			//prepare the render API call. Pick up the template from template store
			var jsondata = "{  " + "\"xdpTemplate\": \"" + "InvoiceData/Invoice" + "\", " + "\"xmlData\": \"" + encdata + "\"}";
			var url_render = "/render/v1/adsRender/pdf?templateSource=storageName";
			//make the API call
			$.ajax({
				url: url_render,
				type: "post",
				contentType: "application/json",
				data: jsondata,
				success: function (data, textStatus, jqXHR) {
					//once the API call is successfull, Display PDF on screen
					var decodedPdfContent = atob(data.fileContent);
					var byteArray = new Uint8Array(decodedPdfContent.length);
					for (var i = 0; i < decodedPdfContent.length; i++) {
						byteArray[i] = decodedPdfContent.charCodeAt(i);
					}
					var blob = new Blob([byteArray.buffer], {
						type: 'application/pdf'
					});
					var _pdfurl = URL.createObjectURL(blob);

					if (!this._PDFViewer) {
						this._PDFViewer = new sap.m.PDFViewer({
							width: "auto",
							source: _pdfurl
						});
						jQuery.sap.addUrlWhitelist("blob"); // register blob url as whitelist
					}

					this._PDFViewer.open();

				},
				error: function (data) {

				}
			});

		}

 

Here is the output of the UI5 application.

 

  1. Execute the application from SCP

 

  1. Click on Edit and change the details (just to make sure that the data is being picked up dynamically)

3.Click on Print. API calls will be made and the PDF is shown in the PdfViewer control

 

As always, please feel free to comment if you have any feedback or questions.

12 Comments
You must be Logged on to comment or reply to a post.
  • Sharadha Krishnamoorthy

    Hi Sharadha,

    I am working on one scenario , I want to check if it is feasible or not initially. Below are details of my scenario:-

    Shipment functionality is not present in S4C system, I have requirement in which I have to show user shipment with all it’s deliveries and if user click on print, it will print out all delivery output.

    In S4C system, Individual deliveries can be taken print out but there is no Shipment concept as such, I have extended Delivery app by custom field that contains Shipment number.

    Now I have to build one custom app(SDK) that will ask user for shipment number, on search it should query all deliveries(S4C) with respect to that shipment number and there should be print button, on clicking of Print button, all deliveries should get printed, output is in the adobe form.

    DELIVERY_NOTE output type can be used to get the data in xml and I have already developed form which is in .xsd format.

    I need your advice what should be the best approach to develop this kind of scenario.

    Regards,

    Ankesh

    • Ankesh,

      What do you mean by S4C system – is it S4HANA for Cloud? The adobe forms service can be called from both on-premise and cloud applications. please refer the link – https://help.sap.com/viewer/6d3eac5a9e3144a7b43932a1078c7628/Cloud/en-US/ed10b24db1d64e6c87d7303986d174e9.html

      It will be just a Rest API call in both the scenarios –  storing the form templates in the template store (recommended option) or sending it along with each request.

      DELIVERY_NOTE output type can be used to get the data in xml => You have to make sure that the xml data is in the same format as expected by the .xsd form template.

      The scenario described by you is very much achievable from either a SAP UI5 application or a Java application ( you can refer my other blog –  https://blogs.sap.com/2018/11/08/generate-pdfs-in-the-cloud-sap-forms-by-adobe/ )

       

      Let me know if you have further questions.

       

      -Sharadha

       

      • Sharadha Krishnamoorthy

         

        Hi Sharadha,

        S4C means S4 HANA Cloud system

        https://adsrestapiformsprocessing-s0008289464trial.hanatrial.ondemand.com/ads.restapi/v1/adsRender/pdf

        This  adove URL  is not working. If I replace V1 with # value, then I get 200 response but in response I am getting below response from 2nd Ajax call( HTML response), It is not in file-content format. Can you please suggest what configuration is done wrong in this?

         

        Through Postman , I am also getting same response.

         

        <!-- HTML for static distribution bundle build -->
        <!DOCTYPE html>
        <html lang="en">
            <head>
                <meta charset="UTF-8">
                    <title>ADS REST API</title>
                    <link rel="stylesheet" type="text/css" href="swagger/swagger-ui.css" >
                        <link rel="icon" type="image/png" href="swagger/icon/favicon-32x32.png" sizes="32x32" />
                        <link rel="icon" type="image/png" href="swagger/icon/favicon-16x16.png" sizes="16x16" />
                        <style>
              html
              {
                box-sizing: border-box;
                overflow: -moz-scrollbars-vertical;
                overflow-y: scroll;
              }
        
              *,
              *:before,
              *:after
              {
                box-sizing: inherit;
              }
        
              body
              {
                margin:0;
                background: #fafafa;
              }
            </style>
                    </head>
                    <body>
                        <div id="swagger-ui"></div>
                        <script src="swagger/swagger-ui-bundle.js"> </script>
                        <script src="swagger/swagger-ui-standalone-preset.js"> </script>
                        <script>
            
            function HideTopbarPlugin() {
          	  // this plugin overrides the Topbar component to return nothing
          	  return {
          	    components: {
          	      Topbar: function() { return null }
          	    }
          	  }
          	}
            
            window.onload = function() {
              // Begin Swagger UI call region
              const ui = SwaggerUIBundle({
                url: "/ads.restapi/v1/swagger.json",
                dom_id: '#swagger-ui',
                deepLinking: true,
                supportedSubmitMethods: [ "options" ],
                presets: [
                  SwaggerUIBundle.presets.apis,
                  SwaggerUIStandalonePreset
                ],
                plugins: [
                  SwaggerUIBundle.plugins.DownloadUrl,
                  HideTopbarPlugin
                ],
                layout: "StandaloneLayout"
              })
              // End Swagger UI call region
        
              window.ui = ui
            }
          </script>
                    </body>
                </html>
  • Hi

    Thanks for writing this blog, I must address something.

    If you follow the steps delivered by SAP, and use SAML authentication, there is no need to generate a token and provide it in the header of the API call.

    You can just do a post to adsRender/pdf

    • Steven,

       

      I am not sure what do you mean by “If you follow the steps delivered by SAP, and use SAML authentication”. Can you please share the documentation you are referring here? As far as I am aware, we need to generate the token using the oAuth client and use it to call the REST APIs. It would be helpful if you share the steps or the documentation.

       

      -Sharadha

  • See step 3 in your blog, hereby the links:

    https://help.sap.com/viewer/6d3eac5a9e3144a7b43932a1078c7628/Cloud/en-US/3be5e26d1919495c8f8fc4e8773e3885.html
    https://help.sap.com/viewer/6d3eac5a9e3144a7b43932a1078c7628/Cloud/en-US/894418cd915b46bd9fcf27ac0bf6d15f.html

    The token is automatically generated, when you set up the destination, as mentioned in the links.
    If you then use that destination, there is no further need to request a token yourself.
    As a callback to the token method is done automatically.
    When changing the neo-app, you have to configure the authentication to SAML.

    I have it working in one of our own SAP UI5 Cloud apps

    • Thanks.  But according to the blog and as per the help links, we have to set up the oAuth client for token generation, which I have explained. I do not see any SAML set up as per your comment anywhere in the links. Would be helpful if you can share the details regarding the SAML set up which you are referring here.

       

      -Sharadha

      **

      For some reason, when I checked earlier,  I was able to see only the links in this reply and not the description which you have written clearly 🙂

      I have updated the blog now. Thanks again for this!

      • Dispatcher takes care of the magic, when the authenticationMethod property is set to saml
        As when you call the service (via the destination), it’s using the dispatcher.

        Map the Destination to your HTML5 Application via Application Descriptor

        1. Open the neo-app.json file of your HTML5 application.
        2. Set the property authenticationMethod to saml.

        You also should change the Authentication on the destination you provided in this blog, from NoAuthentication to OAuth2ClientCredentials and fill up the required fields, with the values from the oAuth Client

        • Steven,

          You are right in saying that there is no need to generate the token from within the application every time. But it is just not achieved by just setting the authentication method to SAML (if you notice, it has already been done in step 5 in my blog).

          But we can avoid the call to token generation and set the authentication for the destination pdf_render  to  ‘OAuth2ClientCredentials’ and maintain the OAuth credentials directly. I have tested this just now and it works perfectly fine. Will update the blog shortly.

          Thanks a lot for pointing this out.

          -Sharadha