Additional Blogs by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
thomas_jung
Developer Advocate
Developer Advocate

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 :smile:

        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=5...!

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

thomas.jung3/blog/2004/07/06/bsp-150-a-developer146s-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.

!https://weblogs.sdn.sap.com/weblogs/images/1918/printerDialog_Attributes.jpg|height=180|alt=image|wi...!</body>

41 Comments