개발 진행중 변경 내용을 게시한다.

Acobat Pro 8 에서 JPG 생성시 문제점

Acrobat Pro 8 에서 JPG 를 생성하면 포함된 텍스트 외곽선이 다음과 같이 추출된다. 

<< 원본 PDF >>


<< Acrobat Pro 8 에서 생성된 JPG >>


PNG 로 바꿔보기도 하고 설정을 변경해보기도 했지만, 포함된 이미지의 퀄리티와 색상만 향상될뿐 이었다.
Acobat Pro 8 에서 해결할 방법이 안보여서 할 수 없이 http://www.foolabs.com/xpdf 에서 제공하는 xpdf 를 사용했다.  PDF 표준을 정하는 곳과는 거리가 멀며, PDF 를 크래킹해서 xpdf 를 만들었다.
xpdf 는 몇가지 프로그램을 제공하는데, 이 중에 pdftoppm 을 이용해서 .ppm 파일을 생성한다음 ImageMagick 에서 png 로 변경하는 과정을 거치기로 했다.

ppm 파일은 포터블 비트맵 포멧을 말하며, 간단한 구조와 무압축(=무손실) 상태이므로 다른 포멧으로 변경하기 용이한 포멧이다.  ImageMagick 을 통해 png 포멧으로 만든 결과물이다.


pdftoppm 과 ImageMagic  을 통해 얻는 결과는 Photoshop PDF 파일을 열어서 얻는 결과와 비슷했다. (보기에 따라선 포토샵 보다 pdftpppm 과 ImageMagick  을 사용하는 것이 더 나아 보이기도 한다..) 
따라서, 1부에 만든 코드중 jpg 추출 부분은 다음과 같이 변경되었다.

조만간 Adobe 에 연락해서 방법이 없는지 문의할 예정이다. 긍정적인 답이 오면 바로 포스팅 하도록 하겠다. 이 글을 읽으시는 분들중 Acrobat Pro 로 해결하는 방법이 있으면 꼭 답변 주길 부탁 드린다.

소수의 개발자 이겠지만, 저와 똑같은 과정을 겪을 분들을 위해 포스팅 한다.

타겟 환경이 mac OS X 이지만, 손에 익은 에디터가 EditPlus 라서 윈도우에서 개발을 하고 mac 에 옮기는 는 프로세스로 개발을 했다. 개발을 중반쯤 넘길때 예상대로(=메뉴얼 대로) 동작하지 않는 부분이 발견되었다. 1주 반동안 고민의 고민을 거듭하고 셀수없는 검색과 시도를 했지만 해결하지 못한채 혹시나 하는 마음에 소스를 mac 에 복사했는데, 이럴수가.. 메뉴얼대로 동작을 하는 것이었다.!!  의미없이 보낸 시간이 너무 아까워서 화가 치밀어 올랐다.

다이얼로그를 안띄우고 실행하면 아래 코드가 올바르게 동작하는데, 메뉴얼에선 이에 관해 전혀 언급되어 있지 않다. 아도비가 알고서 그런건지 모르겠지만, saveAs 함수가 사용된 모든 예제들은 다이얼로그 없이 실행되는 것으로 되어 있다. 

나는 타겟 환경이 mac OS X 이라 이런 문제를 알 수 있었지만, 타겟 환경이 win 인 개발자들은 영문도 모른채 포기할것 같다는 생각이 들어서 급히 포스팅 한다.

app.execDialog() 함수를 실행해서 다이얼로그를 띄우고 버튼 이벤트 메소드에 다음과 같은 코드를 구현하면 다음과 같은 오류가 발생한다. 

oPDF = app.openDoc({
cPath: this.data.oRetn.cPath,
cFS: this.data.oRetn.cFS,
bHidden: true   // mac 에선 올바르게 동작. win 에선 화면이 나타남
});

oPDF.saveAs({
cPath : sPath
, cConvID : 'com.adobe.acrobat.accesstext'
, bCopy : true
, bPromptToOverwrite : false
});

// mac 에선 올바르게 동작. win 에선 다이얼로그가 닫혀질때까지 실행되지 않음.
// 50 페이지 이상 넘어가면 Acrobat 이 뻗게 됨.
oPDF.closeDoc(true);  



시작전에 비슷한 상황에 계신 분들에게 도움이 되고자 이 과정을 거친 이유에 대한 설명을 한다.

0. 본 프로그램은 서버용 프로그램이 아니다 사용자는 모두 mac os X 을 사용한다. 편집디자이너가 mac 과 pc 를 왔다 갔다 하는 불필요한 과정등을 없애기 위해 전임자가 개발한 win 용 프로그램을 걷어 내야 한다. 

