
Update
30.07.2024
Please use the following newer post that describes the Adobe Forms in Cloud Foundry
SAP Forms service by Adobe in SAP BTP, ABAP enviro... - SAP Community
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:
How to visualize the generated PDF in SAPUI5 has been described in the following blog from sharadha.k :
Consume ‘SAP Forms by Adobe’ from SAP UI5 applications
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.
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
Here you have to assign your user (here d<xxxxxx>) the ADSCaller role which entitles this technical user to call the REST API.
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).
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
lv_xml.
cl_web_http_utility=>encode_base64
cl_http_destination_provider=>create_by_cloud_destination
retrieved destination ADS_SRV json payload {"xdpTemplate":"DEMO/TEMPLATE","xmlData":"PEZvcm0+PEZvcm1NYXN0ZXI+PExvZ28xSW1hZ2U+PC9Mb2dvMUltYWdlPjxMb2dvMkltYWdlPjwvTG9nbzJJbWFnZT48TG9nbzNJbWFnZT48L0xvZ28zSW1hZ2U+PFByaW50Rm9ybVRpdGxlVGV4dD5UaXRsZTwvUHJpdCU0VsZW1lbnRJbnRlcm5hbElEPjxXYXJlaG91c2VTdG9yYWdlQmluPjwvV2FyZWhvdXNlU3RvcmFnZUJpbj48L0dJTUk+PC9HSUhlY ... WRlck5vZGU+PC9Gb3JtPg==","formType":"print","formLocale":"de_DE","taggedPdf":"0","embedFont":"0"} {"fileName":"PDFOut.pdf","fileContent":"JVBERi0xLjYNJeLjz9MNCjEyIDAgb2JqCjw8L0ZpbHRlci9GbGF0ZURlY29kZS9GaXJzdCA5L0xlbmd0aCAxNjAvTiAyL1R5cGUvT2JqU3RtPj5zdHJlYW0NCmjePI7NCoMwEIRfZZ8gm2jjD0gObfFSCmK9iRTRpXhJio ... lg374boT0s+zEzO2wKEjSkGqoKL26zARTeltn3mo12wO7zJmzGF3ljjogNZIOHMtp4p3kZz27vpZAQR5daJHkGxUmJohz4cuU4pEe6J hBCTWJ4wLGm14+LGZlA29YB++niWaWvOOsK1fe0Jc+MVYACYnAxKCmVuZHN0cmVhbQplbmRvYmoKc3RhcnR4cmVmCjU5MDcKJSVFT0YK"}
CLASS zcl_demo_ads DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_oo_adt_classrun. PROTECTED SECTION. PRIVATE SECTION. METHODS get_root_exception IMPORTING !ix_exception TYPE REF TO cx_root RETURNING VALUE(rx_root) TYPE REF TO cx_root . ENDCLASS. CLASS zcl_demo_ads IMPLEMENTATION. METHOD if_oo_adt_classrun~main. "Syntax of the URL as described in "Call the REST API" "https://help.sap.com/viewer/6d3eac5a9e3144a7b43932a1078c7628/Cloud/en-US/5d61062ff783453cbbec42f5418fcd14.html "is the following "https://adsrestapiformsprocessing-<yoursubaccount>.<yourregionhost:[xxx.]hana.ondemand.com>/ads.restapi/v1/ 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>| && |</Form>|. DATA(ls_data_xml) = cl_web_http_utility=>encode_base64( lv_xml ). TRY. 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 ). lo_request->append_text( EXPORTING 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( ) ). FIELD-SYMBOLS: <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> ). ENDIF. ENDIF. CATCH cx_root INTO DATA(lx_exception). out->write( 'root exception' ). out->write( get_root_exception( lx_exception )->get_longtext( ) ). ENDTRY. ENDMETHOD. METHOD get_root_exception. rx_root = ix_exception. WHILE rx_root->previous IS BOUND. rx_root ?= rx_root->previous. ENDWHILE. ENDMETHOD. ENDCLASS.
During the setup of this scenario I ran into some problems.
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.
I forgot to provide the query parameter scope=generate-ads-output
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
33 | |
15 | |
15 | |
13 | |
8 | |
8 | |
8 | |
8 | |
7 | |
6 |