반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형

지난번 글에서는 pdf2text($filename) 함수에 있는 for 문 중간 까지 했습니다.

그 중간에 getObjectOptions() 함수를 call 하는 부분 바로 전까지 했는데요.


그 전까지는 PDF 안의 내용을 일단 바이너리 파일로 받아서 obj,endobj 구문별로 나는 다음에 다시 그 안의 내용을 stream,endstream 구문별로 나눴습니다.


그 stream 별로 나눈 값을 for 문 안에서 계속 getObjectOptions() 함수로 던져서 어떤 일을 하도록 시키는데.. 오늘은 그 어떤일이 어떤일인지 공부할 것 같습니다.


PDF 안의 내용을 가져오는 함수는 file_get_contents() 함수였고 그 바이너리 파일들을 다시 세분화 시킬 때 사용했던 함수가 PCRE 함수인 preg_match_all() 과 preg_match() 였었습니다.


그리고 추가로 PHP 정규 표현식 (regular expressions)과 PDF 구조에 대해서 알아야 했구요.


이제 getObjectOptions() 함수에 대해 알아보겠습니다.


function getObjectOptions($object) {
    $options = array();
    if (preg_match("#<<(.*)>>#ismU", $object, $options)) {
        $options = explode("/", $options[1]);
        @array_shift($options);

        $o = array();
        for ($j = 0; $j < @count($options); $j++) {
            $options[$j] = preg_replace("#\s+#", " ", trim($options[$j]));
           
            if (strpos($options[$j], " ") !== false) {
                $parts = explode(" ", $options[$j]);
                $o[$parts[0]] = $parts[1];
            } else
                $o[$options[$j]] = true;
        }
        $options = $o;
        unset($o);
    }

    return $options;
}


여기서도 PCRE 함수가 쓰이네요.

바이너리 파일 내용을 처리하기 위해서 이 함수들은 필수로 이해하고 있어야 할 것 같습니다.


우선 처음에 $options 라는 배열을 만들고 pdf2text() 함수 내에서 던져준 PDF 내의 각 stream 을 가지고 다룹니다.


preg_match("#<<(.*)>>#ismU", $object, $options)


받은 stream 블럭인 $object 안의 내용을 정규표현식인 #<<(.*)>>#ismU 로 구분해서 아까 만든 배열변수인 $options 에 담습니다.

정규표현식 #<<(.*)>>#ismU 가 뭔지 정확히 알면 좋겠는데...

혹시 정규표현식에 강하신 분 계시면 댓글에 이게 정확히 무엇을 말하는지 알려 주시면 감사하겠습니다.





그 다음은 explode() 함수를 썼습니다. 이 함수는 문자열을 특정 기준으로 자르는 함수입니다.

예제를 하나 보면요.


<?php
$str 
'one|two|three|four';

// positive limit
print_r(explode('|'$str2));

// negative limit (since PHP 5.1)
print_r(explode('|'$str, -1));
?>


이 소스를 돌리면 그 결과값은 아래와 같습니다.

Array
(
    [0] => one
    [1] => two|three|four
)
Array
(
    [0] => one
    [1] => two
    [2] => three
)


첫번째 파라미터는 구분자이고 두번째 파라미터는 작업할 문자열 그리고 마지막 파라미터는 limit 이라고 하는데요. 이 세번째는 옵션입니다.

위의 소스를 보면 2를 넣으면 두개로 구분해서 첫번째 | 를 기준으로 구분하고 나머지는 모두 한개의 값으로 처리하는군요.

두번째 소스는 마이너스 값을 넣었는데 | 구분자 이전의 값을 나누는 것 같습니다.

비슷한 함수로는 정규식을 사용해서 구분하는 preg_split() 이 있구요 반대되는 함수로는 implode() 함수가 있습니다.


지금 배우는 소스에서는 $options = explode("/", $options[1]); 로 구분자 / 을 기준으로 options 를 모두 나눴습니다.

여기서 / 는 뭐를 나타내는 것일까요?


지난번 글에 Hello World! 를 표시한 PDF 의 바이너리 파일을 보면 << , >> 구문 안에 어떤 정보들이 들어있고 그 안에 각각의 정보들이 / 나눠져 있는 걸 볼 수 있습니다.

