Текущее время: Пн, июл 16 2018, 16:37

Часовой пояс: UTC + 4 часа


Правила форума


ВНИМАНИЕ!

Вопросы по SAP Query и Quick View - сюда



Начать новую тему Ответить на тему  [ Сообщений: 8 ] 
Автор Сообщение
 Заголовок сообщения: Выкладываю парсер PDF (класс для чтения файла PDF напрямую)
СообщениеДобавлено: Сб, июл 18 2009, 18:42 
Специалист
Специалист
Аватара пользователя

Зарегистрирован:
Вт, июн 02 2009, 23:28
Сообщения: 226
Откуда: MOW
Пол: Мужской
Понадобилось автоматически загружать в SAP информацию из счета-фактуры. Но поставщик присылает счета только в формате PDF.
Поэтому пришлось написать небольшой парсер PDF.
Естественно, можно использовать только для файлов, содержащих текст (иногда в PDF бывает просто картинка, тогда без вариантов).

Прилагаю исходный код парсера в виде отдельного инклуда (оформлен в виде класса).
Также прилагаю небольшой пример для демонстрации возможностей (к сожалению, без файла PDF).

Принцип парсера заключается в том, что он на каждой странице файла PDF считывает все текстовые элементы: их текст, их координаты на странице, а также шрифт. Все это записывается во внутреннюю таблицу элементов ( с помощью внутреннего метода parse ).
Для удобства работы с данной таблицей предоставляется ряд функций: поиск текста, поиск текста по координатам, поиск текста под другим текстом и т.д.

Код самого парсера:
Code:
*&---------------------------------------------------------------------*
*&  Include           ZMM_PDF_PARSER
*&---------------------------------------------------------------------*

* Текстовый элемент, загруженный из PDF
TYPES: BEGIN OF t_text_element,
         num_element   TYPE I,
         num_page      TYPE I,
         X             TYPE I,
         Y             TYPE I,
         font(20)      TYPE C,
         text          TYPE STRING,
         textUcase     TYPE STRING,
       END OF t_text_element.

TYPES: tt_text_element TYPE t_text_element OCCURS 0.

* Класс для парсинга PDF (разделение и структурирование его текстовых элементов) и
* удобной работы с данными элементами
CLASS lcl_pdf_parser DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: try_read_number IMPORTING
                                     pi_number TYPE ANY
                                   CHANGING
                                     pc_number TYPE ANY
                                   EXCEPTIONS
                                     INVALID_FORMAT.
    METHODS: load_pdf_from_server IMPORTING
                                    pi_filename TYPE C
                                  EXCEPTIONS
                                    ERROR_IN_LOADING
                                    ERROR_IN_PARSING,
             load_pdf_from_gui    IMPORTING
                                    pi_filename TYPE C
                                  EXCEPTIONS
                                    ERROR_IN_LOADING
                                    ERROR_IN_PARSING,
             load_pdf_from_itab   IMPORTING
                                    pi_it_filedata   TYPE STANDARD TABLE
                                  EXCEPTIONS
                                    ERROR_IN_PARSING,
             is_loaded EXPORTING
                         pe_loaded TYPE C,
             get_num_pages EXPORTING
                             pe_num_pages TYPE I,
             get_all_elements EXPORTING
                                pe_elements TYPE tt_text_element,
             find_text IMPORTING
                         pi_num_page   TYPE I DEFAULT 1
                         pi_text       TYPE C
                         pi_match_case TYPE C DEFAULT SPACE
                       EXPORTING
                         pe_elements   TYPE tt_text_element,
             find_text_below IMPORTING
                               pi_element   TYPE t_text_element
                               pi_accuracy  TYPE I
                             EXPORTING
                               pe_elements  TYPE tt_text_element,
             find_text_right IMPORTING
                               pi_element   TYPE t_text_element
                               pi_accuracy  TYPE I
                             EXPORTING
                               pe_elements  TYPE tt_text_element,
             find_text_in_box IMPORTING
                                pi_num_page TYPE I
                                pi_left     TYPE I
                                pi_top      TYPE I
                                pi_right    TYPE I
                                pi_bottom   TYPE I
                              EXPORTING
                                pe_elements TYPE tt_text_element.

  PRIVATE SECTION.
    DATA: mt_filelines      TYPE TABLE OF STRING,
          mt_elements       TYPE TABLE OF t_text_element,
          m_num_pages       TYPE I.

    METHODS: parse.
