Skip to Content
Technical Articles
Author's profile photo Andre Fischer

Generate PDFs in SAP Cloud Platform, ABAP environment

One of our customers had the requirement to generate PDFs in his SAP Cloud Platform, ABAP environment system.

Since the Adobe service offered in SAP Cloud platform – ‘SAP Forms by Adobe’ cannot yet be leveraged natively in SAP Cloud Platform, ABAP environment for the time being we had to use the REST API as a workaround.

In this blog I will describe the steps that are necessary to connect the SAP Forms by Adobe to your SAP Cloud Platform, ABAP environment system and provide a sample class that retrieves a template from the template store, fills it with data and generates the PDF as a based64-encoded json string.

The following steps have be performed:

  • Register an OAuth Client in the Neo subaccount where the SAP Forms by Adobe service resides
  • Configure the SAP Forms for Adobe Service
    1. add roles and change authentication methods
    2. Upload a template
  • Create a destination in the destination service in cloud foundry that is used by SAP Cloud Platform, ABAP Environment
  • Create a sample class

How to visualize the generated PDF in SAPUI5 has been described in the following blog from Sharadha Krishnamoorthy :

Consume ‘SAP Forms by Adobe’ from SAP UI5 applications

Register an OAuth Client

How to register an OAuth Client in the Neo environment is described in the SAP Online Help.

We will call the REST API using the destination service of the space where our ABAP system has been deployed using the authentication method OAuth2ClientCredentials.

It enables grant of an OAuth access token based on the client credentials only, without user interaction. As in this case this flow is used for enabling system-to-system communication with a service user.

  1. In the Branding tab we have to note down the URL that points to the token endpoint which contains the Technical Name <abcd123456> of your Neo account.



  2. In the Clients tab we have to register a new client with the following values:
    Subscription: formsprocessing/adsrestapi
    Authorization Grant: Client Credentials
    Token Lifetime: <left empty>

    By leaving the text box of the token lifetime empty the lifetime of the tokens is infinite.


    Do NOT use the subscription formsprocessing/ads, but  formsprocessing/adsrestapi.
    The longer name is not shown completely in the text box in the screen shot.
    I ran myself into this error and it took me a while to find the root cause.
    When you nevertheless use the wrong scope you will later get an error message:
    “scopes exceed the scope registered for the client.”

Configure Adobe Forms Service

How to activate the Adobe Forms Service in SAP Cloud Platform is described in the SAP Online Help.

In the service configuration overview page we have to follow the following links

  1. Roles & Destinations
  2. REST API Roles & Destinations
  3. REST API Template Store UI

Service Configuration: Roles & Destinations – Roles


Here you have to assign your user (here d<xxxxxx>) the ADSCaller role which entitles this technical user to call the REST API.

Service Configuration: REST API Roles & Destinations – Destinations

  1. In the destination tab we have to change the authentication settings of the destination ADS that points to the Adobe Document Service in your Neo account so that your user (here d<xxxxxx>) and the password is provided.

  2. In the Roles tab we have to assign the user (here d<xxxxxx>) the StorageUIAdmin role so that we can upload templates that are used by the Adobe Forms Service.

Service Configuration: REST API Template Store UI

When you click on the link REST API Template Store UI a SAP Fiori application starts that lets you upload a template in your template store.

Using the REST API we can populate these templates using XML data that is sent by the REST client to the REST API. The response of this service call is a base64 encoded json string that can be visualized by an appropriate client such as a SAPUI5 application.

You first have to create a form (here called DEMO ) and then in the details screen you can uploaded a template (here called TEMPLATE).


Configure the destination service in cloud foundry

In your cloud foundry environement where your ABAP environement resides you have to create a destination in the destination service

How to create a communication arrangement for outbound communication is described in this tutorial Create a Communication Arrangement for Outbound Communication.


Here we provide the following details