1. 내가 속한 조직은 MS SQL Server 만 사용한다. 문제는, MS에서 mac os x 용 ODBC 드라이버를 만들어주지 않고 apple 또한 개발을 안하고 있다. (두 회사가 앙숙 임을 상기시켜 준다) 따라서 mac os x 에서 MS SQL Server 에 직접 붙을수는 없다. 물론 몇몇 공개 및 상용 드라이버가 있지만 두개 모두 100% 신뢰하냐는 질문에는 Never 다.

이에 관해 인터넷상에서 몇몇 분들이 MS 에서 제공하는것이 있다는 엉뚱한 답변으로 사람들을 혼란스럽게 하는것 같아서 추가로짚고 넘어간다. MS 에서는 win 용 ODBC 이외에 java native 4 드라이버만을 배포하고 있다. mac os X 이 java 를 지원하고 있으므로 사용 가능하다. 하지만, 이것은 java 프로그램을 할때만 사용할 수 있다.

win 용 Acrobat Pro 8 경우 javascript 에서 ADO 를 사용할수 있다. 그렇지만, 제공되는 기능이나 속도가 기대 이하였다. 게다가 이 기능은 CS3 제품중 Acobat Pro 만 유일하다.
IIS 올리는데 비용 부담이 없고 개발툴 역시 무료로 배포되는 Visual Studio Express 로 웹서비스 대부분의 기능은 개발이 가능하다. (게다가 한글화 까지..) JAVASCRIP 로 SOAP 을 이용하는 부분은 CS3 의 다른 제품(인디자인, 포토샵 등) 에서도 지원하며 사용법도 비슷하다. 나중을 위해서라도 도전해 보시길 바란다

2. 전임자가 윈도우 애플리케이션상에서 PDF 병합/분할 처리를 위해 pdftk 1.12 을 이용했다. 이 라이브러리의 큰 문제는 pdf 1.3 으로 변환된다는 것이다. PDF 풀컬러 인쇄를 하는 업체에선 문제가 된다. 자세한 내용은 다른 포스트에 성토 하겠다. 하여튼 안전하게 PDF 1.6 을 다루기 위해서는 Acobat Pro 나 할러퀸 같은 알만한 S/W 가 필요 하며, 회사에서 정품 CS3 를 가지고 있어서 Acobat Pro 8 을 사용한다.

3. Acobat Pro 8 의 javascript 엔진에서 SOAP 접근 및 PDF 결합/분리/Text 추출 등의 모든 작업과 간단한 화면 생성이 가능하다. 또한 메뉴에 넣을 수 있으므로 사용자가 편리하게 사용할 수 있다. 

블로그가 업무 보고를 하는 곳은 아니니 이정도로만 하고 본론에 들어가겠다.

환경은 다음과 같다.