ENDCLASS.

CLASS lcl_pdf_parser IMPLEMENTATION.

  " Загружает файл PDF из папки на сервере
  METHOD load_pdf_from_server.
    DATA: BEGIN OF lwa_xline,
            XLINE(3000) TYPE X,
          END OF lwa_xline.
    DATA: lh_all_file TYPE STRING,
          lh_len   TYPE I,
          lh_len1  TYPE I,
          lh_line(3000) TYPE C.
    DATA: c_conv TYPE REF TO CL_ABAP_CONV_IN_CE.

    " Загружаем

    OPEN DATASET pi_filename FOR INPUT IN BINARY MODE.
    IF sy-subrc <> 0.
      "PERFORM log_msg USING '' '' 'ZCAT' c_error c_important '402' pi_filename '' '' ''.
      RAISE ERROR_IN_LOADING.
    ENDIF.

    DO.
      CLEAR lh_len.
      READ DATASET pi_filename INTO lwa_xline-xline ACTUAL LENGTH lh_len.
      IF lh_len > 0.
        c_conv = cl_abap_conv_in_ce=>create( input       = lwa_xline-xline
                                             replacement = space
                                             encoding    = '1504' ).
        c_conv->read( EXPORTING n = lh_len IMPORTING data = lh_line len = lh_len1 ).
        CONCATENATE lh_all_file lh_line INTO lh_all_file RESPECTING BLANKS.
      ENDIF.
      IF sy-subrc <> 0.
        EXIT.
      ENDIF.
    ENDDO.

    FREE c_conv.

    REFRESH mt_filelines.
    SPLIT lh_all_file AT CL_ABAP_CHAR_UTILITIES=>CR_LF+1(1) INTO TABLE mt_filelines.
    CLOSE DATASET pi_filename.

    " Парсим
    CALL METHOD parse( ).

    IF mt_elements[] IS INITIAL.
      REFRESH mt_filelines[].
      RAISE ERROR_IN_PARSING.
    ENDIF.
  ENDMETHOD.     "    METHOD load_pdf_from_server.

  " Загружает файл PDF в GUI
  METHOD load_pdf_from_gui.

    " Загружаем

    REFRESH mt_filelines[].

    CALL FUNCTION 'GUI_UPLOAD'
      EXPORTING
        filename                = pi_filename
      TABLES
        data_tab                = mt_filelines[]
      EXCEPTIONS
        file_open_error         = 1
        file_read_error         = 2
        no_batch                = 3
        gui_refuse_filetransfer = 4
        invalid_type            = 5
        no_authority            = 6
        unknown_error           = 7
        bad_data_format         = 8
        header_not_allowed      = 9
        separator_not_allowed   = 10
        header_too_long         = 11
        unknown_dp_error        = 12
        access_denied           = 13
        dp_out_of_memory        = 14
        disk_full               = 15
        dp_timeout              = 16
        OTHERS                  = 17.

    IF sy-subrc <> 0.
      RAISE ERROR_IN_LOADING.
    ENDIF.

    " Парсим
    CALL METHOD parse( ).

    IF mt_elements[] IS INITIAL.
      REFRESH mt_filelines[].
      RAISE ERROR_IN_PARSING.
    ENDIF.
  ENDMETHOD.     "    METHOD load_pdf_from_gui.

  " Загружает файл PDF, предварительно загруженный во внутреннюю таблицу
  METHOD load_pdf_from_itab.
    REFRESH mt_filelines[].

    mt_filelines[] = pi_it_filedata[].

    IF mt_filelines[] IS INITIAL.
      RAISE ERROR_IN_PARSING.
    ENDIF.

    " Парсим
    CALL METHOD parse( ).

    IF mt_elements[] IS INITIAL.
      REFRESH mt_filelines[].
      RAISE ERROR_IN_PARSING.
    ENDIF.
  ENDMETHOD.   "  load_pdf_from_itab

  " Выполняет парсинг загруженного PDF
  METHOD parse.

    DATA: lit_str            TYPE TABLE OF STRING,
          lh_str             TYPE STRING,
          lwa_element        TYPE t_text_element,
          lh_line            TYPE STRING,
          lh_in_obj(1)       TYPE C,
          lh_in_stream(1)    TYPE C,
          lh_in_text(1)      TYPE C,
          lh_font            TYPE STRING,
          lh_X               TYPE I,
          lh_Y               TYPE I,
          lh_len             TYPE I,
          lh_num             TYPE I.

    CHECK mt_filelines[] IS NOT INITIAL.

    REFRESH mt_elements[].

    " Проверяем, что это действительно PDF - это должно быть написано в начале файла
    READ TABLE mt_filelines INDEX 1 INTO lh_line.
    IF lh_line(4) <> '%PDF'.
      RETURN.
    ENDIF.

    m_num_pages = 1.

    LOOP AT mt_filelines INTO lh_line.
      CONDENSE lh_line.
      SPLIT lh_line AT SPACE INTO TABLE lit_str.

      " Если мы не внутри блока текста, отслеживаем его начало
      IF lh_in_obj = 'X' AND lh_in_stream = 'X' AND lh_in_text <> 'X'
        AND lh_line = 'BT'.

        lh_in_text = 'X'.
        CLEAR: lh_font, lh_X, lh_Y.
      ENDIF.

      " Если мы не внутри stream, отслеживаем его начало
      IF lh_in_obj = 'X' AND lh_in_stream <> 'X' AND lh_line = 'stream'.
        lh_in_stream = 'X'.
      ENDIF.

      " Если мы не внутри объекта, отслеживаем начало объекта
      IF lh_in_obj <> 'X'.
        IF LINES( lit_str ) >= 3.
          READ TABLE lit_str INDEX 3 INTO lh_str.
          IF lh_str = 'obj'.
            lh_in_obj = 'X'.
          ENDIF.
        ENDIF.
      ENDIF.

      " Отслеживаем начало новой страницы
      IF lh_in_obj = 'X' AND lh_line = '<< /Type /Page'.
        m_num_pages = m_num_pages + 1.
        CLEAR: lh_X, lh_Y.
      ENDIF.

      " Отслеживаем тип строки текста по последним 2 символам и считываем
      IF lh_in_obj = 'X' AND lh_in_stream = 'X' AND lh_in_text = 'X'.
        lh_str = lh_line.
        lh_len = strlen( lh_str ).
        IF lh_len >= 2.
          IF lh_len > 2.
            lh_len = lh_len - 2.
            SHIFT lh_str BY lh_len PLACES LEFT.
          ENDIF.

          CASE lh_str.
            WHEN 'Tf'.   " Шрифт        [ /F410130 8 Tf
              READ TABLE lit_str INDEX 1 INTO lh_font.
            WHEN 'Td'.   " Позиционирование        [ -499 63 Td

              " X
              READ TABLE lit_str INDEX 1 INTO lh_str.
              CALL METHOD try_read_number EXPORTING
                                            pi_number = lh_str
                                          CHANGING
                                            pc_number = lh_num
                                          EXCEPTIONS
                                            INVALID_FORMAT = 1.
              IF sy-subrc = 0.
                lh_X = lh_X + lh_num.
              ENDIF.

              " Y
              READ TABLE lit_str INDEX 2 INTO lh_str.
              CALL METHOD try_read_number EXPORTING
                                            pi_number = lh_str
                                          CHANGING
                                            pc_number = lh_num
                                          EXCEPTIONS
                                            INVALID_FORMAT = 1.
              IF sy-subrc = 0.
                lh_Y = lh_Y + lh_num.
              ENDIF.

            WHEN 'Tj'.   " Текст        [ (AGCO Parts Division) Tj

              lh_str = lh_line.
              SHIFT lh_str BY 1 PLACES LEFT.
              lh_len = strlen( lh_str ).
              lh_len = lh_len - 4.
              lh_str = lh_str(lh_len).
              "SHIFT lh_str RIGHT DELETING TRAILING ') Tj'.
              "SHIFT lh_str BY 4 PLACES LEFT.

              " Добавляем текстовый элемент
              lwa_element-num_page  = m_num_pages.
              lwa_element-X         = lh_X.
              lwa_element-Y         = lh_Y.
              lwa_element-font      = lh_font.
              lwa_element-text      = lh_str.
              lwa_element-textUcase = lwa_element-text.
              TRANSLATE lwa_element-textUcase TO UPPER CASE.
              APPEND lwa_element TO mt_elements.
          ENDCASE.
        ENDIF.
      ENDIF.

      " Если мы внутри блока текста, то отслеживаем его окончание
      IF lh_in_obj = 'X' AND lh_in_stream = 'X' AND lh_in_text = 'X'
        AND lh_line = 'ET'.

        CLEAR lh_in_text.
      ENDIF.

      " Если мы внутри stream, отслеживаем ее окончание
      IF lh_in_obj = 'X' AND lh_in_stream = 'X' AND lh_in_text <> 'X'
        AND lh_line = 'endstream'.

        CLEAR lh_in_stream.
      ENDIF.

      " Если мы внутри объекта, отслеживаем окончание объекта
      IF lh_in_obj = 'X' AND lh_in_stream <> 'X' AND lh_in_text <> 'X'
        AND lh_line = 'endobj'.

        CLEAR lh_in_obj.
      ENDIF.
    ENDLOOP.

    " Сортируем, чтобы все текстовые надписи шли сверху вниз и слева направо
    SORT mt_elements BY num_page y DESCENDING x.

    LOOP AT mt_elements INTO lwa_element.
      lwa_element-num_element = sy-tabix.
      MODIFY mt_elements FROM lwa_element.
    ENDLOOP.

    m_num_pages = m_num_pages - 1.

    IF mt_elements[] IS INITIAL.
      m_num_pages = 0.
    ENDIF.
  ENDMETHOD.    "   METHOD parse

  " Читает число из строки, если неверный формат, то генерит exception
  METHOD try_read_number.
    DATA: lh_number  TYPE STRING.

    lh_number = pi_number.
    REPLACE ',' IN lh_number WITH '.'.

    CATCH SYSTEM-EXCEPTIONS
      CONVERSION_ERRORS = 1.

      pc_number = lh_number.
    ENDCATCH.
    IF sy-subrc <> 0.
      RAISE INVALID_FORMAT.
    ENDIF.
  ENDMETHOD.       "    try_read_number

  " Возвращает X если PDF загружен и пусто в противном случае
  METHOD is_loaded.
    IF mt_elements[] IS NOT INITIAL.
      pe_loaded = 'X'.
    ELSE.
      CLEAR pe_loaded.
    ENDIF.
  ENDMETHOD.       "    is_loaded

  " Возвращает количество страниц в документе
  METHOD get_num_pages.
    pe_num_pages = m_num_pages.
  ENDMETHOD.     "    get_num_pages

  " Возвращает список всех элементов, отсортированный по страницам, сверху вниз и
  " слева направо
  METHOD get_all_elements.
    pe_elements = mt_elements.
  ENDMETHOD.        "   get_all_elements

  " Возвращает элементы, содержащие определенный текст
  "  pi_num_page   - номер страницы, если 0, то искать на всех страницах;
  "  pi_text       - искомый текст;
  "  pi_match_case - учитывать ли большие-маленькие буквы или искать невзирая на них;
  "  pe_elements   - возвращает список найденных текстовых элементов или пустую таблицу,
  "                  если такой текст не найден.
  METHOD find_text.
    DATA: lr_page            TYPE RANGE OF t_text_element-num_page,
          lwa_r_page         LIKE LINE OF lr_page,
          lr_text            TYPE RANGE OF t_text_element-text,
          lwa_r_text         LIKE LINE OF lr_text,
          lr_textUcase       TYPE RANGE OF t_text_element-textUcase,
          lwa_r_textUcase    LIKE LINE OF lr_textUcase,
          lwa_element        TYPE t_text_element,
          lh_text            TYPE STRING.

    REFRESH pe_elements[].

    IF pi_num_page IS NOT INITIAL.
      lwa_r_page-option = 'EQ'.
      lwa_r_page-sign   = 'I'.
      lwa_r_page-low    = pi_num_page.
      APPEND lwa_r_page TO lr_page.
    ENDIF.

    IF pi_text IS NOT INITIAL.
      IF pi_match_case = 'X'.
        lwa_r_text-option = 'EQ'.
        lwa_r_text-sign   = 'I'.
        lwa_r_text-low    = pi_text.
        APPEND lwa_r_text TO lr_text.
      ELSE.
        lh_text = pi_text.
        TRANSLATE lh_text TO UPPER CASE.

        lwa_r_textUcase-option = 'EQ'.
        lwa_r_textUcase-sign   = 'I'.
        lwa_r_textUcase-low    = lh_text.
        APPEND lwa_r_textUcase TO lr_textUcase.
      ENDIF.
    ENDIF.

    LOOP AT mt_elements INTO lwa_element
      WHERE num_page   IN lr_page
        AND text       IN lr_text
        AND textUcase  IN lr_textUcase.

      APPEND lwa_element TO pe_elements.
    ENDLOOP.
  ENDMETHOD.      "    find_text

  " Возвращает элементы, находящиеся под данным элементом, сравнивается левый верхний угол
  " элемента с левыми верхними углами других элементов на той же странице.
  "  pi_element  - текстовый элемент, под которым нужно искать;
  "  pi_accuracy - диапазон поиска влево и вправо от координаты X левого верхнего угла
  "                элемента при сравнении с Х-координатой других элементов;
  "  pe_elements - найденные элементы.
  METHOD find_text_below.
    DATA: lr_page            TYPE RANGE OF t_text_element-num_page,
          lwa_r_page         LIKE LINE OF lr_page,
          lr_X               TYPE RANGE OF t_text_element-X,
          lwa_r_X            LIKE LINE OF lr_X,
          lr_Y               TYPE RANGE OF t_text_element-Y,
          lwa_r_Y            LIKE LINE OF lr_Y,
          lwa_element        TYPE t_text_element.

    REFRESH pe_elements[].

    lwa_r_page-option = 'EQ'.
    lwa_r_page-sign   = 'I'.
    lwa_r_page-low    = pi_element-num_page.
    APPEND lwa_r_page TO lr_page.

    lwa_r_X-option = 'BT'.
    lwa_r_X-sign   = 'I'.
    lwa_r_X-low    = pi_element-X - pi_accuracy.
    lwa_r_X-high   = pi_element-X + pi_accuracy.
    APPEND lwa_r_X TO lr_X.

    lwa_r_Y-option = 'LT'.
    lwa_r_Y-sign   = 'I'.
    lwa_r_Y-low    = pi_element-Y.
    APPEND lwa_r_Y TO lr_Y.

    LOOP AT mt_elements INTO lwa_element
      FROM pi_element-num_element
      WHERE num_page   IN lr_page
        AND X          IN lr_X
        AND Y          IN lr_Y.

      APPEND lwa_element TO pe_elements.
    ENDLOOP.
  ENDMETHOD.      "    find_text_below

  " Возвращает элементы, находящиеся справа от данного элемента,
  " сравнивается левый верхний угол элемента с левыми верхними углами других
  " элементов на той же странице.
  "  pi_element  - текстовый элемент, справа от которого нужно искать;
  "  pi_accuracy - диапазон поиска вниз и вверх от координаты Y левого верхнего угла
  "                элемента при сравнении с Y-координатой других элементов;
  "  pe_elements - найденные элементы.
  METHOD find_text_right.
    DATA: lr_page            TYPE RANGE OF t_text_element-num_page,
          lwa_r_page         LIKE LINE OF lr_page,
          lr_X               TYPE RANGE OF t_text_element-X,
          lwa_r_X            LIKE LINE OF lr_X,
          lr_Y               TYPE RANGE OF t_text_element-Y,
          lwa_r_Y            LIKE LINE OF lr_Y,
          lwa_element        TYPE t_text_element.

    REFRESH pe_elements[].

    lwa_r_page-option = 'EQ'.
    lwa_r_page-sign   = 'I'.
    lwa_r_page-low    = pi_element-num_page.
    APPEND lwa_r_page TO lr_page.

    lwa_r_X-option = 'GT'.
    lwa_r_X-sign   = 'I'.
    lwa_r_X-low    = pi_element-X.
    APPEND lwa_r_X TO lr_X.

    lwa_r_Y-option = 'BT'.
    lwa_r_Y-sign   = 'I'.
    lwa_r_Y-low    = pi_element-Y - pi_accuracy.
    lwa_r_Y-high   = pi_element-Y + pi_accuracy.
    APPEND lwa_r_Y TO lr_Y.

    LOOP AT mt_elements INTO lwa_element
      WHERE num_page   IN lr_page
        AND X          IN lr_X
        AND Y          IN lr_Y.

      APPEND lwa_element TO pe_elements.
    ENDLOOP.
  ENDMETHOD.        "     find_text_right

  " Возвращает элементы, находящиеся внутри прямоугольника на данной странице
  METHOD find_text_in_box.
    DATA: lr_page            TYPE RANGE OF t_text_element-num_page,
          lwa_r_page         LIKE LINE OF lr_page,
          lr_X               TYPE RANGE OF t_text_element-X,
          lwa_r_X            LIKE LINE OF lr_X,
          lr_Y               TYPE RANGE OF t_text_element-Y,
          lwa_r_Y            LIKE LINE OF lr_Y,
          lwa_element        TYPE t_text_element,
          lh_left            TYPE I,
          lh_right           TYPE I,
          lh_top             TYPE I,
          lh_bottom          TYPE I,
          lh_num             TYPE I.

    REFRESH pe_elements[].

    lh_left   = pi_left.
    lh_top    = pi_top.
    lh_right  = pi_right.
    lh_bottom = pi_bottom.

    IF lh_left > lh_right.
      lh_num = lh_left.
      lh_left = lh_right.
      lh_right = lh_num.
    ENDIF.

    IF lh_top > lh_bottom.
      lh_num = lh_bottom.
      lh_bottom = lh_top.
      lh_top = lh_num.
    ENDIF.

    lwa_r_page-option = 'EQ'.
    lwa_r_page-sign   = 'I'.
    lwa_r_page-low    = pi_num_page.
    APPEND lwa_r_page TO lr_page.

    lwa_r_X-option = 'BT'.
    lwa_r_X-sign   = 'I'.
    lwa_r_X-low    = lh_left.
    lwa_r_X-high   = lh_right.
    APPEND lwa_r_X TO lr_X.

    lwa_r_Y-option = 'BT'.
    lwa_r_Y-sign   = 'I'.
    lwa_r_Y-low    = lh_top.
    lwa_r_Y-high   = lh_bottom.
    APPEND lwa_r_Y TO lr_Y.

    LOOP AT mt_elements INTO lwa_element
      WHERE num_page   IN lr_page
        AND X          IN lr_X
        AND Y          IN lr_Y.

      APPEND lwa_element TO pe_elements.
    ENDLOOP.
  ENDMETHOD.       "    find_text_in_box