/ 이 무슨 의미인지 알려면 PDF 구조에 대해 공부해야 할 것 같습니다.


obj, stream,<<,>>,/ 대충 지금까지 나온 PDF 내부에서 사용되는 기호입니다. 


하여간 / 로 나는 값을 PHP 의 array_shift() 함수로 돌립니다.

배열의 첫번쨰 요소를 없애버리는 겁니다. 그리고 두번째 요소가 첫번째 요소로 되는거죠.


<?php
$stack 
= array("orange""banana""apple""raspberry");
$fruit array_shift($stack);
print_r($stack);
?>


이렇게 하면 결과가 아래와 같이 됩니다.

Array
(
    [0] => banana
    [1] => apple
    [2] => raspberry
)


Hello World! PDF 의 바이너리를 보면 / 다음엔 Size, Root 이런 문자가 오는데 이걸 없애 버리는 건가요?


하여간 그 다음으로 넘어가면 $o라는 배열 변수를 만듭니다.

그래서 방금전에 explode 까지 한 결과값인 $options 배열을 for 문으로 돌립니다.



$options 배열의 각각의 요소를 다시 preg_replace() 함수를 이용해서 변환을 시켜주네요.


preg_replace("#\s+#", " ", trim($options[$j]));


#\s+# 로 변환을 하고 그 결과값의 좌우 공백을 없앤 값을 $options 배열에 다시 넣어줍니다.


그리고 $options[$j] 안에 공백이 있으면 다시 그 공백을 기준으로 나눠서 $parts 변수에 담습니다.

그 다음에 방금 전 만들어 주었던 $o 의 $parts[0] 에 $parts[1] 을 담습니다.

만약 공백이 없다면 $o[$options[$j]] 에 true 값을 대입시킵니다.


그래서 이 $o  변수를 $options 에 담은 다음에 그 값을 return 합니다.


여기까지가 getObjectOptions() 메소드가 하는 일 입니다.


정리하면 #<<(.*)>>#ismU 으로 나누고 그걸 다시 / 으로 나누고 배열을 shift 한 다음에 배열안의 내용을 #\s+# 로 나눠서 공백이 있으면 공백을 기준으로 다시 나누고 공백이 없으면 $o[$options[$j]] 를 true 로 해서 이 $o의 값을 $options 에 담아서 return 을 해 줍니다.


정규표현식이 정확히 무엇을 하라는 것인지 알고 싶네요.

혹시 아시는 분 설명 부탁드려요.


오늘은 여기까지 할거구요.


위의 만들어진 $options 값을 pdf2text() 메소드에서 받아서 어떻게 처리하는지 다음에 알아 볼께요.


반응형


반응형
Posted on . Written by



지난 몇달간 여러분들 중 많은 분들이 Corona Geek show 들을 보셨을 겁니다. 이 비디오들은 코로나 커뮤니티에서 왕성하게 활동해 오신 코로나 개발자인Charles Mckeever 가 모아놓은 건데요, 코로나 ecosystem 에서 진행중인 흥미로운 것들에 대해 얘기하고 일반적인 모바일 컴퓨팅에 대해 논의한 것들입니다.






저희들은 그 쇼들을(the shows) 쭉 보아왔습니다. 그리고 그 중 몇개는 저희들도 참여(participated) 했었습니다. 저희들은 그 아이디어를 사랑했고 Charles 가 그 일을 계속 진행하는 것에 대해 깊은 인상을 받았습니다. 저희들도 예전에 이와 비슷한 것들을 하려고 계획하긴 했었습니다만 실제로 그 일을 할 시간을 갖지는 못했었습니다.



