Текущее время: Вс, июл 22 2018, 06:47

Часовой пояс: 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
Сообщения: 1839
Ого!
Спасибо.
Пока не требуется, но сохраняю себе на память.

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


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: 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
Сообщения: 205
Пол: Мужской
Я вот так парсю. Получаю 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
Сообщения: 1095
Кроме картинок в PDF есть еще формат "обфускации". При этом текст в документе кликабелен, но в исходнике хранится как-бы шифрованный.

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


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

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


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

Сейчас этот форум просматривают: нет зарегистрированных пользователей


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

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