ENDCLASS.


Теперь привожу пример чтения PDF
Предположим, у нас счет-фактура. Нужно прочитать номер этого счета-фактуры и его дату.
Номер располагается на первой странице под надписью "No de. Facture", дата располагается также на первой странице под надписью "Date emission".

Код примера:
Code:
PROGRAM ZZP_PDF_PARSER_DEMO.

PERFORM pdf_parser_demo.

INCLUDE ZMM_PDF_PARSER.   " В данном инклуде сам парсер, код которого приведен выше

FORM pdf_parser_demo.
  DATA: lc_pdf_parser TYPE REF TO lcl_pdf_parser,
        lt_elements   TYPE TABLE OF t_text_element WITH HEADER LINE,
        lt_elements2  TYPE TABLE OF t_text_element WITH HEADER LINE,
        l_left        TYPE I,
        l_top         TYPE I,
        l_right       TYPE I,
        l_bottom      TYPE I,
        l_invnumb     TYPE STRING,
        l_invdate     TYPE STRING,
        l_text        TYPE STRING.

  CREATE OBJECT lc_pdf_parser.
*  lc_pdf_parser->load_pdf_from_server( EXPORTING
*                                         pi_filename = 'D:\USR\SAP\PUT\CAT\AG\INBOX\20090619\XA0189872.pdf'
*                                       EXCEPTIONS
*                                         ERROR_IN_LOADING = 1
*                                     ).

