I got an issue to create a PDF form with charts. Those charts are not static of course.

So, we are to use the Adobe LiveCycle Designer and CL_IGS_CHART_ENGINE.

First of all I’ve tried to create a simple report that can create an image file with chart.

The easiest way to create a chart – is to use a functional module PIG_EZ_CHART. It has a very simple interface:


CALL FUNCTION ‘PIG_EZ_CHART’
EXPORTING
*   FORMAT               = 0
*   WIDTH                = 320
*   HEIGHT               = 200
*   USE_DB               = ‘ ‘
*   TYP                  = ‘PIE’
*   TITLE                = ”
*   LEGEND               = ‘DEFAULT’
*   SCHEME               = ‘DEFAULT’
values              
=
*   TITLE_CAT            = ”
*   TITLE_VAL            = ”
* IMPORTING
*   CONTENT_TYPE         =
*   CONTENT_LENGTH       =
*   CONTENT_ID           =
* TABLES
*   CONTENT              =
*   IMAGEMAP             =
.


The only one field we need to pass is values string. It’s a charlike string where lines separated by ‘@.’ and field values separated by ‘@,’.

Here is the simple short code to get a chart image:


DATA: lv_data    TYPE pig_datachar30k,
lt_content
TYPE TABLE OF w3mime,
lv_filename
TYPE string VALUE ‘C:\Temp\MyFirstChart.jpg’.

lv_data = ‘First_line@,Label1@,12@.Second_line@,Label2@,4’.

CALL FUNCTION ‘PIG_EZ_CHART’
EXPORTING
values 
= lv_data
*    typ     = ‘BARS_STACKED’
TABLES
content
= lt_content.

CALL METHOD cl_gui_frontend_services=>gui_download
EXPORTING
filename
= lv_filename
filetype
= ‘BIN’
CHANGING
data_tab
= lt_content
EXCEPTIONS
OTHERS   = 24.

Here we’re trying to get 2 lines somewhere and 2 labels for them with different values. Here is the result:

MyFirstChart.1.jpg

Here is just one label and there are no lines presented! And where are my values?

But you can see I’ve commented one short line with passing parameter ‘TYP’. Here is a list of values for this parameter (INCLUDE LSGRAPHICSTOP):

constants:
* presentation type:
sgma_prestype_trend
(20)           type c value ‘TREND’,

sgma_prestype_lines(20)           type c value ‘LINES’,
sgma_prestype_lines_3d
(20)        type c value ‘LINES_3D’,

sgma_prestype_area(20)            type c value ‘AREA’,
sgma_prestype_area_3d
(20)         type c value ‘AREA_3D’,
sgma_prestype_area_stacked
(20)    type c value ‘AREA_STACKED’,
sgma_prestype_area_stacked_3d
(20) type c value ‘AREA_STACKED_3D’,

sgma_prestype_bars(20)            type c value ‘BARS’,
sgma_prestype_bars_3d
(20)         type c value ‘BARS_3D’,
sgma_prestype_bars_stacked
(20)    type c value ‘BARS_STACKED’,
sgma_prestype_bars_stacked_3d
(20) type c value ‘BARS_STACKED_3D’,

sgma_prestype_cols(20)            type c value ‘COLUMNS’,
sgma_prestype_cols_3d
(20)         type c value ‘COLUMNS_3D’,
sgma_prestype_cols_stacked
(20)    type c value ‘COLUMNS_STACKED’,
sgma_prestype_cols_stacked_3d
(20) type c
value ‘COLUMNS_STACKED_3D’,

Let’s try with
typ     = ‘BARS_STACKED’
Here is a result:

MyFirstChart.jpg

Definitely better!