Type: HTTP
Description: SAP Cloud Platform Forms by Adobe Service
URL: https://adsrestapiformsprocessing-<abcd123456>
Proxy Type: Internet
Authentication: OAuth2ClientCredentials
Client ID: b407a0a4-65ed-37d5-bd9d-c94bdb79f5ae
Client Secret: <password that has been used to create the OAuth Client>
Token Service URL: https://oauthasservices-<abcd123456>
Token Service User: b407a0a4-65ed-37d5-bd9d-c94bdb79f5ae
Token Service Password: <password that has been used to create the OAuth Client>
In the additional properties we add the property scope with the value generate-ads-output .
The Client Secret as well as the Token Service Password is the password that you have used to create the OAuth Client. The Token Service URL you have noted down when you have created the OAuth client.
By specifying this propetery the destination service will add a query parameter to the token service URL alongside with the query parameter grant_type. ( … /oauth2/api/v1/token?grant_type=client_credentials&scope=generate-ads-output)
If you don’t provide this value the REST call will fail with an authorization error.


The sample code performs the following actions.
  1. The data that is used to generate the PDF is provided as XML data in
  2. This data is then base64 encoded using the whitelisted API
  3. Using the destination ADS_SRV and the communication arrangement a http destination generated by calling
  4. The http request is created
    • http headers are added for json support
    • A query parameter ?templateSource=storageName is added that specifies that a template should be used
    • The relative URL of the REST API /ads.restapi/v1/adsRender/pdf is added to the host namestored in the destination
    • The json payload is created by calling the json library /ui2/cl_json=>serialize that contains  the name  of the template DEMO/TEMPLATE and the based64 encoded XML input data.
  5. The response of the REST API is formatted in json and is read into ABAP data structures /ui2/cl_json=>generate
The console output would be as follows:
retrieved destination ADS_SRV

json payload