* Загрузка файла PDF с локального компьютера, есть также возможность с сервера или из внутренней таблицы
  lc_pdf_parser->load_pdf_from_gui( EXPORTING
                                      pi_filename = 'C:\_toarchive\444\sf1.pdf'
                                    EXCEPTIONS
                                      ERROR_IN_LOADING = 1 ).

* Поиск текста на странице. Поиск по маске пока не предусмотрен, но теоретически это несложно доработать,
* но у меня такой необходимости не было
  lc_pdf_parser->find_text( EXPORTING
                              pi_num_page   = 1
                              pi_text       = 'No. de facture'
                              pi_match_case = SPACE
                            IMPORTING
                              pe_elements   = lt_elements[] ).

  IF lt_elements[] IS INITIAL.
    MESSAGE 'Файл не является счетом-фактурой от поставщика' TYPE 'I'.
    FREE lc_pdf_parser.
    RETURN.
  ENDIF.

  READ TABLE lt_elements INDEX 1.

* Идем текст непосредственно под надписью No. de facture, координаты которой нашли раньше
* pi_accuracy - это диапазон поиска по координате X, например если текст на 2 единицы вправо, то
*               визуально он все равно находится под надписью
  lc_pdf_parser->find_text_below( EXPORTING
                                    pi_element  = lt_elements
                                    pi_accuracy = 10
                                  IMPORTING
                                    pe_elements = lt_elements2[] ).

  IF lt_elements2[] IS INITIAL.
    MESSAGE 'Неверный формат файла, номер счета-фактуры не найден' TYPE 'I'.
    FREE lc_pdf_parser.
    RETURN.
  ENDIF.

  READ TABLE lt_elements2 INDEX 1.
  l_invnumb = lt_elements2-text.