그래서 이 Corona Geek 을 Corona Labs folder 에 포함시키고 Charles 를 on board 시키기로 했습니다. 우리는 Corona Geek 을 더 발전시켜 나갈 계획입니다. 저희들의 비전은 개발자 커뮤니티를 좀 더 활발하게 발전시켜 나가는 겁니다. 개발자들이 가서 모바일 개발과 모바일 마케팅에 대해 (코로나에 대한 것 뿐만 아니라) 배울수 있는 그런 커뮤니티를요. Corona Labs team, Corona Ambassadors 그리고 Corona developers로서 여러분의 경험을 공유할 수 있는 그런 공간 입니다. 이 공간을 TV 채널의 일부, 개발자 포럼의 일부 그리고 educational resource의 일부로 생각해 주세요. 거기에다가 어떤 좋은 아이디어가 떠오르면 그런 아이디어도 나눌 수 있는 공간입니다. 이게 Google+ Hangouts and video 와 비슷합니다. 하지만 Corona Geek 은 이 뿐만 아니라 다른 미디어로 더 확장해 나갈 겁니다.





저희들은 코로나의 개발자 공간에 대해 믿음을 갖고 있고 또 자랑스럽게 생각하고 있습니다. 여러분들이 저희 플랫폼을 사용하기로 결정했고 또 앱을 만들고 계신것이 영광스럽습니다. 이 플랫폼을 더 강력하게 만드는 것은 여러분들에게 더 가치있는 resources 들을 제공하는 것이란 것을 알고 있습니다. Corona Geek 으로 우리의 커뮤니티에 대한 노력을 배가할 것이며 이와 더불어 코로나를 아름답고 성공적인 게임과 앱을 개발하는 최고의 플랫폼으로 만드는 도구로 이를 활용할 것입니다.

이러한 일들을 하기 위해 Corona Geek 을 선택했습니다. 이와 관련된 비전과 계획은 today’s Hangout (Monday, January 7)에 있습니다. 태평양 시간으로 낮 12시에 시작할 겁니다. 여기에 여러분들을 초청합니다.



저희들은 또한 Corona Geek forum도 만들었습니다. 이 Corona Geek 과 관련한 여러분의 생각과 아이디어, 제안 을 듣고 싶습니다. 주저하지 마시고 저희들에게 직접 이메일을 주세요. 저희들의 이메일은 charles[AT]coronalabs[dot]com 과 david[AT]coronalabs[dot]com 입니다.



감사합니다.


David



반응형


반응형

그럼 이전 글에 이어서 계속 진행하겠습니다.


pdf2text($filename) 을 하다가 마쳤었는데요.


이어서 알아볼 것은 preg_match_all 함수입니다.


preg_match_all("#obj(.*)endobj#ismU", $infile, $objects);


이런 초반부터 어려운게 걸렸네요.

이 함수에는 3개의 파라미터가 들어가는데요.

첫번쨰 파라미터는 정규표현식이 들어갑니다.

이 첫번째 파라미터의 패턴을 가지고 두번쨰 파라미터에 있는 객체내를 search 하는 겁니다.

그래서 그 정규식대로 구분해서 세번째 파라미터에 있는 변수에 결과를 담는거죠.


이 정규식과 관련해서는 여기여기 에 있는 글들을 참조하세요.

그리고 PHP 튜토리얼 사이트는 여기여기 가 있습니다.


이 preg_match_all 메소드에 대한 튜토리얼은 여기에 있습니다.


예제를 하나 보시면...


<?php
preg_match_all
("|<[^>]+>(.*)</[^>]+>|U",
    
"<b>example: </b><div align=left>this is a test</div>",
    
$outPREG_PATTERN_ORDER);
echo 
$out[0][0] . ", " $out[0][1] . "\n";
echo 
$out[1][0] . ", " $out[1][1] . "\n";
?>


위 코드를 실행하면 아래와 같은 결과를 얻습니다.


<b>example: </b>, <div align=left>this is a test</div> example: , this is a test


그러니까 첫번째 파라미터로 들어간 정규식은 모든 태그를 없애는 거나보네요.


그래서 결과가 배열 변수  $out 에 들어가게 되는데요. 

이 배열의 첫번째 열에는 full pattern 이 들어가고 두번쨰 열이 바로 이 태그가 없어진 값들이 들어갑니다.


정규식을 잘 몰라서 왜 그런지는 모르겠구요.


하여간 우리가 공부하는 소스코드는 PDF 의 내용을 담은 변수를 #obj(.*)endobj#ismU 라는 정규식을 써서 Search하고 그 값이 $objects 에 들어갑니다.


