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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형

그럼 지난 글에 이어서 계속 분석해 보겠습니다.


이제 pdf2text 함수에서 getCharTransformations 함수 call 하는 부분을 볼 차례입니다.

지난번에 preg_match_all 을 사용해서 BT,ET 로 구분해서 데이터를 따로 모으고 그 데이터를 압축해제 해서 그나마 사람이 조금 알아 볼 수 있는 데이터로까지 만들었었는데요.


이 BT,ET  구분에 해당하지 않는 else 부분에서 getCharTransformations 함수를 호출합니다.


전달하는 인자는 첫번째 인자로 pdf2text 초반에 만들어 뒀던 $transformations 배열 변수하고 getDecodedStream 함수를 통해서 얻었던 $data 변수입니다.





function getCharTransformations(&$transformations, $stream) {
    preg_match_all("#([0-9]+)\s+beginbfchar(.*)endbfchar#ismU", $stream, $chars, PREG_SET_ORDER);
    preg_match_all("#([0-9]+)\s+beginbfrange(.*)endbfrange#ismU", $stream, $ranges, PREG_SET_ORDER);

    for ($j = 0; $j < count($chars); $j++) {
        $count = $chars[$j][1];
        $current = explode("\n", trim($chars[$j][2]));

        for ($k = 0; $k < $count && $k < count($current); $k++) {
            if (preg_match("#<([0-9a-f]{2,4})>\s+<([0-9a-f]{4,512})>#is", trim($current[$k]), $map))
                $transformations[str_pad($map[1], 4, "0")] = $map[2];
        }
    }
    for ($j = 0; $j < count($ranges); $j++) {
        $count = $ranges[$j][1];
        $current = explode("\n", trim($ranges[$j][2]));
        for ($k = 0; $k < $count && $k < count($current); $k++) {
            if (preg_match("#<([0-9a-f]{4})>\s+<([0-9a-f]{4})>\s+<([0-9a-f]{4})>#is", trim($current[$k]), $map)) {
                $from = hexdec($map[1]);
                $to = hexdec($map[2]);
                $_from = hexdec($map[3]);

                for ($m = $from, $n = 0; $m <= $to; $m++, $n++)
                    $transformations[sprintf("%04X", $m)] = sprintf("%04X", $_from + $n);
            } elseif (preg_match("#<([0-9a-f]{4})>\s+<([0-9a-f]{4})>\s+\[(.*)\]#ismU", trim($current[$k]), $map)) {
                $from = hexdec($map[1]);
                $to = hexdec($map[2]);
                $parts = preg_split("#\s+#", trim($map[3]));
                
                for ($m = $from, $n = 0; $m <= $to && $n < count($parts); $m++, $n++)
                    $transformations[sprintf("%04X", $m)] = sprintf("%04X", hexdec($parts[$n]));
            }
        }
    }
}

이 함수에서도 처음부터 preg_match_all 함수가 사용됩니다. 정규 표현식은 '#([0-9]+)\s+beginbfchar(.*)endbfchar#ismU' 와 '#([0-9]+)\s+beginbfrange(.*)endbfrange#ismU' 가 사용됩니다.

처음것은 숫자와 관련돼 있고 그 다음에는 뭔지는 모르지만 begin bf char 와 end bf char 가 사용됐습니다.

다른 형식의 파일들은 BT, ET 가 아니라 beginbfchar 와 endbfchar 로 블럭이 나뉘나 봅니다.

이 부분은 우리가 테스트 하는 pdf 파일형식이랑은 상관없는 것 같지만 그래도 시작했으니 마저 보겠습니다.


하여간 이 두 preg_match_all 함수를 사용해서 $chars 와 $ranges 라는 변수를 반들어 냈습니다.


그 다음에는 chars 에 대해서 먼저 for 문을 돌리는데요.

explode() 함수를 사용해서 줄바뀜 '\n'  이 일어나는 단위로 분리를 하고 trim() 으로 좌우의 공백을 없앱니다.


다음 그 분리된 값들로 다시 for 문을 돌리는데요.

preg_match 를 사용해서 다시 나눕니다. 정규표현식인 '#<([0-9a-f]{2,4})>\s+<([0-9a-f]{4,512})>#is' 를 사용해서 나누는데.. 이게 뭔뜻인지 속시원히 알면 좋겠네요.

혹시 이와 관련해서 쉽고 좋은 글 있는 곳아시는 분 계시면 알려 주세요.


하여간 이렇게 해서 만들어진 값을 $map 에 담습니다.

그리고 그 값을 $transformations 배열 변수에 원하는 형식으로 담는군요.


str_pad() 함수 관련한 예제는 아래에 있습니다.