* Аналогично ищем другую надпись
  lc_pdf_parser->find_text( EXPORTING
                              pi_num_page   = 1
                              pi_text       = 'Date Emission'
                              pi_match_case = SPACE
                            IMPORTING
                              pe_elements   = lt_elements[] ).

  IF lt_elements[] IS INITIAL.
    MESSAGE 'Файл не является счетом-фактурой от поставщика' TYPE 'I'.
    FREE lc_pdf_parser.
    RETURN.
  ENDIF.

* Демонстрация другой возможности - поиска всех текстов в прямоугольнике
* Координаты: слева направо идет возрастание X, а снизу вверх возрастание Y (а не наоборот),
*             как в школе по математике. Точка (0,0) располагается в левом нижнем углу
  READ TABLE lt_elements INDEX 1.

  l_left   = lt_elements-X - 10.
  l_top    = lt_elements-Y - 3.
  l_right  = lt_elements-X + 10.
  l_bottom = lt_elements-Y - 30.

  lc_pdf_parser->find_text_in_box( EXPORTING
                                    pi_num_page = 1
                                    pi_left     = l_left
                                    pi_top      = l_top
                                    pi_right    = l_right
                                    pi_bottom   = l_bottom
                                  IMPORTING
                                    pe_elements = lt_elements2[] ).

  IF lt_elements2[] IS INITIAL.
    MESSAGE 'Неверный формат файла, дата счета-фактуры не найдена' TYPE 'I'.
    FREE lc_pdf_parser.
    RETURN.
  ENDIF.

  READ TABLE lt_elements2 INDEX 1.
  l_invdate = lt_elements2-text.

  CONCATENATE 'Файл PDF успешно прочитан, № СФ =' l_invnumb ', дата СФ =' l_invdate INTO l_text SEPARATED BY SPACE.
  MESSAGE l_text TYPE 'I'.

  FREE lc_pdf_parser.
