Skip to Content

Introduction<br />Let’s face it printing from the browser stinks. Add to that fact that a good user interface often hides many elements or only allows for a small number of records in a table to be displayed at a time; thereby making a good printout from the browser nearly impossible. </p><p>To me the obvious solution was to print from the server. I wanted to come up with a reusable approach that would allow me to take an internal table that was being used as the source of a <htmlb:tableView> and output it using ALV Grid. That way I could take advantage of all the rich printing and formatting that ABAP programmers had come to rely on in classical Dynpro development. </p><p>I ended up with a static class method that will process an internal table and using the ALV Grid List Output mode, force the results to the print spooler. In this example I used the new 640 ALV Object Model (because I have been itching for an opportunity to try it out). However since the ALV OM classes are mostly wrappers around the control or Reuse ALV function modules, it should be easy to back port this solution to WebAS 620. </p><p>Naturally though I just didn’t want to output my entire internal table. Quite often I have extra fields used in internal processing that I don’t display in the output. Also maybe I changed the column headers. To support these situations, the static method will also apply a Table View Iterator or Column Definition Table to the data before processing it in the ALV Grid. </p><p>As if this wasn’t enough to chew on, I soon realized that I needed a reusable printer dialog. I needed a way for users to be able to choose which Printer they wanted and other printer settings like Print Immediately or Delete After Output. Luckily I had already developed a BSP Extension Element that I had used in the pass for a printer dialog while outputting Adobe Forms. This extension element will provide the UI elements for querying the printer, number of copies, etc. during server side printing. </p><p>The following is a link to the downloadable code: </p><p>

ServerSidePrintFromBSP.zip

Because I have attached the complete source code, I am not going to list every line of code in this weblog. I will only touch on the more interesting coding points.

PRINT Method Interface

We will start with the coding for the Static Method that performs the output. First we need to have a look at the interface of this method.

CLASS-METHODS print

IMPORTING

!itab TYPE REF TO data

!iterator TYPE REF TO if_htmlb_tableview_iterator

OPTIONAL

!col_def TYPE tableviewcontroltab OPTIONAL

!print_options TYPE sfpprip

!messages TYPE REF TO cl_bsp_messages.

We have five importing parameters. The first is a reference to the data table we want to process. By declaring this as a TYPE REF TO DATA we can receive and process an internal table of any flat structure. Next we have two optional parameters – the iterator and the column definition table. Neither are critical to the overall process, however if we want control over what columns are output or what column headers are used we will want to use one of them. The column definitions table has priority over the iterator.

Next we have a structure in which we will pass in our printer parameters. I used type SFPPRIP because that is what my printer dialog expects (which we will see later in this document). This is the structure for Adobe Forms. If you are not on 640 you might not have this structure. Most of the fields are the same as the SmartForm or List processing options structure, but the names might be different. Later you will see that I map these values to the List options. If you are not on 640 or if you aren’t going to use my printer dialog example as well, you might want to go ahead and defining a different importing structure.

Finally we have a reference to the messages object. This is so any processing errors can be passed back out to the calling routine. Using the CL_BSP_MESSAGES object is not required, you could just as easily pass an error string in and out of the method. I kind of like all the functionality that the Messages object provides however.

PRINT Method Coding

We start our processing by getting a usable handle to our internal table reference. Then you can see some variables we will use later – a reference to the ALV Grid OM class and the variables for list print output. If you didn’t want to pass in SFPPRIP, you might want to just fill a structure of type PRI_PARAMS and pass it in instead.

METHOD print.

****Get a handle on the Internal Table

FIELD-SYMBOLS: TYPE table.

ASSIGN itab->* TO .

****ALV Grid OM Table Class

DATA: table TYPE REF TO cl_salv_table.

****Variables for the List Format Printing Options

DATA: print_parameters TYPE pri_params,

valid_flag(1) TYPE c.

Next I need to map my Input print options structure into the one needed for list processing. You never want to attempt to fill PRI_PARAMS yourself. You should always use the function module GET_PRINT_PARAMETERS.