Source code


    INTERFACES if_oo_adt_classrun.

    METHODS get_root_exception
        !ix_exception  TYPE REF TO cx_root
        VALUE(rx_root) TYPE REF TO cx_root .



  METHOD if_oo_adt_classrun~main.

    "Syntax of the URL as described in "Call the REST API"
    "is the following

    CONSTANTS lc_ads_render TYPE string VALUE '/ads.restapi/v1/adsRender/pdf'.
    CONSTANTS lc_storage_name TYPE string VALUE 'templateSource=storageName'.
    CONSTANTS lc_template_name TYPE string VALUE 'DEMO/TEMPLATE'.

    "the ABAP field names such as "xdp_Template" will be converted to camel case "xdpTemplate"
    "by the json library /ui2/cl_json

    TYPES :
      BEGIN OF struct,
        xdp_Template TYPE string,
        xml_Data     TYPE string,
        form_Type    TYPE string,
        form_Locale  TYPE string,
        tagged_Pdf   TYPE string,
        embed_Font   TYPE string,
      END OF struct."

    DATA name_value_pairs  TYPE  if_web_http_request=>name_value_pairs   .

    name_value_pairs = VALUE #(
                   ( name = 'Accept' value = 'application/json, text/plain, */*'  )
                   ( name = 'Content-Type' value = 'application/json;charset=utf-8'  ) ).

    DATA lr_data TYPE REF TO data.

    DATA(lv_xml) = |<Form>| &&
    |<FormMaster>| &&
    |<Logo1Image></Logo1Image>| &&
    |<Logo2Image></Logo2Image>| &&
    |<Logo3Image></Logo3Image>| &&
    |<PrintFormTitleText>Title</PrintFormTitleText>| &&
    |<SenderAddressText></SenderAddressText>| &&
    |<WatermarkText>Test Copy</WatermarkText>| &&
    |<AdministrativeData>| &&
    |<CreationDateTime>2019-07-24T08:21:26</CreationDateTime>| &&
    |<LocaleCountry>DE</LocaleCountry>| &&
    |<LocaleLanguage>E</LocaleLanguage>| &&
    |<TenantIsProductive>false</TenantIsProductive>| &&
    |<User></User>| &&
    |</AdministrativeData>| &&
    |<Footer>| &&
    |<FooterBlock1Text>Footer1</FooterBlock1Text>| &&
    |<FooterBlock2Text>Footer2</FooterBlock2Text>| &&
    |<FooterBlock3Text>Footer3</FooterBlock3Text>| &&
    |<FooterBlock4Text>Footer4</FooterBlock4Text>| &&
    |</Footer>| &&
    |<RecipientAddress>| &&
    |<AddressID>655846</AddressID>| &&
    |<AddressLine1Text>Company</AddressLine1Text>| &&
    |<AddressLine2Text>Test Company</AddressLine2Text>| &&
    |<AddressLine3Text>SAP SE</AddressLine3Text>| &&
    |<AddressLine4Text>PO Box 13 27 89</AddressLine4Text>| &&
    |<AddressLine5Text>123459 Walldorf</AddressLine5Text>| &&
    |<AddressLine6Text></AddressLine6Text>| &&
    |<AddressLine7Text></AddressLine7Text>| &&
    |<AddressLine8Text></AddressLine8Text>| &&
    |<AddressType>1</AddressType>| &&
    |<Person></Person>| &&
    |</RecipientAddress>| &&
    |</FormMaster>| &&
    |<GIHeaderNode>| &&
    |<Language>EN</Language>| &&
    |<MaterialDocument>101</MaterialDocument>| &&
    |<MaterialDocumentItem>ITEM-2202</MaterialDocumentItem>| &&
    |<MaterialDocumentYear>2019</MaterialDocumentYear>| &&
    |<PrinterIsCapableBarCodes>true</PrinterIsCapableBarCodes>| &&
    |<GIMI>| &&
    |<AccountingDocumentCreationDate>2019-07-01T00:00:00</AccountingDocumentCreationDate>| &&
    |<BaseUnit>EA</BaseUnit>| &&
    |<Batch></Batch>| &&
    |<CostCenter></CostCenter>| &&
    |<CreatedByUser>SAP</CreatedByUser>| &&
    |<FixedAsset></FixedAsset>| &&
    |<GoodsMovementQuantity>100000.000</GoodsMovementQuantity>| &&
    |<GoodsMovementType>561</GoodsMovementType>| &&
    |<GoodsMovementTypeName>Initial stock entry</GoodsMovementTypeName>| &&
    |<GoodsReceiptAcctAssgmt></GoodsReceiptAcctAssgmt>| &&
    |<GoodsReceiptAcctAssgmtText></GoodsReceiptAcctAssgmtText>| &&
    |<GoodsReceiptPostingDate>2019-07-01T00:00:00</GoodsReceiptPostingDate>| &&
    |<Language>EN</Language>| &&
    |<MaintOrderOperationCounter>00000000</MaintOrderOperationCounter>| &&
    |<MaintOrderRoutingNumber>0000000000</MaintOrderRoutingNumber>| &&
    |<ManufacturingOrder></ManufacturingOrder>| &&
    |<MasterFixedAsset></MasterFixedAsset>| &&
    |<Material>M1</Material>| &&
    |<MaterialDocument>4900060890</MaterialDocument>| &&
    |<MaterialDocumentItem>0001</MaterialDocumentItem>| &&
    |<MaterialDocumentYear>2019</MaterialDocumentYear>| &&
    |<MaterialName>Material1</MaterialName>| &&
    |<Plant>0001</Plant>| &&
    |<PlantName>German-Plant</PlantName>| &&
    |<PrinterIsCapableBarCodes>true</PrinterIsCapableBarCodes>| &&
    |<ProjectNetwork></ProjectNetwork>| &&
    |<SalesOrder></SalesOrder>| &&
    |<SalesOrderItem>000000</SalesOrderItem>| &&
    |<SalesOrderScheduleLine>0000</SalesOrderScheduleLine>| &&
    |<StorageLocation>0003</StorageLocation>| &&
    |<TextElementText></TextElementText>| &&
    |<VersionForPrintingSlip>1</VersionForPrintingSlip>| &&
    |<WBSElementInternalID>00000000</WBSElementInternalID>| &&
    |<WarehouseStorageBin></WarehouseStorageBin>| &&
    |</GIMI>| &&
    |</GIHeaderNode>| &&

    DATA(ls_data_xml) = cl_web_http_utility=>encode_base64( lv_xml ).

        DATA(lo_destination) = cl_http_destination_provider=>create_by_cloud_destination(
                                 i_name                  = 'ADS_SRV'
                                 i_service_instance_name = 'AdobeDocumentServicesCommArrangement'
                                 i_authn_mode            = if_a4c_cp_service=>service_specific

        out->write( 'retrieved destination ADS_SRV' ).

        DATA(lo_http_client) = cl_web_http_client_manager=>create_by_http_destination( i_destination = lo_destination ).
        DATA(lo_request) = lo_http_client->get_http_request( ).

        lo_request->set_header_fields( i_fields = name_value_pairs ).
        lo_request->set_query( query =  lc_storage_name ).
        lo_request->set_uri_path( i_uri_path = lc_ads_render ).

        DATA(ls_body) = VALUE struct( xdp_Template = lc_template_name
                                      xml_Data = ls_data_xml
                                      form_Type = 'print'
                                      form_Locale = 'de_DE'
                                      tagged_Pdf = '0'
                                      embed_font = '0' ).

        DATA(lv_json) = /ui2/cl_json=>serialize( data = ls_body compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).

        out->write( 'json payload' ).
        out->write( lv_json ).

            data   = lv_json

        DATA(lo_response) = lo_http_client->execute( i_method = if_web_http_client=>post ).
        DATA(lv_json_response) = lo_response->get_text( ).

        out->write( 'lv_json_response:' ).
        out->write( lo_response->get_text( ) ).

          <data>                TYPE data,
          <field>               TYPE any,
          <pdf_based64_encoded> TYPE any.

        "lv_json_response has the following structure `{"fileName":"PDFOut.pdf","fileContent":"JVB..."}

        lr_data = /ui2/cl_json=>generate( json = lv_json_response ).

        IF lr_data IS BOUND.
          ASSIGN lr_data->* TO <data>.
          ASSIGN COMPONENT `fileContent` OF STRUCTURE <data> TO <field>.
          IF sy-subrc EQ 0.
            ASSIGN <field>->* TO <pdf_based64_encoded>.
            out->write( <pdf_based64_encoded> ).
      CATCH cx_root INTO DATA(lx_exception).
        out->write( 'root exception' ).
        out->write( get_root_exception( lx_exception )->get_longtext(  ) ).


  METHOD get_root_exception.
    rx_root = ix_exception.
    WHILE rx_root->previous IS BOUND.
      rx_root ?= rx_root->previous.


Problems that can occur

During the setup of this scenario I ran into some problems.

Destination supplies no authorization data: {…}copes exceed the scope registered for the client.

This was the output when I used wrong OAuth credentials. Since the name of the subscription was not shown completely I erroneously used the subscription formsprocessing/ads instead of formsprocessing/adsrestapi.

<!doctype html><html lang=”en”><head><title>HTTP Status 403 – Forbidden</title><style type=”text/css”>body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 403 – Forbidden</h1></body></html>

I forgot to provide the query parameter scope=generate-ads-output


Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Binson Varikkasseril Abraham
      Binson Varikkasseril Abraham

      Hi Andre,

      Thanks for collecting the points together and sharing with all. ?




      Author's profile photo Pavlo Astashonok
      Pavlo Astashonok

      Nice shot. Is Adobe Forms Service available only in productive SCP accounts, not in trial?

      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author

      Hi Pavel,

      Adobe Forms is also available in Neo trial accounts.

      However the class /ui2/cl_json is not yet whitelisted in Steampunk Trial systems, only in productive systems.

      Once this library is available I will update my blog.

      You would then use cl_http_destination_provider=>create_by_url to create destination and you would have to provide the OAuth credentials as a string.

      Best Regards,


      Author's profile photo kranthikumar surampudi
      kranthikumar surampudi

      Hi Andre,


      If we have have the dynamic tables in the Form How can we achieve this scenario with using XML data mapping.




      Author's profile photo Pascal Bremer
      Pascal Bremer

      Typical workflow:

      1. On ABAP you create function that generates xml data
      2. You export this xml data to adobe livecycle designer ( )
      3. You design your form around this data
      4. Upload your form to the form template store
      5. On ABAP call the function that generates xml data and pass them to the rest api (also select your uploaded form by name)
      6. On ABAP retrieve the pdf and store it or send it to the user

      This blog post is only about connecting the abap environment service to the Forms by Adobe API.
      This is not an how to on developing adobe forms.

      Author's profile photo Dineshkumar Chinniah
      Dineshkumar Chinniah

      Hi Pascal,

      My work till now on this topic.

      1. Destination is done.

      2. Template design is done through Adobe livecycle designer.

      3. Template is converted to .xdp format and uploaded in SAP cloud Neo platform.

      That template has dynamic part. For which I need to fetch data from tables and pass into corresponding binding variables in the template. How do I achieve that?


      What should be next step in this scenario. Please suggest a solution.

      Author's profile photo Pascal Bremer
      Pascal Bremer

      There are multiple ways to serialize abap data to xml.

      One of them would be to loop through the table and dynamically create your xml via iXML:

      Best regards

      Author's profile photo kranthikumar surampudi
      kranthikumar surampudi

      Hi Pascal,


      iXML interfaces are not available in SAP ABAP CLOUD PLATFORM,

      Can you please suggest any other way to serialize our abap data to XML in ABAP CLOUD .


      Best Regards,


      Author's profile photo Sramana Ghosh
      Sramana Ghosh

      Hi Andre,

      I am getting the following error:

      Destination supplies no authorization data: {...}o not support OAuth Service content reponse type: text/html.

      Is it because my admin has set the authentication to NoAuthentication instead of BasicAuthentication for destination configuration in Neo?

      Author's profile photo Vijay Sharma
      Vijay Sharma

      Thanks Andre Fischer  for sharing this.

      What would be different if i want to generate PDFs in SCP from ABAP in On-Premise?

      Setup part i understand includes:

      • Configuring the ABAP System
        • Establish the SSL Connection to SAP Cloud Platform
        • Configure the RFC Destination
        • Configure the ICF Service on your ABAP System
      • Installing and Configuring the Cloud Connector
      • Configure the Destination in SAP Cloud Platform Subaccount

      How the code for calling APIs will be like?




      Author's profile photo Michael Bader
      Michael Bader

      Hi Andre,

      great Blog thanks for this approach to create a PDF document.

      Unfortunately it suffers a bit on the fact, that 'Forms by Adobe' Service is only available in an Neo environment. In particular customers, that want to use ABAP on cloud platform, are ordering a foundry-SCP instance.

      Is there a reason, that 'Forms by Adobe' is only available on Neo?
      Are there plans to provide this service in cloud-foundry also?

      Thanks a lot & Regards


      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author

      Hi Michael,

      We plan to offer a multi-cloud version of Forms by Adobe. It’s also on the road map which you can find here:




      Author's profile photo Praveen Padhy
      Praveen Padhy

      Hi Andre Fischer

      Thank you for the nice explanation. I just had 1 question in mind as in this case how we can activate the fillable property of the form and make the form fillable?



      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author


      => setting property formType, interactive allows you to fill out a PDF.

      Author's profile photo Praveen Padhy
      Praveen Padhy

      Thanks Andre! 🙂 This was very helpful and it works now. If possible could you also throw some light on whether it is also feasible to visualize the form preview of the generated pdf content in the BTP ABAP environment itself?

      Author's profile photo Giacomo Savioli
      Giacomo Savioli

      Hi Andre Fischer

      I followed your blog and can produce the printout. Amazing blog!

      But i have an issue with one tag in xml data. The material description has this character °.

      I recive and error "InvalidDataException: Xml parsing error: not well-formed (invalid token) ".. 

      But if i check xml in online tools the xml that i produce it's well-formed.

      Any suggestion?




      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author

      Unfortunately not. I suggest to post this issue as a question in the Q&A section.