<?php
$input 
"Alien";
echo 
str_pad($input10);                      // produces "Alien     "
echo str_pad($input10"-="STR_PAD_LEFT);  // produces "-=-=-Alien"
echo str_pad($input10"_"STR_PAD_BOTH);   // produces "__Alien___"
echo str_pad($input"___");               // produces "Alien_"
?>


어떤 string 에 공백이나 일정한 문자나 기호를 넣을 수 있는 함수 같네요.

우리가 공부하는 소스에서는 아래와 같이 이용했습니다.

$transformations[str_pad($map[1], 4, "0")] = $map[2];


그럼 $map[1] 다음에 4칸을 0으로 채운 곳에 $map[2] 를 대입한다는 얘기네요.


그 다음은 $ranges 에 대해 for 문을 돌립니다.

이 경우도 줄바꿈을 기준으로 데이터를 분리하고 위 for 문과 비슷하게 돌리는데요.


hexdec() 함수가 보입니다.


<?php
var_dump
(hexdec("See"));
var_dump(hexdec("ee"));
// both print "int(238)"

var_dump(hexdec("that")); // print "int(10)"
var_dump(hexdec("a0")); // print "int(160)"
?>


매뉴얼에는  hexadecimal string 을 decimal number 로 변환해 주는 함수로 나와 있습니다.


이 두번째 for 문은 자세히 안 보겠습니다. 지금 사용하는 pdf 파일을 decode 하는데는 사용되지 않는 부분 같으니까요.


그러면 이제 다 분석 한 거구요.

pdf2text() 함수의 마지막 줄만 남았습니다.


return getTextUsingTransformations($texts, $transformations);


이 return 값이 완전히 사람이 읽을 수 있는 텍스트로 바뀐 값입니다.


지난 글에서 살짝 사람이 읽을 수 있는 데이터로 압축을 해제해서 만든 값을 getTextUsingTransformations() 함수에서 돌려서 완전히 사람이 읽을 수 있는 글자로 만들어 주는 겁니다.


제가 얻고 싶은 대로 데이터를 가공하려면 이 getTextUsingTransformations() 함수 부분을 잘 공부해야 될 것 같습니다.


이 함수는 다음 글에서 자세히 분석해 보겠습니다.




반응형


반응형

지난번 글에 getDecodedStream 까지 분석 했습니다.

그 글 올린지 일주일이 지났네요.
회사에서 다른 일을 하느라 이 부분 분석을 못했는데요.
그 일이 어느정도 마무리 됐으니 다시 마저 분석에 들어가야 겠습니다.


지난번 다루다 끝났던 getDecodedStream 함수를 보면요.$key값이  ASCIIHexDecode, ASCII85Decode 그리고 FlateDecode 냐에 따라서 실행하는 함수가 달랐습니다.

제가 테스트 하는 PDF 문서는 FlateDecode 를 사용하더라구요.


그러면 

            if ($key == "FlateDecode"){
                $_stream = decodeFlate($_stream);
                echo $_stream . "<p>";
                }

가 실행이 될 텐데요.


그 결과값을 미리 브라우저에 찍어 봤습니다.





뭐 이런게 찍히네요.

지금까지 쪼개고 쪼개고 쪼갠 값은 $_stream 의 값을 해당 함수에 넣고 돌려서 얻은 결과가 위 데이터입니다.

사람이 볼 수 있는 데이터가 되려면 아직 멀어보이네요.

그러면 decodeFlate($_stream) 을 살펴 보겠습니다.


function decodeFlate($input) {
    return @gzuncompress($input);
}



뭐 이 함수는 아주 간단합니다. 다른 decodeAsciiHex($_stream) 이나 decodeAscii85($_stream) 은 그래도 뭔가 로직이 있는데 이 함수는 달랑 @gzuncompress() 메소드만 사용했네요.

이 메소드를 알아보겠습니다.

gzuncompress() 메소드의 PHP 매뉴얼을 보면 This function uncompress a compressed string. 라고 돼 있습니다. 그러니까 이 함수는 압축된 string 을 압축해제 하는 함수라고 합니다.

지금까지 작업한 $_stream 을 압축해제하면 저 위에 있는 화면 같은 데이터가 추출 되는군요.


<?php
$compressed   
gzcompress('Compress me'9);
$uncompressed gzuncompress($compressed);
echo 
$uncompressed;
?>


위 코드를 실행 해 보세요. 그러면 Compress me 그대로 출력 될 겁니다. 압축했다가 다시 압축을 풀었으니까요.

그럼 이제 어디로 가야 될까요? 1주일이 지나서 감이 멀어졌는데. 일단 getDecodedStream 함수에서 일을 처리하고 return 하는 부분까지 했으니까 이 함수를 호출했던 pdf2text() 함수로 다시 가야겠죠.


$data = getDecodedStream($stream, $options);


이 부분이었습니다. 저 $data 변수에는 아까 브라우저에 출력했던 그 이상한 데이터가 담겨져 있을 겁니다. $_stream 의 압축을 푼 데이터죠.