It’s rather simple. But we have some limitations on influencing customizations. Try to change background color or scales representation. For these purposes we have a Chart Designer (http://www.sdn.sap.com/irj/scn/downloads?rid=/library/uuid/e0a9ba90-0201-0010-d3a2-9cb376b5e181) and also we have a report GRAPHICS_GUI_CE_DEMO that allow creating and saving customizations.

Customization is a XML file. It describes almost all parameters of your chart. So you can create any customization and same it as a XSL-transformation. We will need to change a caption of our chart, so there is a little need to change content of this static xml.

I found that it is good idea to leave data filling as it is. I mean it’s a simple string with all data separated by signs ‘@,’ and ‘@.’ We will have at least one method to get desired chart.

To use CL_IGS_CHART_ENGINE we will need to pass two XML structures:

  1. Customizing structure (set_customizing method)
  2. Data structure (set_data method)

The first structure we can get and pass to Chart Engine by following code:

DATA: m_ixml     TYPE REF TO if_ixml,
m_ixml_sf 
TYPE REF TO if_ixml_stream_factory,
lv_data   
TYPE string,
lref_chart
TYPE REF TO cl_igs_chart_engine,
l_istream 
TYPE REF TO if_ixml_istream,
l_document
TYPE REF TO if_ixml_document.

m_ixml = cl_ixml=>create( ).
m_ixml_sf
= m_ixml->create_stream_factory( ).
*  Call XSL-transformation to get customization
CALL TRANSFORMATION (YOUR_XSL_TRANSFORMATION)
SOURCE caption
= iv_text   “Desired  caption
height 
= iv_height “Desired Height
RESULT XML lv_data
.
*  Prepare XML data for parsing
l_istream
= m_ixml_sf->create_istream_cstring(
string
= lv_data ).
l_document
= m_ixml->create_document( ).
l_parser
= m_ixml->create_parser(
stream_factory
= m_ixml_sf
istream       
= l_istream
document      
= l_document ).
l_result
= l_parser->parse( ).
CHECK l_result IS INITIAL.
*  Create chart engine instance
CREATE OBJECT lref_chart.
*  Pass Customization
lref_chart
->set_customizing( custom_doc = l_document ).


And finally we need to pass data to our chart instance.

As soon as I’ve decided to leave data filling as it is I have to create a method to get structures back from single string:

METHOD get_categories_n_series.
TYPES: BEGIN OF gty_category,
text TYPE string,
END OF gty_category,
BEGIN OF gty_value,
value TYPE dmbtr,
END OF gty_value,
gty_value_tab
TYPE TABLE OF gty_value,
BEGIN OF gty_series,
text   TYPE  string_unicode,
values
TYPE  gty_value_tab,
END OF gty_series.
DATA: lt_lines TYPE TABLE OF string,
lv_category
TYPE string,
lv_xval
TYPE string,
lv_yval
TYPE char100,
lv_rest
TYPE string,
lv_categ
TYPE TABLE OF gty_category.
FIELD-SYMBOLS:
<line>
TYPE string,
<seria>
TYPE gty_series,
<value>
TYPE gty_value.
SPLIT iv_data AT c_line_delimiter INTO TABLE lt_lines.
LOOP AT lt_lines ASSIGNING <line>..
SPLIT <line> AT c_field_delimiter INTO
lv_category lv_xval lv_yval lv_rest
.
lv_categ
text = lv_xval.
COLLECT lv_categ INTO et_cteg.
READ TABLE et_series ASSIGNING <seria>
WITH KEY text = lv_category.
IF sysubrc <> 0.
APPEND INITIAL LINE TO et_series ASSIGNING <seria>.
<seria>
text = lv_category.
ENDIF.
APPEND INITIAL LINE TO <seria>values ASSIGNING <value>.
REPLACE ALL OCCURRENCES OF ‘,’ IN lv_yval WITH ‘.’.
CONDENSE lv_yval.
REPLACE ALL OCCURRENCES OF ‘-‘ IN lv_yval WITH .
<value>value = lv_yval.
ENDLOOP.
ENDMETHOD.

Now we need to pass these data to our chart instance.

l_document = m_ixml->create_document( ).
*  Prepare XML header with encoding
l_encoding
= m_ixml->create_encoding(
byte_order
= if_ixml_encoding=>co_little_endian
character_set
= ‘utf-8’ ).
l_document
->set_encoding( l_encoding ).
*  Create a root element (see documentation for Chart Designer)
l_simplechartdata
= l_document->create_simple_element(
name
= ‘ChartData’ parent = l_document ).
* Create node to fill categories
l_categories
= l_document->create_simple_element(
name
= ‘Categories’ parent = l_simplechartdata ).
* For each category calculated – need to create node
LOOP AT lt_categories ASSIGNING <category_line>.
l_element
= l_document->create_simple_element(
name
= ‘Category’ parent = l_categories ).
l_element
->if_ixml_node~set_value( <category_line>text ).
ENDLOOP.
* It’s for just this task – determine one of two series
lv_first
= abap_true.
LOOP AT lt_series ASSIGNING <seria>.
l_series
= l_document->create_simple_element(
name
= ‘Series’ parent = l_simplechartdata ).
IF lv_first = abap_true.
*    Customizing – is an attribute influencing on apperiance of
*    separeted column or bar or etc.
l_series
->set_attribute( name = ‘customizing’ value = ‘Series1’ ).
CLEAR lv_first.
ELSE.
l_series
->set_attribute( name = ‘customizing’ value = ‘Series2’ ).
ENDIF.
*  Fill name
l_series
->set_attribute( name = ‘label’ value = <seria>text ).

LOOP AT <seria>values ASSIGNING <chart_value>.
*    Create a Point node see documentation for Chart Designer
l_element
= l_document->create_simple_element(
name
= ‘Point’ parent = l_series ).
l_point
= l_document->create_simple_element(
name
= ‘Value’ parent = l_element ).
*    Here we fill just Y value
l_point
->set_attribute( name = ‘type’ value = ‘y’ ).
lv_string
= <chart_value>value.
l_point
->if_ixml_node~set_value( lv_string ).
ENDLOOP.
ENDLOOP.
lref_chart
->set_data( l_document ).
*    Call Chart Engine
CALL METHOD lref_chart->execute
EXCEPTIONS
OTHERS = 1.

IF sysubrc = 0.
CLEAR lt_mime.
CALL METHOD lref_chart->get_image
IMPORTING
image
= lt_mime.
CLEAR lv_result.
*    lv_result type xstring
*    Collect all data into single XSTRING

LOOP AT lt_mime ASSIGNING <mime>.
lv_result
= lv_result && <mime>line.
ENDLOOP.
*    Get STRING from XSTRING to pass into PDF form
CALL FUNCTION ‘SSFC_BASE64_ENCODE’
EXPORTING
bindata                 
= lv_result
IMPORTING
b64data                 
= rv_result
EXCEPTIONS
OTHERS                   = 8.
ENDIF.

Now let’s imagine we’ve completed all duties on charts well. As a result we got a string with image for our PDF. There we’re to create a field of type Image Field with binding to our string calculated.

And we will get something like this:

Без-имени-1.gif

To report this post you need to login first.

17 Comments

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

  1. Klaus Kronawetter

    thanks, but I have a question: how do you make your charts look like in your last picture? When I’m creating bar charts with the ChartDesigner, they look more like this:

    (0) 
      1. Klaus Kronawetter

        Sorry, I think you didn’t understand my question.

        I mean the last picture in your blog entry (the charts with the blue bars) – where did you get the picture from? Is this a SAP chart, made with the CL_IGS_CHART_ENGINE? Or is it a HTML chart? Because it looks completely different than my charts in SAP.

        (0) 
        1. Guryanov Alexandr Post author

          Did you try to use a report GRAPHICS_GUI_CE_DEMO to save a customization? Actually there is a standalone program ChartDesigner but it doesn’t work properly. So the only way I found to customize Charts is that report.

          (0) 
          1. Klaus Kronawetter

            Ah thanks, now I can reproduce your chart.

            What do you mean, the ChartDesigner doesn’t work properly? I always use the ChartDesigner – you just have to save the customizing as an xml document and import the file into the MIME repository, where you can load it from your ABAP program.

            Ich loaded your customizing into the ChartDesigner and it works. I can see and change the customizing from here and export it as xml file again.

            Thanks again!

            (0) 
            1. Guryanov Alexandr Post author

              I’m happy that you achieved desired results. I mean that when I push the button ‘Next’ it crushes down. Perhaps it’s an undocumented feature 🙂

              And of course, you’re welcome!

              Ah yes, ChartDesigner can load and save customizations. So you’re right.

              (0) 
              1. Klaus Kronawetter

                Maybe you should reinstall the ChartDesigner, because (as I mentioned before) it works for me. I can create the whole customizing and save it as xml file. I think your installation is broken…

                (0) 
  2. Ravi Shankar

    Hi,

    My requirment is to add chart in ADOBE form can you please share your full code if it is possible… Because here some code are not fully attached.

    Thanks in advance for your reply.

    Regards,

    Ravi Shankar.L

    (0) 
    1. Guryanov Alexandr Post author

      Can you please let me know which part you need to be explained? What is the main problem to implement this solution into PDF? Is it about creating XSL transformation with customization or it’s about getting xml with data? Or something else? Regards, Alex

      (0) 
      1. Ravi Shankar

        Following are my doubts,

        1. 1. The first structure we can get and pass to Chart Engine by following code:” l_result not declared like class IF_IXML not having the method called “l_result = l_parser->parse( ).”
        2. 2. In “And finally we need to pass data to our chart instance.” Declaration for gty_series->Value field have been determined dynamically not allowing me to declare it.
        3. 3. Please share the code “Now we need to pass these data to our chart instance.” With declarations.
        4. 4. We need to store this image in MIME or in personal desktop.

        I am new in these using classes if anything asked silly means please ignoring it. But your document is the best solution to bring data in PDF 😉


        Regards,

        Ravi Shankar.L

        (0) 
        1. Guryanov Alexandr Post author

          I see 🙂

          Well, we can go from the very end. What do we need? Just a string with a JPG content. That’s why rv_string is:
          DATA: rv_string type string.

          This step is to run the chart engine. It will generate an image:

          CALL METHOD lref_chart->execute
            EXCEPTIONS
              OTHERS = 1.

          IF sysubrc = 0.
          CLEAR lt_mime.


          *Here we’re to get that image

          * lt_mime type W3MIMETABTYPE. “You can learn it from se24->CL_IGS_CHART_ENGINE
          CALL METHOD lref_chart->get_image
            IMPORTING
             image
          = lt_mime.
          CLEAR lv_result.


          *Get desired string as xstring.
          *    lv_result type xstring
          *    Collect all data into single XSTRING

          LOOP AT lt_mime ASSIGNING <mime>.
          lv_result
          = lv_result && <mime>line.
          ENDLOOP.

          And now two points more difficult:

          1. Create XSLT for customization. And send it as a costomization to the Chart engine.

          2. Create xml with our data to transfer values.

          1.

          So the first can be done by downloading desired customization from the Chart Designer. Then you can simply create XSL transformation at t-code strans. You can copy-paste the whole content of that customization into the transformation.

          I gave the whole code for this part in the text. From the line “The first structure we can get and pass to Chart Engine by following code:

          2.

          I decided to store all the data as described in the example: lv_data = ‘First_line@,Label1@,12@.Second_line@,Label2@,4’.

          This allow us using both methods: the simple one and the difficult one. At the same time we can’t transfer this line into the Chart Engine. We have to translate it into the xml format. That’s why I created a method METHOD get_categories_n_series.

          So we’re to pass there a line and to get back tables with categories and series. The method call would look like:

          get_categories_n_series( exporting iv_data = {your line with data}

                                               importing et_cteg = lt_categories

                                                             et_series = lt_series ).

          By the way. Types described in the method are global. So your lt_categories type gty_value_tab and your lt_series type table of gty_series.

          I found a mistake in my method: lv_categ TYPE TABLE OF gty_category. should be

          lv_categ TYPE gty_category.

          Now we have two tables:

          1. Categories texts and

          2. Series with their values

          So we are to compose the xml structure. It’s described after the line “Now we need to pass these data to our chart instance.

          And you asked about types… I went trough the text and found that it may be unreadable 🙂 Sorry. So you can read the main method from the line “The first structure we can get and pass to Chart Engine by following code:

          Then you can skip method get_categories_n_series and continue from the line “Now we need to pass these data to our chart instance.

          That mean you will have:

          1. Chart Engine instance created.

          2. Customization paased there.

          3.

          4. Data transformed into the xml structure.

          You can see that I skipped point 3. Here you are to pass your data line to the method get_categories_n_series

          I hope this will help you! Unfortunatelly I can’t give you the source code as soon as I have no connection to that system.

          Kindest regards

          Alex

          (0) 
        2. Guryanov Alexandr Post author

          To your question about

          “l_result = l_parser->parse( ).”

          You can find:

          l_parser = m_ixml->create_parser(
          stream_factory
          = m_ixml_sf
          istream       
          = l_istream
          document      
          = l_document ).

          So l_parser is:

          data: l_parser type ref to IF_IXML_PARSER.

          BR,

          Alex

          (0) 

Leave a Reply