ENDFORM.          "     pdf_parser_demo


Принять этот ответ
Вернуться к началу
 Профиль Отправить email  
 
 Заголовок сообщения: Re: Выкладываю парсер PDF (класс для чтения файла PDF напрямую)
СообщениеДобавлено: Ср, июл 22 2009, 16:39 
Почетный гуру
Почетный гуру
Аватара пользователя

Зарегистрирован:
Вт, май 30 2006, 09:34
Сообщения: 1837
Ого!
Спасибо.
Пока не требуется, но сохраняю себе на память.

_________________
С уважением.


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Выкладываю парсер PDF (класс для чтения файла PDF напрямую)
СообщениеДобавлено: Чт, июн 02 2011, 14:10 
Начинающий
Начинающий

Зарегистрирован:
Чт, мар 17 2011, 18:05
Сообщения: 17
Не могли бы вы подсказать как получить физический размер страницы? Так же получает Adobe Reader в свойствах файла.


Принять этот ответ
Вернуться к началу
 Профиль Отправить email  
 
 Заголовок сообщения: Re: Выкладываю парсер PDF (класс для чтения файла PDF напрямую)
СообщениеДобавлено: Чт, июн 02 2011, 17:56 
Специалист
Специалист

Зарегистрирован:
Вт, ноя 28 2006, 17:02
Сообщения: 114
Спасибо!
Надеюсь в следующий раз будете получать данные для загрузки в XML.


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Выкладываю парсер PDF (класс для чтения файла PDF напрямую)
СообщениеДобавлено: Чт, июн 16 2011, 18:06 
Специалист
Специалист
Аватара пользователя

