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>
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.
!itab TYPE REF TO data
!iterator TYPE REF TO if_htmlb_tableview_iterator
!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.
****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’
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
out_parameters = print_parameters
valid = valid_flag
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) ).
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
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.
list_display = abap_true
r_salv_table = table
t_table = ).
CATCH cx_salv_msg INTO salv_msg.
messages->add_message_from_exception( condition = ‘print’
exception = salv_msg ).
****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.
p_tableview_id = ‘itab’
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 ).
*****User supplied a column definition table directly – us it
ELSEIF col_def IS NOT INITIAL.
l_col_def = col_def.
****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 ).
set_short_text( scrtext_s ).
****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 ).
****Field is not in the Iterator – Hide it in the output
-r_column->set_visible( abap_false ).
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)
NEW-PAGE PRINT OFF.
messages->add_message2( condition = ‘print’
message = ‘Print Output is complete'(i01)
messagetype = ‘I’ ).
The following is an image that shows what our completed dialog will look like.
DATA: closedialog TYPE REF TO zcl_es_bsp_elmnt_close_dialog.
closedialog ?= zcl_es_bsp_elmnt_close_dialog=>factory(
WHILE m_page_context->element_process( element = closedialog ) = co_element_continue.
ENDWHILE. “End Close Dialog Render
DATA: button TYPE REF TO cl_htmlb_button.
button ?= cl_htmlb_button=>factory(
id = me->id
id_postfix = ‘__PrintBtn’
text = ‘Print'(p01) ).
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.
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.
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
Printer Dialog Attributes
The follow are a few screen shots that show the attributes and properties for the printer dialog element.