참고로 pdf 파일은 수많은 obj/endobj, stream/endstream , Startxref/Xref,Trailer 등으로 구성 돼 있습니다.



위와 같은 PDF 파일이 있다면 바이너리 파일은 아래와 같습니다.


%PDF-1.7

1 0 obj  % entry point
<<
  /Type /Catalog
  /Pages 2 0 R
>>
endobj

2 0 obj
<<
  /Type /Pages
  /MediaBox [ 0 0 200 200 ]
  /Count 1
  /Kids [ 3 0 R ]
>>
endobj

3 0 obj
<<
  /Type /Page
  /Parent 2 0 R
  /Resources <<
    /Font <<
      /F1 4 0 R 
    >>
  >>
  /Contents 5 0 R
>>
endobj

4 0 obj
<<
  /Type /Font
  /Subtype /Type1
  /BaseFont /Times-Roman
>>
endobj

5 0 obj  % page content
<<
  /Length 44
>>
stream
BT
70 50 TD
/F1 12 Tf
(Hello, world!) Tj
ET
endstream
endobj

xref
0 6
0000000000 65535 f 
0000000010 00000 n 
0000000079 00000 n 
0000000173 00000 n 
0000000301 00000 n 
0000000380 00000 n 
trailer
<<
  /Size 6
  /Root 1 0 R
>>
startxref
492
%%EOF


자세한 내용을 알고 싶으시면 여기 를 참조하세요.


이 프로그램 하나 이해하기 위해 공부해야 할 것들이 무지 많군요.






원래 소스로 돌아가서요.


    for ($i = 0; $i < count($objects); $i++) {
        $currentObject = $objects[$i];
        if (preg_match("#stream(.*)endstream#ismU", $currentObject, $stream)) {
            $stream = ltrim($stream[1]);
            $options = getObjectOptions($currentObject);
            if (!(empty($options["Length1"]) && empty($options["Type"]) && empty($options["Subtype"])))
                continue;

            $data = getDecodedStream($stream, $options);
            if (strlen($data)) {
                if (preg_match_all("#BT(.*)ET#ismU", $data, $textContainers)) {
                    $textContainers = @$textContainers[1];
                    getDirtyTexts($texts, $textContainers);
                } else
                    getCharTransformations($transformations, $data);
            }
        }
    }


아까 얻었던 $objects 배열의 count 만큼 for 문을 돌립니다.

바이너리 파일을 하나하나 텍스트 문자로 변환시키기 위해 전체 $objects 를 for 문 돌리는 거겠죠.

이 for 문 안에서는 $objects 배열에 있는 각각의 객체들을 일단 $currentObject 에 담네요.

그 다음에 다시 PCRE 함수가 나왔네요.

이와 관련해서는 저 위에 튜토리얼 페이지 링크를 걸어놨죠?  


if 문안에 preg_match("#stream(.*)endstream#ismU", $currentObject, $stream) 이 있습니다.


위 정규식으로 search 한 내용을 $stream 에 담슴니다.

한개의 stream 을 담는거네요.


PDF 소개글을 보면 Stream 은 아래와 같이 정의 돼 있습니다.

Stream (<< /Length ... >> stream ... endstream): embedded data, can be compressed. It starts with a dictionary that describes the stream such as its length or the encoding (/Filter) is uses.


stream 에 대해 자세히 공부하시려면 여기 로 가세요.


그 다음은 php2text.php 에 있는 getObjectOptions($currentObject)를 콜 합니다.


흠.... 소스를 자세히 뜯어 보니까 그 흐름은 좀 더 이해하게 되는데요...


이 소스를 완벽하게 이해하고 또 마음대로 수정해서 사용하려면

PHP 정규 표현식 (regular expressions) 를 알아야 하고 PCRE 함수를 알아야 하고 PDF 의 구조를 알아야 하네요.


일단 오늘의 성과는 이 소스를 이해하기 위해 필요한 지식 중 내가 뭐가 부족한지 알았습니다.


시간이 되면 파고 들겠는데... 그럴 시간이 있을 지 모르겠네요.


일단 오늘은 여기까지 하구요. 다음 글에서는 getObjectOptions() 메소드를 공부할 생각입니다.


그럼...


반응형