Зарегистрирован:
Вт, июн 02 2009, 23:28
Сообщения: 226
Откуда: MOW
Пол: Мужской
v2k написал(а):
Не могли бы вы подсказать как получить физический размер страницы? Так же получает Adobe Reader в свойствах файла.

К сожалению не разбирался с этим, так как нужно было только выдрать текст. Более того, реальная проблема была когда начал читать сканированный текст - там буквы часто произвольно соединялись в слова (не соответствовали реальным словам), и только по расположению и размеру шрифта можно было понять как они расположены реально. Все равно особо не разобрался с этим т.к. надо было читать какой-то зашифрованный кусок со шрифтом, решил задачу и так по ключевым словам, значкам, по расположению строк и т.д. То есть по косвенным признакам - и все работало 100%.

Вообще по формату PDF есть подробный reference

PS Для следующей разработки понадобилось доработать класс. Но руки пока не дошли его выложить. Так что если кто-то захочет воспользоваться и не заработает выложенный вариант, пишите, пришлю новую версию в том виде как есть, может кому-то поможет.


Последний раз редактировалось raaleksandr Пт, июл 01 2011, 21:38, всего редактировалось 1 раз.

Принять этот ответ
Вернуться к началу
 Профиль Отправить email  
 
 Заголовок сообщения: Re: Выкладываю парсер PDF (класс для чтения файла PDF напрямую)