그 다음 소스는 아래 내용입니다.


            if (strlen($data)) {
                if (preg_match_all("#BT(.*)ET#ismU", $data, $textContainers)) {
                    $textContainers = @$textContainers[1];
                    getDirtyTexts($texts, $textContainers);

                } else
                    getCharTransformations($transformations, $data);
            }


strlen 함수는 C언어에서도 똑같이 쓰는 것 같은데요. 문자열의 길이를 구하는 함수입니다.

그러니까 strlen($data) 는 $data 의 길이를 구하는거고 0 이면 데이터가 없다는 겂니다. 그러면 PHP는 0 을 false 로 취급하니까 이 if 문을 처리하지 않고 건너 뛸 겁니다.

$data 에 값이 있다면 그것이 길던 짧던 상관 안하고 if 문 안의 코드를 실행합니다.

그 다음은 다시 정규식 표현을 사용하는 preg_match_all 함수가 나오네요.


$data를 BT, ET 등으로 구분해서 $textContainers 에 넣는 것 같습니다.

BT, ET 는 아까 뽑았던 이상한 데이터들에 많이 있습니다. 잘은 모르겠지만 그냥 통박을 굴려보면 Begin Text, End Text 의 준말이 아닐까 생각되네요.

그 다음에 @가 나오는데요. 이건 PHP 에서 에러 관련해서 처리하도록 하는 부호입니다.


<?php
/* Intentional file error */
$my_file = @file ('non_existent_file') or
    die (
"Failed opening file: error was '$php_errormsg'");

// this works for any expression, not just functions:
$value = @$cache[$key];
// will not issue a notice if the index $key doesn't exist.

?>


위에 관련 예제가 있습니다.

그 다음에는 getDirtyTexts($texts, $textContainers); 가 나옵니다.


새로운 함수 호출인데요. 두번째 인자는 방금 뽑아낸 $textContainers 변수를 전달하고 첫번째 인자는 pdf2text 초반에 만들어 뒀던 배열변수네요.

이 변수에 아직 아무 값도 안 담겼던 것 같은데...


그럼 이 호출된 함수를 볼까요?

function getDirtyTexts(&$texts, $textContainers) {
    for ($j = 0; $j < count($textContainers); $j++) {
        if (preg_match_all("#\[(.*)\]\s*TJ#ismU", $textContainers[$j], $parts))
            $texts = array_merge($texts, @$parts[1]);
        elseif(preg_match_all("#Td\s*(\(.*\))\s*Tj#ismU", $textContainers[$j], $parts))
            $texts = array_merge($texts, @$parts[1]);
    }
}


짧은 함수네요.

$textContainers 의 수만큼 for 문을 돌리는데 다시 preg_match_all 를 사용해서 $textContainers 파편들을 '#\[(.*)\]\s*TJ#ismU' 이 정규식 형식에 맞게 구분을 해서 $parts 에 집어넜습니다.


아까 받았던 $texts 에는 이 값들을 계속 차곡차곡 쌓아놓네요.

array_merge() 함수를 사용해서인데요. 이 함수는 한개 이상의 배열을 합하는 함수입니다.

elseif 문 안에도 똑 같은 일을 하죠. 다만 구분하는 정규표현식이 다를 뿐이죠.

즉 이 함수는 BT, ET 별로 쪼갠 값을 다시 위 정규표현식에 맞게 쪼개서 $texts 에 담는 함수입니다.


그 다음은 다시 pdf2text 함수로 돌아가야 되는데요.

BT,ET 로 나누지 않을 경우는 getCharTransformations 함수를 사용하네요.

이 함수부터는 다음 글에서 다룰께요.

마무리 하면서 getDirtyTexts($texts, $textContainers); 바로 밑에 아래 코드를 추가해 봤습니다.


                    for ($k = 0; $k < count($texts); $k++) {
                        echo $texts[$k] . "<br>";
                    }


바로 지금까지 만든 $texts 내용을 찍어 본 건데요.

아래 내용입니다.



이제 조금 사람이 알아볼 수 있는 글들이 나타나기 시작했습니다.


(W)-36(e)7(e)7(k )46(o)7(f) 에는 Week of  가 있죠?

그 중간에 숫자는 뭔지 잘 모르겠지만요. 빈괄호 ()는 space 일까요?


(Oct)-11(o)5(ber )10(1)4( ) 는 October 1 하고 빈괄호가 있네요.

이제 뭔가 보이기 시작합니다.

그럼 다음 시간에는 이 데이터를 마저 decode 해 보겠습니다.





반응형


반응형

주말에 인터넷 서핑을 하다가 지역신문 기사를 하나 번역해 봤어요.


This Colonial Dame is cutting pond ice -- who, where, why?