****Convert the Input Print Parameters (Designed for Adobe Forms)

****Into the ABAP List format

CALL FUNCTION ‘GET_PRINT_PARAMETERS’

EXPORTING

authority = print_options-authority

copies = print_options-copies

cover_page = print_options-cover

data_set = print_options-dataset

department = print_options-division

destination = print_options-dest

expiration = print_options-lifetime

immediately = print_options-reqimm

layout = ‘X_65_255’

list_name = print_options-suffix2

list_text = print_options-covtitle

new_list_id = print_options-reqnew

no_dialog = abap_true

receiver = print_options-receiver

release = print_options-reqdel

IMPORTING

out_parameters = print_parameters

valid = valid_flag

EXCEPTIONS

archive_info_not_found = 1

invalid_print_params = 2

invalid_archive_params = 3

OTHERS = 4.

IF sy-subrc <> 0 OR valid_flag NE abap_true.

messages->add_message2( condition = ‘print’

message = ‘Invalid Print Parameters'(e01) ).

EXIT.

ENDIF.

Even though we are inside of BSP processing, we can still force list output directly to the print spool with the printer options our user selected. To do this we just need a call to NEW-PAGE.

***Start List Processing with our Print Parameters

NEW-PAGE PRINT ON PARAMETERS print_parameters

NO DIALOG.

Next we will create our instance of the ALV OM and get a pointer to the columns object. You will see that while the processing is very different than the pre-640 ALV grid, it has been streamlined. I actually rather like the Object Model.

***Create the ALV Object Model

DATA: salv_msg TYPE REF TO cx_salv_msg.

DATA: error_string TYPE string.

TRY.

cl_salv_table=>factory(

EXPORTING

list_display = abap_true

IMPORTING

r_salv_table = table

CHANGING

t_table = ).

CATCH cx_salv_msg INTO salv_msg.

messages->add_message_from_exception( condition = ‘print’

exception = salv_msg ).

EXIT.

ENDTRY.

****Process the Column Definitions

DATA: columns TYPE REF TO cl_salv_columns_table.

****Get a reference to the columns object and set the optimize width.

columns = table->get_columns( ).

columns->set_optimize( abap_true ).

Next if the consumer of this method has supplied an iterator or table column definition, we need to apply it here by altering the columns object of the ALV OM (for pre-640 this would be the same as generating a field catalog and manipulating it).

DATA: l_col_def TYPE tableviewcontroltab.

DATA: iterator_error TYPE REF TO cx_sy_dyn_call_illegal_method.

****We have an iterator Class –

****Process it and get a columun defintion table

IF col_def IS INITIAL AND

iterator IS NOT INITIAL.

DATA: p_overwrites TYPE tableviewoverwritetab.

TRY.

iterator->get_column_definitions(

EXPORTING

p_tableview_id = ‘itab’

CHANGING

p_column_definitions = l_col_def

p_overwrites = p_overwrites ).

CATCH cx_sy_dyn_call_illegal_method INTO iterator_error.

messages->add_message_from_exception( condition = ‘print’

exception = iterator_error ).

EXIT.

ENDTRY.

*****User supplied a column definition table directly – us it

ELSEIF col_def IS NOT INITIAL.

l_col_def = col_def.

ENDIF.

****Adjust our column definition (otherwise know as Field Catalog)

****by the Iterator or Column Dfinition table.

IF l_col_def IS NOT INITIAL.

DATA: scrtext_l TYPE scrtext_l,

scrtext_m TYPE scrtext_m,

scrtext_s TYPE scrtext_s,

tooltip TYPE lvc_tip.

DATA: col TYPE salv_t_column_ref.

FIELD-SYMBOLS: LIKE LINE OF col,

LIKE LINE OF l_col_def.

****Get a listing of all columns

col = columns->get( ).

****Loop through all the columsn

LOOP AT col ASSIGNING .

****Read to see if the current columns is in our Iterator

READ TABLE l_col_def ASSIGNING

WITH KEY columnname = -columnname.

IF sy-subrc = 0.

****Field is in the iterator – set it visible.

-r_column->set_visible( abap_true ).

****Is there an override title in the Iterator – if yes

****use it for all the column headers of the output

IF -title IS NOT INITIAL.

scrtext_l = -title.

scrtext_m = -title.

scrtext_s = -title.

-r_column->set_long_text( scrtext_l ).

-r_column->set_medium_text( scrtext_m ).

-r_column->

set_short_text( scrtext_s ).

        ENDIF.

****Is there an override tooltip in the Interator – if yes

****ues it for the tooltip of the output (actually meaningless

****for only printing – but hey you never know what you might

****use this for in the future 🙂

        IF -tooltipheader IS NOT INITIAL.

          tooltip = -tooltipheader.

          -r_column->set_tooltip( tooltip ).

        ENDIF.

      ELSE.

****Field is not in the Iterator – Hide it in the output

        -r_column->set_visible( abap_false ).

      ENDIF.

    ENDLOOP.

  ENDIF.

Finally we close out the processing by forcing the ALV to produce its output and then closing the list processing.

****Set the ALV to display (forces printing in the Dark

        • = background or BSP)

  table->display( ).

  NEW-PAGE PRINT OFF.

  messages->add_message2( condition   = ‘print’

                          message     = ‘Print Output is complete'(i01)

                          messagetype = ‘I’ ).

ENDMETHOD.

The following is an image that shows what our completed dialog will look like.

!https://weblogs.sdn.sap.com/weblogs/images/1918/AdobeBSPPrintDialog.jpg|height=379|alt=image|width=553|src=https://weblogs.sdn.sap.com/weblogs/images/1918/AdobeBSPPrintDialog.jpg|border=0!

I will say up front that the coding supplied here isn’t really a complete solution as much as a starting point. I have shared the structure and rendering of the UI element in order to save you time incase you want to implement something similar. You could just render these elements in line in an existing application. In that case you could just about use all the coding as is. You might want instead to open this dialog in another window. I have not supplied any code however for passing the selected values back to the original window. I have designed my element to be displayed in a modal window using floating iframes. This keeps the browser from treating the area as another window and I can share my model with the results directly via a stateful application. For this reason you will find a small block of JavaScript code toward the end of the element processing.

DATA: javascript_close TYPE string.

CONCATENATE me->id ‘_Close’ INTO javascript_close.

DATA: closedialog TYPE REF TO zcl_es_bsp_elmnt_close_dialog.

closedialog ?= zcl_es_bsp_elmnt_close_dialog=>factory(

clientevent = javascript_close ).

WHILE m_page_context->element_process( element = closedialog ) = co_element_continue.

ENDWHILE. “End Close Dialog Render

CONCATENATE javascript_close ‘();’ INTO javascript_close.

DATA: button TYPE REF TO cl_htmlb_button.

button ?= cl_htmlb_button=>factory(

id = me->id

id_postfix = ‘__PrintBtn’

onclientclick = javascript_close

text = ‘Print'(p01) ).

Most likely you can just ignore this section of code that hooks the JavaScript function into the close button since it is specific to the dialog window processing that I am not able to share. Or perhaps this is where you can put your own logic to close your processing area.

If you have never attempted to create your own BSP Extension. I would suggest that you read the following weblogs first. They will introduce many of the concepts (such as redirecting the button event into our own handler class) that you will find in this element’s coding.

BSP In-Depth: Using the HTMLB Event System

BSP Programming: Writing Composite Elements

BSP Programming: Handling HTMLB Events

BSP Programming: Event Handling in Composite Elements

BSP – a Developer’s Journal: Part VI – Example application with customer BSP Extensions and Design 2003 themes

Creating a BSP Extension for Downloading a Table

You might also notice that I use my F1 label (as see in weblog: Custom BSP Extension Element for Field Level Help  (Custom BSP Extension Element for Field Level Help)). If you don’t want to implement this custom extension as well, just adjust the calls to zcl_es_bsp_elmnt_f1_help_lbl with cl_htmlb_label. I also have a routine called zcl_es_abap_utilities=>read_field_desc. I use this routine in case you didn’t use data binding. This static method will look up the language depended descriptions from the data dictionary. It isn’t terribly impressive, but it is nice little space saver.

method read_field_desc .

*@78QImporting@ FIELD TYPE ANY

*@7BQReturning@ VALUE( DESC ) TYPE SCRTEXT_M Medium field label

data: el_desc type ref to cl_abap_elemdescr,

isddic type abap_bool,

field_d type dfies.

try.

el_desc ?= cl_abap_typedescr=>describe_by_data( field ).

isddic = el_desc->is_ddic_type( ).

check isddic = abap_true.

field_d = el_desc->get_ddic_field( ).

desc = field_d-scrtext_m.

****Catch all Global Execptions – like bad casts

catch cx_root. “#EC CATCH_ALL

endtry.

endmethod.

Printer Dialog Attributes

The follow are a few screen shots that show the attributes and properties for the printer dialog element.

image

!https://weblogs.sdn.sap.com/weblogs/images/1918/printerDialog_Attributes.jpg|height=180|alt=image|width=558|src=https://weblogs.sdn.sap.com/weblogs/images/1918/printerDialog_Attributes.jpg|border=0!</body>

To report this post you need to login first.

41 Comments

You must be Logged on to comment or reply to a post.

    1. Thomas Jung Post author
      I seem to be unable to download it as well at the moment. The content is hosted by SDN, so I will a message for a support request.
      (0) 
        1. Remi Kaimal B
          I am able to download, the code.
          However couldn’t find the code for the z classes : zcl_es_bsp_elmnt_f1_help_lbl &
          zcl_es_abap_utilities

          used by the class, ZCL_ES_BSP_ELMNT_PRINTER_DIAG
          in IF_BSP_ELEMENT~DO_AT_BEGINNING.

          Regards,
          Remi

          (0) 
          1. Thomas Jung Post author
            Have a look at the last paragraph of the PRINT Method Coding section of the blog.  It contains the link to the F1 element blog, instructions on how to adjust the solution if you don’t want to use that element, and all the coding that you need from ZCL_ES_ABAP_UTILITIES.
            (0) 
            1. Remi Kaimal B
              Thank you for the prompt reply.

              I have replaced the use of zcl_es_bsp_elmnt_f1_help_lbl
              with cl_htmlb_label
              and commented the use of zcl_es_abap_utilities
              Model is of type IF_BSP_MODEL_BINDING

              Now, i get an exception while testing my bsp application :
              An exception with the type CX_SY_REF_IS_INITIAL occurred, but was neither handled locally, nor declared in a RAISING clause

              What could be the possible cause of this.
              Thanks and regards,
              Remi

              (0) 
              1. Thomas Jung Post author
                You would have to tell me where in the code the exception is being thrown if I am to have any hope of helping you with the error.

                The error in general means that the you have an object reference that has been created.  That could be caused for many reasons.

                (0) 
                1. Remi Kaimal B
                  Sorry for haven missed out that :
                  The exception is thrown in the methodn RESOLVE_MODEL_BINDING
                  at line :
                  table = m_table_model->get_attribute_data_ref( attribute_path = m_table_path ).
                  Regards,
                  Remi
                  (0) 
                    1. Vinay Baji
                      Hi,

                      I am facing an error of similar type when printing,
                      the error is “an exception with the type CX_SY_REF_IS_INITIAL occurred, but was neither handled locally, not declared in a RAISING clause”

                      the method is _ONINITIALIZATION, te line is no.8
                      can you please assist me on this

                      (0) 
                    2. Vinay Baji
                      Hi, I am facing a similar issue, when I am trying to print, I get an error saying
                      “”an exception with the type CX_SY_REF_IS_INITIAL occurred, but was neither handled Locally, nor declared in a RAISING class”.

                      The error appears in _ONINITIALIZATION method line number 8.
                      Please can you assist

                      (0) 
                      1. Thomas Jung Post author
                        When the OnInitialization event isn’t used in the code sample from this blog.  So this error would seem to be coming from your code. I can’t possibly say what is wrong since I have no idea what code you have in that event in your application.
                        (0) 
  1. Faaiez Sallie
    Thanks Thomas. I always enjoy your weblogs. One thing I’d like to know though is how do you upload and download a class’s source code? I’ve searched SDN for the answer to this question without any success. Thanks again.
    (0) 
    1. Thomas Jung Post author
      To download the classes I use the ABAP program BSP_EXPORT_CLASSES.  Although it has BSP in the name, it is not specific to BSP and can export any ABAP Class.

      Unfortunetly importing isn’t quite as easy. There isn’t any one program that will just import from this file and create the complete class for you.  However in 6.40 and higher you can start by creating just the class manually. You can then use the menu GOTO->Public Section (or Protected or Private).  From here you can cut and paste in all of your attributes, methods and method parameters from the export file.  This is a huge time saver if you have a lot of methods with complex parameters. In the next major release of Netweaver, this gets even easier as there is a new source code based Class Editor.  This makes the complete import of the class file possible.

      (0) 
  2. Ricky Orea
    Hi Thomas,

    Sorry for this question, I’m just trying to familirize myself more with BSP so please pardon my ignorance.

    I’m trying to pass an internal table to the print method but I get an error message saying that it’s not type compatible with the formal parameter “ITAB”.

    Is there something I need to do with my internal table before I pass it to the print method?

    Thank you for your help,
    Ricky Orea

    (0) 
    1. Thomas Jung Post author
      Yes – Itab is generically types.  Look at the interface definition.  It is type ref to data.  Therefore you must take you specific internal table and only pass in a data reference.

      Use the syntax – GET REFERENCE OF to do so.

      Example:
      data itab type standard table of sflight.
      data rtab type ref to data.
      get reference of itab into rtab.

      (0) 
      1. Ricky Orea
        Hi Thomas,

        Thanks for the help and prompt reply, I still could not call the method successfully, the erorr now is in the messages parameter, here’s my code:

        —————————————
        data rtab type ref to data.
        get reference of t_output into rtab.
        data i_messages type ref to cl_bsp_messages.

        zcl_es_bsp_print_table=>print(
          itab = rtab
           print_options = me->print_options
           messages = i_messages ).
        ———————————

        I get an error message saying “An attempt was made to execute a dynamic method callon an initial(NULL-) object reference. The reference must refer to an object.”

        This error is encountered in the print method in the following lines of code:

        messages->add_message2( condition   = ‘print’
                                message     = ‘Print Output is complete'(i01)
                                  messagetype = ‘I’ ).

        ——————–

        Any idea?

        Thanks again.

        (0) 
        1. Thomas Jung Post author
          Your passing in a null message object, which then triggers a dump when the method coding tries to use that object.  You need to pass in the message object from your BSP application.  The messages object is always an attribute of the page object and it is available directly within the controller as well.  The idea is that the print method can write messages directly back into the calling application.
          (0) 
            1. Thomas Jung Post author
              >How do you reference the message object attribute of the page object?

              page->MESSAGES

              That is all you need if this called from the event handler of a FlowLogic Page.  The page object is already exposed to the event handler (just view the signature).

              If you are in a controller class then it is even easier as each controller already has an attribute called MESSAGES and that is what you would use.

              (0) 
  3. Ricky Orea
    Hi Thomas,

    Why is the text in the column title getting truncated? I’m passing an iterator to to the Print Method for the server side printing but when I looked at the printout all the column titles are cut-off and only the first 10 characters are displayed.

    Please help.

    Thanks,
    Ricky

    (0) 
    1. Thomas Jung Post author
      Have you checked that when you set the column definitions that the complete column title is present?  Also how many columns do you have?
      (0) 
        1. Ricky Orea

          oops, sorry i pasted the wrong code for the first column, here’s the correct one:-title      = ‘Org Unit Name/EE Name’.

          (0) 
        2. Thomas Jung Post author
          I don’t see anything wrong with what you are doing.  I would have to try and recreate the problem in my system.  I have travel planned for this week and next, so I can’t really say when I will get a chance.  Basically though I don’t see any reason why your columns would be getting cut-off.
          (0) 
  4. Nitesh Jain
    Dear Thomas,

    I am unable to download the attached ZIP file ServerSidePrintFromBSP.zip ..

    Got in touch with SDN support and they recommended to get in touch with you. Kindly update the link to this file so that i can download it.

    regards
    Nitesh

    (0) 
    1. Thomas Jung Post author
      Please forward the email you received from SDN support to me.  My email address is in my business card as well as it follows the SAP standard (firstname.lastname@sap.com). 

      The content is hosted by SDN and I have no control over that hosting.  I have no idea why the link stopped working or how I am supposed to know what I can change it to.

      (0) 
      1. Thomas Jung Post author
        I was informed by SDN support that this file was once hosted within the SDN content repositories but can no longer be found.  I was told that given the age of the file (pre 2006) that this was not a suprise.

        Since the stored content has been lost, I will have to recreate the example and submit a new version of the code to SDN for hosting.  This process will probably take a few days. Sorry for the inconvienence.

        (0) 
  5. Prasanth Kasturi

    Hi Thomas,

     

    Thanks for the blog.

     

    I uploaded the source code and I have a class and a structure.

     

    I am not sure how to make use of the class and do my printing.My BSP page uses windows.print() function to print my page. must I call the Class on click of my button.

     

    Can you please explain how to make use of the class.

     

    Regards

    (0) 
    1. Thomas Jung Post author

      The class is called from a server side event.  So you need a button that triggers a server side event handler.  You can call this class from inside that event handler like any other ABAP class.

      (0) 
      1. Prasanth Kasturi

        Hi Thomas,

         

        Thank you for the reply.

         

        My table structure is a deep structure. By using the method RENDER_CELL_START method the structure is decided at the run time.

         

        My table has columns which inturn display a table .

         

        Can you please guid me how to proceed in this situation.

         

        Regards

        Prasanth Kasturi

        (0) 
        1. Thomas Jung Post author

          This approach uses an ALV to print the table.  The ALV will not display or print a nested table structure.  In fact I don’t know of any standard technology which will display or print a nested table. Even the hiearchy ALV isn’t really nested, but two tables with a join relationship. You would have to custom build the output.

          (0) 
            1. Thomas Jung Post author

              What do you mean by “whole BSP Output”? Do you mean the HTML rendering output?  If so, no.  That’s HTML content which the print spooler wouldn’t understand.

              (0) 
              1. Prasanth Kasturi

                Hi Thomas,

                 

                Thanks for your reply .

                 

                Could you please help me in this scenario, Any suggestion or steps will be of great help to me in this scenario.

                 

                My BSP page displays TABLE data. The table data is dynamic and rendered at run time. Two coulmns of our output table will again display a table. This is done using BEE concept in the RENDER_CELL_START method.

                 

                My print function is currently using the method windows.print( ) and output is getting truncated, if we change the Page Setup it is working fine. is there any chance of changing the Page Setup from SAP , like the PAGE ( A3, A4 etc) and Orientation (Landscape/Potrait).

                 

                I posted in the forum but there is no reply .

                 

                Thanks in advance and Wish you a happy and Prosperous New year

                 

                Regards.

                Prasanth

                (0) 
                1. Thomas Jung Post author

                  If such a thing were possible, it would have to be done with JavaScript and not anything that BSP provides. I’m afraid I don’t know of a way to do it however. I’d suggest searching the internet for general JavaScript examples to acomplish this as it wouldn’t be an SAP specific solution.

                  (0) 

Leave a Reply