클라이언트 : mac OS X Tiger. Acobat Pro 8 의 javascript 엔진 이용함.
서버 : win 2003 Server. Visual Studio 2008 에서 .Net 3.5 로 웹서비스 생성. (C# 사용)


아래는 IIS 에 올린 웹서비스 예제 코드다. Acobat Pro 8 의 JavaScript 엔진에서 이 메소드를 접근할 것이다.

// ASP.net 에서 생성한 테스트 코드
public string HelloWorld2(string d)
{
    return "Hello World" + d;
}


아래 코드는 Acobat pro 8 에서 웹서버에 접속해서 HelloWorld2() 를 호출하는 예제 이다.
Acobat pro 8 에서 ASP.NET 으로 생성된 웹서비스를 사용하는 경우, Net.SOAP.request  개체에서 반드시 설정할 두가지 속성이 있다. 두 속성을 지정하지 않으면 오류는 발생하지 않지만 파라메터가 전달되지 않는다.
(Acrobat 8 이후로는 SOAP 대신 Net.SOAP  으로 사용할것.)

// Acrobat javascript 코드
var cNameSpace = "http://10.10.1.1/";

var cTestString = { soapType : "xsd:string" , soapValue : "22" };
var req = {};
req["HelloWorld2"] = {d : cTestString};

var response = Net.SOAP.request({
        cURL: cNameSpace + "Service.asmx?WSDL"
        , oRequest : req
        , cAction : cNameSpace + "HelloWorld2"
        , bEncoded : false
        , cNamespace : cNameSpace
        , cResponseStyle: SOAPMessageStyle.Message
        , cVersion : SOAPVersion.version_1_2  
});

var value = response[0].soapValue[0].soapValue
console.println("result = " + value);

bEncoded :
ASP.NET 에서 생성되는 WSDL 은 Document/literal wrapped 을 사용하는데, Acrobat 의 SOAP 관련 javascript 개체들은 encoding 이 기본값이다. false 로 지정해야 한다.

cVersion :
asp.net 2.0 이상은 SOAP 1.1 요청이 있으면 1.1 형식으로 반환된다. 1.2 와 1.1 차이는 구글에서 검색하면 많은 정보를 얻을 수 있으므로 포스팅 하진 않는다.
Acrobat 8.0 이상을 사용한다면 반드시 SOAPVersion.version_1_2 로 설정하시기 바랍니다.


이정도면  Acrobat Pro 8 JavaScript 에서 ASP.NET WebService 를 이용하는데 큰 무리가 없을듯 하다.  자세한용은  "Developing Acrobat® Applications Using JavaScript" 이나 "JavaScript™ for Acrobat® API Reference" 를 보면서 참조하길 바란다.
프로그램적으로 PDF 를 결합/분할 해야 하는 미션을 부여받은 프로그래머들중 Acrobat Pro 가 없거나, Acrobat Pro 가 있다고 해도 능력에 벅차거나 공부하기 귀찮은 개발자들은 대부분 pdftk 을 사용하고 있는것 같다. 전임자는 후자쪽에 속하는 경우였고 그분이 사용하신 버전은  1.12 였다.

최근 인쇄품질 개선을 위해 몇가지 테스트를 하는중 알게된 내용중, pdftk 1.12 를 사용할경우 문제점에 대해 설명한다.

pdftk 1.12 를 사용하면 pdf 1.4 으로 변경된다.!!

Acrobat Pro 8 경우엔 pdf 1.7 까지 생성 가능하다. 대부분의 사람들은 PDF 는 다 똑같다고 생각하겠지만, 1.5 부터 8 device color 에서 31 device color 로 확장되었으며, 알파값(투명도) 인쇄가 가능해졌다. 거기에 더해서 링크 및 레이어 보존등 많은 기능이 추가되었다. 1.4 와 1.6을 비교해본 바로는 LCD 모니터상에서도 구별될 정도로 컬러 단계가 많이 세분화 되었다.

pdftk 의 장점은 단 하나. Acrobat Pro 8의 파일 결합에 비해 속도가 무척 빠르다는 것이다. 이유는 버전을 다운 시키고 최적화 및 중복 글꼴 제거, 웹에서 빠르게 보기 등을 전혀 하지 않기 때문이라고 생각 된다. 버전을 맞춘 상태에서 Acorbat pro 8 에서 생성된 결과를 비교하면 파일의 사이즈가 차이가 난다. 빠른게 장땡이 아니라는 의미다.

나는 개발 경력이 꽤 되지만, Acrobat 플러그인 개발이 쉽지만은 않은것이 사실이다. 하지만, 가장 중요한 것은 인쇄 품질이 향상된다는 것이다. 따라서 PDF 출력을 하는 조직에 속한 프로그래머는 이러한 내용을 어렵거나 또는 모른다고 외면하고 심지어 상사와 조직에게 사실을 감추지 않아야 될 것이다.


※얼마전 pdttk 을 배포하는 사이트에 가보니 버전이 올라갔다. 테스트하신 분들이 계시면 생성된 PDF 의 버전 및 결과에 대해서 답글 주길 바란다.

※최근에 pdf 를 이미지화 하는 방식 대신 백터 방식으로 처리하는 방식을 개발한 e-book 업체를 만나게 되었다. 이 솔루션은 링크 등의 정보를 추출해서 페이지에 링크를 거는 기능이 있다. PDF 가 링크 정보를 가지기 위해선 버전이 1.5 이상은 되어야 한다. 이북을 위해서라도 버전업이 필요한 시점이 올 수도 있다는 생각이 든다.

전임자가 만든 프로그램중 pdf 를 이미지(JPG)로 변환하는 프로그램이 있는데, 여러 가지 이유들로 걷어내고 새롭게 만들게 되었다. 나와 같은 상황에 처한 개발자들이 삽질하는 것을 막기 위해 글을 올린다.


블로그 제목이 Publishing Programming for Mac OS X 이지만, 몇가지 사정으로 이 프로그램은 Win 용 Acobat Pro 8 의 COM 인터페이스를 이용한 DLL 형태로 만들게 되었다. 이유부터 설명한다. 

MS SQL Server 상에 트렌젝션(데이터량) 이 크다. 트랜젝션이 크지 않았으면 DB 쪽 처리만 Mac OS X Server 에서 ASP.NET 기반의 SOAP 을 통해 처리했었을 것이다.

윈도우 기반 영업시스템에서 호출하거나 재사용이 가능해야 한다.


개발 환경은 다음과 같다.

win 용 Acrobat pro 8
Win 2003 Server
Win XP
Visual Basic 6
ImageMagick -6.4.8-10-Q8


각 작업별로 사용되는 라이브러리는 다음과 같다. DB 나 XML 을 사용하는 것은 MSDN 을 찾기 바란다.

pdf -> jpg 환  :  Acobat  Pro
pdf 의 텍스트 추출 : Acobat  Pro
변환된 jpg 의 썸네일 생성 : ImageMagick


Adobe CS3 는 windows Server 2003 이상에서만 설치된다. 2000 Server 에서 설치되는 편법이 있다고 들었지만 분명 이유가 있어서 OS 를 제한했다고 여겨진다. 설치를 진행하면서 알게된 사실인데, 서버에 설치되는 제품은 인디자인과  Acrobat Pro 8 정도 밖엔 안된다. 포토샵과 일러스트레터는 안깔린다.


▶ImageMagick

ImageMagick 은 이미지 변환툴중 역사도 오래되었고 꾸준한 업데이트가 이루어지고 있는 라이브러리다. 또한 mac OS X 및 파이썬, 루비 등의 최신 언어에서도 접근이 가능한 인터페이스를 제공하고 있다. http://www.imagemagick.org 에서 최신 버전을 받기 바란다. 나는 ImageMagick-6.4.8-10-Q8-windows-dll.exe 를 사용했다.
파일명을 주목하자. Q 은 Color Depth 를 말하며 Q8은 8bit , Q16은 16bit 를 말한다. 디카 RAW 포멧도 아니니 처리 속도나 메모리를 고려해서 Q8 을 사용하기로 결정했다.
아래는 설치화면이다. COM 인터페이스에서 이용하기 위해선 설치중 하단의 체크박스를 선택해야 한다. 


대부분의 경우 Microsoft Visual C++ 2008 SP1 Redistributable Package 가 설치되어 있지 않다. 다운로드 페이지의 하단에 보면 링크가 있으니 받아서 설치하도록 한다. 영문이지만 사이트에 친절하게 설명되어 있으니 조급해 하지 말고 차근히 읽어 보시길 바란다.

※ ImageMagick 는 경로명에 한글이 들어가면 실행이 안된다. 반드시 영문으로 폴더 명 파일명을 사용하기 바란다.



▶개발시 주의할 점.
vbs 는 디버그가 어려워서 vb6 에서 작업한다음 조금 수정해서 vbs 파일에 붙이는 과정을 거치고 있다. Acrobat Pro 의 경우 vb6 에서 참조 를 통해 Adobe Acrobat 8.0 Type Library 를 사용할때와 vbs 에서 CreateObject("") 를 통해 개체를 만들때 이름이 조금씩 다르다.
크게 다른것은 아니니까 당황하지 않길 바란다. 참고로 Acrobat SDK 에 포함된 문서의 모든 예제는 CreateObject() 를 이용하고 있다.



▶개발시 주의할 점.
아래 소스는 개발전 테스트를 위해 만든 소스 이다. 참고하길 바란다.

Option Explicit

Dim gApp, gAVDoc, gPDFDoc
Dim gPDFSel, gPDFRect, gPDFPV
Dim gPDFPage, gPDFHL

Set gApp = CreateObject("AcroExch.App")
gApp.Hide   '화면이 뜨는 경우를 막기 위해서 명시적으로 처리함.
Set gPDFRect = CreateObject("AcroExch.Rect")
Set gPDFHL = CreateObject("AcroExch.HiliteList")

Set gAVDoc = CreateObject("AcroExch.AVDoc")
gAVDoc.Open "C:\Documents and Settings\cynosure\바탕 화면\[007디자인1면]2009010185-16.pdf", ""
Set gPDFDoc = gAVDoc.GetPDDoc
Set gPDFPV = gAVDoc.GetAVPageView
Set gPDFPage = gPDFPV.GetPage

'내용 추출
gPDFHL.Add 0, 32767
Set gPDFSel = gPDFPage.CreatePageHilite(gPDFHL)

Dim lngint
Dim strValue
For lngint = 0 To gPDFSel.GetNumText - 1
   strValue = strValue & gPDFSel.GetText(lngint)
Next


'jpg 파일 저장
Dim aa
Set aa = gPDFDoc.GetJSObject()
Call aa.SaveAs("/d/kk.jpg", "com.adobe.acrobat.jpeg")  'Acrobat 6.0 부터 safe path 가 적용됨. 자세한 내용은 js_api_reference.pdf 의 "safe path" 를 참조하세요.
Set aa = Nothing


'썸네일 생성
Dim objIO
Set objIO = CreateObject("ImageMagickObject.MagickImage.1")
Call objIO.Convert("d:\kk.jpg", "-resize", "50x36^!", "d:\t_kk.jpg")   'command line 명령어중 스페이스를 기준으로 나눔
Set objIO = Nothing


'마무리
gAVDoc.Close False
gApp.Exit


'개체 삭제
Set gPDFHL = Nothing
Set gPDFRect = Nothing
Set gPDFDoc = Nothing
Set gAVDoc = Nothing
Set gApp = Nothing

'MsgBox "완료"




+ Recent posts