January 11, 2013 5:06 pm
By Sheila Lennon






이 로드 아일랜드의 여인에 대해 아시는게 있으신가요? 로드 아일랜드 농장에서 지금 무엇을 하고 있는지 아세요? 사진을 클릭해서 크게 보세요 무엇을 하고 있을까요.


편집자 주 : 지난주 출판 한 다음에 Fountain 과 Union Street 코너의 더 오래된 사진을 찾았습니다. 빌딩들도 다르고 James Lavell 이 있던 자리에 Doorley's tap 이 더 먼저 있었습니다. 여기를 보세요.



Updated Sunday:



스커트를 입고 pike pole 을 사용하는 여인은 불굴의 예술가이자 농부인 Julia Lippitt Mauran 입니다. Cranston의 Lippitt Hill Farm 에 있는 그녀의 연못에서 얼음을 떼 내고 있습니다. 저 사진은 1926년 1월 13일에 찍은 것으로 87년 전 사진입니다. 떼어낸 어름은 말이 끄는 썰매에 실려서 얼음집으로 운반됐습니다. 그곳에 얼음을 쌓아놓고 여름에 음식을 시원하게 보관 했었죠.


원래 사진에 있었던 caption 은 아래 내용입니다.


"Julia L. Mauran"이 자신의 연못에 있는 얼음을 떼어 내는 것을 지휘하고 있다. 수작업으로 이루어 졌는데 톱으로 얼음을 잘라 block 으로 만드는 작업이다. 그리고 뱀처럼 긴 통로를 통과하고 나서 썰매에 실린다. 큰 얼음공장에서 현대 기계로 자르고 나르는 방법에 비교하면 조금 원시적인 방법이다. 드러난 물에 얼고 미끄럽게 보이는 걸로 봐서 아주 추운날씨인 것을 보여준다.


(Amish Ice Cutting 사진은 예전의 방법으로 진행되는 얼음 커팅 작업 과정을 보여줍니다.)


반면에 아래 사진은 큰 얼음공장에서 현대식 기계를 사용해서 얼음을 사용하고 싣는 방법을 보여줍니다. 1925년경 Lymansville 로 추정됩니다






Julia Lippitt Mauran은 독특한 면이 있습니다.


그녀의 어머니인 Sarah Williams Lippitt 은 Roger Williams 의 자손입니다. Sarah 의 할아버지인 Brig. Gen. Christopher Lippitt은 1805년에 Cranston 의 Lippitt Hill 에 농장을 지었습니다. 그 땅은 1715년 부터 가문의 땅이었죠. Sarah 도 그곳에서 자랐습니다. (Christopher 는 1809년에 West Warwick에 Lippitt Mill 을 지었습니다.) 1859년에 Warren 의 William Tyler Mauran 과 결혼한 후 Sarah는 Providence 에 정착했습니다. 그녀의 남편은 그곳에서 보석을 가공했죠.


Julia 는 1860년에 태어났습니다. 당시 인구조사 내용을 보면 1900년도까지 그녀는 287 Butler Ave 에 살았습니다. 길의 맨 마지막 집이었는데 어머니 Sarah (1832-1911)와 남자형제인 John (은행 직원) 과 William (보석 가공사) 과 함께 살았습니다. Julia 는 선생님으로 등록이 돼 있습니다. 그녀는 목각사이자 바구니 만드는 사람이었습니다. 조각된 Oak chest 와 panel 전시회를 1893년 World's Columbian Exposition(aka Chicago World's Fair) 에서 열었습니다.








1980년에 찍은 Western Cranston 의 외곽에 있는 Hope Road 의 Lippit Hill Farm 의 farmhouse 사진입니다. 1910년도의 인구조사 내용을 보면 Julia(50)는 Cranston 의 Hope Road 에 있었습니다. 그녀의 직업은 "Farmer - Manager" 로 되어 있구요. Frank Gunzalez 라는 직원과 거주하고 있었습니다. ancestry.com에 있는 그녀의 profile 에 있는 가족들의 얘기로는 그녀는 매주 손으로 직접 버터를 만들었다고 합니다. 거주지를 직접 수선하고 88살에도 우물 지붕의 널을 수선했다고 적혀 있습니다. The big furnace of the 20 room, colonial house burns 5 good logs and she stokes it as well as chopping kindling.


그녀는 로드아일랜드의 charter member of the Colonial Dames 였습니다. The Handicraft Club의 공동설립자였구요. Providence County Garden Club 의 설립자이자 첫번째 회장이었습니다. 1997년 3월 26일 Providence Journal Bulletin story 는 그녀에 대해 좀 더 깊은 내용을 다뤘습니다.





We post a photo from Rhode Island's history every day at facebook/ProvidenceJournal.

Time Lapse Gallery: All the lead photos and links to the stories.




반응형