СообщениеДобавлено: Пт, июл 01 2011, 17:47 
Начинающий
Начинающий

Зарегистрирован:
Чт, мар 17 2011, 18:05
Сообщения: 17
Со своим вопросом я разобрался. PDF в принципе тэговый формат, есть тэг MediaBox, там и хранится физический размер страницы.


Принять этот ответ
Вернуться к началу
 Профиль Отправить email  
 
 Заголовок сообщения: Re: Выкладываю парсер PDF (класс для чтения файла PDF напрямую)
СообщениеДобавлено: Чт, июл 12 2018, 14:16 
Специалист
Специалист

Зарегистрирован:
Вт, авг 17 2004, 09:47
Сообщения: 203
Пол: Мужской
Я вот так парсю. Получаю XML из PDF и ее уже через DOM или трансформацию получаю значения:
Code:
      data lr_fp type ref to if_fp.
      lr_fp = cl_fp=>get_reference( ).

      data lr_pdf type ref to if_fp_pdf_object.
      lr_pdf =
        lr_fp->create_pdf_object(
          connection = cl_fp=>get_ads_connection( ) ).

      lr_pdf->set_document(
        pdfdata = i_pdf ). " Бинарные данные PDF файла

      lr_pdf->set_task_extractdata( ).

      lr_pdf->execute( ).

      data l_xml type xstring.
      lr_pdf->get_data(
        importing
          formdata = l_xml ).

      e_xml = zalrcl_text_static=>convert_xstr2str( l_xml ).

_________________
Абап в телеграмме
https://t.me/sapabap


Принять этот ответ
Вернуться к началу
 Профиль Отправить email  
 
 Заголовок сообщения: Re: Выкладываю парсер PDF (класс для чтения файла PDF напрямую)
СообщениеДобавлено: Пн, июл 16 2018, 16:23 
Почетный гуру
Почетный гуру
Аватара пользователя

Зарегистрирован:
Чт, дек 20 2007, 19:21
Сообщения: 1087
Кроме картинок в PDF есть еще формат "обфускации". При этом текст в документе кликабелен, но в исходнике хранится как-бы шифрованный.

_________________
я твой сап эфай внедрял
BAdI-позитив


Принять этот ответ
Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 8 ] 

Часовой пояс: UTC + 4 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: SAPer


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB