ColdFusion에서의 QRCode 생성 이미지 Masking - 1

ColdFusion(또는 Java)에서 QR코드를 생성하려면 대표적인게 구글의 zxing이 있겠지만 딱 한가지 단점이 있습니다. zxing의 경우 QR코드의 색상은 변경이 가능하지만 배경을 흰바탕으로만 만들어져서 QR코드를 디자인 등에 활용하기에 제약이 있습니다. 

 

반면 주로 PDF의 생성 등으로 활용하는 iText를 이용하면 QR코드의 배경을 투명하게 처리할 수 있는데요, 현재 iText가 버전이 7.X대로 업그레이드 되어 있지만 구버전인 5.X대도 현재의 ColdFusion 서버들 예를 들어 Adobe Coldfusion 2018, Lucee 5.x에서도 그대로 활용가능합니다. 물론 zxing은 QR코드의 해석에도 활용되므로 오늘의 코드에서도 필요한 라이브러리입니다.

 

ColdFusion에서 생성한 QR코드

 

오늘은 Adobe ColdFusion 2018 서버에 iText와 zxing을 적용하는 방법, CFML(ColdFusion Markup Language)로 QR코드를 생성하는 방법, 생성된 QR코드를 다른 이미지와 합성해 보는 방법, 그리고 마지막으로 생성된 QR코드가 Reader 앱 등으로 정확히 인식되는지 확인하는 방법에 대해 알아볼까 합니다. 

 

먼저 Adobe ColdFusion 2018 서버를 자신의 운영체제 맞는 버전으로 다운로드 해야 합니다. 서버운영체제에서의 실제 운영환경이라면 설치버전을 다운로드해야 겠지만 개발자를 위한 익스프레스 버전을 다운로드하면 압축만 풀어 바로 사용할 수 있습니다. 

 

Adobe ColdFusion 2018 Express Download : Windows64bit, Linux64bit, Mac, WAR64(Tomcat 등 기존 서블릿엔진용)

(참조 : https://www.adobe.com/kr/products/coldfusion-family.html)

 

익스프레스버전의 경우 Tomcat 등과 같은 일반 Java 서블릿 엔진처럼 실행합니다. 압축을 푼 경로내의 cfusion/bin 디렉토리네의 cfstart.exe(Linux, Mac의 경우 cfstart.sh)가 서버의 시작 실행파일이고 같은 디렉토리내의 cfstop.exe(Linux, Mac의 경우 cfstop.sh)가 서버의 중지 실행파일입니다. Windows라면 CMD창에서 Linux나 Mac이라는 Shell에서 실행하시면 됩니다. (마우스 더블클릭은 당연 안됩니다. ^^;;)

 

여기까지 가볍게 Adobe ColdFusion 2018 Express의 설치와 실행/중지까지 알아봤습니다. 실행하셨다면 다시 중지해주시고, 다음의 iText와 zxing을 설치할 차례입니다.

 

iText의 경우 5.X버전을 사용할 예정이므로 해당 배포사이트에서 다운로드합니다. 참고로 5.X는 더이상 버전 업데이트가 없고 7.X가 현재의 최신버전이지만 필요없는 부분이 많고 PDF가 아닌 QR코드용으로만 사용할 예정이기에 5.X대를 사용하려고 합니다. 7.X대에서는 오늘 작성할 CFML코드가 작동하지 않습니다. 

 

iText 5.5.31 Download : https://github.com/itext/itextpdf/releases/tag/5.5.13.1 에서 itextpdf-5.5.13.1.zip 파일을 다운로드 한 후 itextpdf-5.5.13.1.jar 파일만 추출 합니다.

 

Zxing 3.4.0 Download : https://mvnrepository.com/artifact/com.google.zxing 에서 ZXing CoreZXing Java SE Extensions 두 파일을 다운로드 하여 zxing-core-3.4.0.jar, zxing-javase-3.4.0.jar를 각각 추출합니다. 공식 zxing 웹사이트에서는 소스코드만 배포하기에 미리 만들어진 아카이브 파일을 활용하고자 jar파일을 다운로드 하는 것 입니다.

 

모든 파일을 준비하면 각각 itextpdf-5.5.13.1.jar, zxing-core-3.4.0.jar, zxing-javase-3.4.0.jar 총 3개의 파일이 준비됩니다.

 

위 3개의 파일을 Adobe ColdFusion 2018 서버의 cfusion/lib 디렉토리에 복사해 줍니다. 그런 다음 서버를 시작해 줍니다. 이제 모든 준비는 끝났습니다.  

 

Adobe ColdFusion 2018 Express의 홈 디렉토리는 cfusion/wwwroot/ 이므로 이 곳에 작성한 cfm파일을 넣어주면 됩니다. 이미 아시겠지만 cfm파일이 바로 php나 jsp와 같은 ColdFusion에서 해석하는 웹스크립트 파일입니다. Adobe ColdFusion 서버나 오픈소스로 배포되는 Lucee 같은 것이 바로 ColdFusion Markup Language를 해석하는 Web Application Server인 것이죠. 마치 Tomcat처럼요. 이에 대한 내용은 차후 자세히 더 다루어 보겠습니다. 

 

접속주소는 http://127.0.0.1:8500/ 입니다. 8500포트는 Adobe ColdFusion 서버에서 사용하는 내장 기본 웹서버의 포트입니다.

 

이제 코드를 작성할 차례입니다. 첨부된 qrcode.cfm 파일의 코드는 다음과 같습니다. 

<cfprocessingdirective pageencoding="UTF-8" />
<cfcontent type="text/html; charset=UTF-8" />

<cftry>
	
	<!--- QRCode Size --->
	<cfset qrsize = 500 />
	
	<!--- QRCode R,G,B,Alpha --->
	<cfset qrColor = "0,0,0,255" />
	
	<!--- Background R,G,B,Alpha --->
	<cfset bgColor = "255,255,255,0" />
	
	<!--- QRCode Text --->
	<cfset QRText = "콜드퓨전" />
	
	<!--- 한글디코딩을 위해 QRText문자열 UTF-8를 ISO-8859-1로 변경 --->
	<cfset initText = createobject("java", "java.lang.String").init(#QRText#) />
	<cfset toISOstring = createobject("java", "java.lang.String").init(initText.getbytes('UTF-8'),'ISO-8859-1').tostring() />
	
	<!--- QR코드의 생성 --->
	<cfset javacolor = createObject("java","java.awt.Color") />
	<cfset qrcode = createObject('java','com.itextpdf.text.pdf.BarcodeQRCode').init(#toISOstring#,#qrsize#,#qrsize#,JavaCast('null','')) />
	<cfset qrcolor = javacolor.init(JavaCast("int",#listGetAt(qrColor, 1)#),JavaCast("int",#listGetAt(qrColor, 2)#),JavaCast("int",#listGetAt(qrColor, 3)#),JavaCast("int",#listGetAt(qrColor, 4)#)) />
	<cfset bgcolor = javacolor.init(JavaCast("int",#listGetAt(bgColor, 1)#),JavaCast("int",#listGetAt(bgColor, 2)#),JavaCast("int",#listGetAt(bgColor, 3)#),JavaCast("int",#listGetAt(bgColor, 4)#)) />
	<cfset imgAWT = qrcode.createAwtImage(qrcolor, bgcolor) />
	<cfset qrcodeImg = ImageNew("", #qrsize#, #qrsize#, "argb") />
	<cfset graphics = ImageGetBufferedImage(qrcodeImg).getGraphics() />
	<cfset graphics.drawImage(imgAWT, 0, 0, javacast("null", "")) />
	
	<!--- QR코드 이미지 저장 --->
	<cfimage action="write" source="#qrcodeImg#" destination="#expandPath('./simple_qrcode.png')#" quality="1" overwrite="true" mode="755" />
	<cfset graphics.dispose() />
	
	<cfcatch>
	<cfoutput>#cfcatch.message#</cfoutput>
	</cfcatch>
</cftry>

<img src="simple_qrcode.png" border="0">

1~2번째 줄은 CFML문서를 유니코드로 처리하기 위한 코드라 실제로 리눅스환경이나 맥환경에서는 없어도 되는 코드입니다. CFML은 기본적으로 HTML의 마크업스러운? 언어를 따라한 쉬운 언어이기에 약간의 웹프로그래밍 코드경력이 있으면 보시는 족족 이해가 될 겁니다. 가로세로 500픽셀 사이즈의 표준QR코드를 생성하는 코드이고, 그 내용은 QRText라는 변수에 담겨있는 "콜드퓨전"이란 단어가 됩니다.

 

<cftry>~ 와 <cfcatch>의 구문은 오류발생시 오류메시지의 출력을 위한 코드이므로 개발단계에서 유용한 구문입니다. 참고로 모든 CFML은 태그와 함수로 구성되어 있고, 태그들은 <cf 라는 접두어를 공통적으로 가집니다. 언어적인 스펙은 차후 따로 정리해 보겠습니다. 

 

위 코드가 실행되면 가로 500픽셀, 세로 500픽셀의 검정색의 QR코드가 생성됩니다. 아래 그림 참조. 단 앞에서 말씀하였듯이 검정 이외의 영역은 실제로는 투명처리된 PNG이미지 입니다. Zxing으로도 QR코드는 생성할 수 있지만 배경을 투명으로 처리하지 못하기에 이 다음에서 살펴볼 다른 이미지와의 합성을 할 수 없기에 iText를 활용하여 QR코드를 만들었습니다. 

스캔해 보세요. 콜드퓨전

QR코드는 안정적인 인식을 위해 코드부분을 제외하고 상하좌우면에 일정 공간을 비우게 되는데 위 이미지에서도 실제로는 약 70픽셀의 공간이 상하좌우에 적용되어 실제 검정 부분의 면적이 작게 보입니다. 

 

위 코드에서도 변수 QRText 부분에 어떤 URL을 넣게되면 링크가 걸리는 QR코드를 생성하여 특정 모바일 페이지 등으로 연결할 수 있겠죠?

간단하게 QR코드를 생성해 봤고, 이제는 특정 이미지와 QR코드를 합성하는 코드를 만들어 봅시다. 

 

QR코드와 이미지를 합성하여 봅시다!

mask.cfm의 코드는 다음과 같습니다. 

<cfprocessingdirective pageencoding="UTF-8" />
<cfcontent type="text/html; charset=UTF-8" />

<cftry>
	<!--- QR코드이미지와 합성용 백그라운드 이미지로드 --->
	<cfset sourceImage = ImageNew("#ExpandPath('background1.jpg')#") />
	<cfset maskImage = ImageNew("#ExpandPath('simple_qrcode.png')#") />
    
	<!--- AntiAliasing 활성 --->
	<cfset ImageSetAntiAliasing(maskImage, "on") />
    
	<!--- 검정색 즉, QRCode색상부분만 Mask --->
	<cfset ImageSetDrawingColor(maskImage, "##000000") />
	<cfset maskGraphics = ImageGetBufferedImage(maskImage).getGraphics() />
	<cfset AlphaComposite = createObject("java", "java.awt.AlphaComposite") />
	<cfset maskGraphics.setComposite(AlphaComposite.SrcIn) />
	<cfset maskGraphics.drawImage(ImageGetBufferedImage(sourceImage),
		   javacast("int", 0), javacast("int", 0), javacast("null", "")) />
	<cfset maskGraphics.dispose() />
    
    <!--- 새로운 QR코드이미지 저장 --->
	<cfset ImageWrite(maskImage,"#expandPath('./masked1.png')#") />
<cfcatch>
	<cfoutput>#cfcatch.message#</cfoutput>
</cfcatch>
</cftry>

<img src="masked1.png" border="0">

위 코드는 아까 생성한 QR코드 이미지와 백그라운드 합성용 이미지를 불러와서 QR코드 이미지의 검정 부분만 백그라운드 이미지로 마스킹을하여 새로운 이미지를 만들어 내는 코드입니다. 결과는 아래와 같이 나오죠.

마스킹된 새로운 QR코드

위 QR코드 이미지는 보기에 예쁘지만 실제로는 인식이 안됩니다. 왜냐하면 QR코드의 표준에 맞지 않아 코드부분과 배경부분의 명암차 등이 명확하게 인지되지 못하기 때문이죠. 하지만 인식이 되는 이미지도 있습니다. 아래와 같은 사막 이미지는 색상이 단조롭고 명암차이 크지 않아 배경과 구분하기 쉽기 때문입니다. 원칙은 흰바탕에 검정코드영역이여야 하지만 이런 활용이 가능한 이유는 QR코드는 자가복원력이 크기 때문에 일부분이 찢겨나가거나 이물질이 묻어도 인식이 잘 됩니다.

인식이 잘 되는 사막 이미지가 마스킹된 QR코드

자, 이제 마지막으로 특정 QR코드에 대해 모바일앱 등이 아닌 CFML코드상으로 QR코드를 해석하는 해석기를 만들어 봅시다. 이때 위에서 Adobe ColdFusion 2018서버의 lib 디렉토리에 넣어준 zxing관련 2개의 라이브러리를 사용합니다.

 

qrreader.cfm의 코드는 다음과 같습니다. 

<cfprocessingdirective pageencoding="UTF-8" />
<cfcontent type="text/html; charset=UTF-8" />

<cftry>
	<!--- 해석할 QR코드 이미지 불러오기 --->
	<cfimage source="#expandPath('./masked2.png')#" name="decodeQrcode">
    
	<cfset buff = ImageGetBufferedImage(decodeQrcode) />
	<cfset source = createObject("java","com.google.zxing.client.j2se.BufferedImageLuminanceSource").init(buff) />
	<cfset binarizer = createObject("java","com.google.zxing.common.GlobalHistogramBinarizer").init(source) />
	<cfset bitmap = createObject("java","com.google.zxing.BinaryBitmap").init(binarizer) />
	<cfset reader = createObject("java","com.google.zxing.qrcode.QRCodeReader").init() />
	<cfset decodedResult = reader.decode(bitmap, javacast("null", "")) />
	<cfset scanResultText = decodedResult.getText()>
    
    <!--- 해석된 내용 출력 --->
	<cfoutput>#scanResultText#</cfoutput>
    <cfcatch></cfcatch>
</cftry>

위 코드를 실행하면 아래 그림과 같이 맨 위 QR코드를 생성할 때 넣어준 내용인 "콜드퓨전"이 화면에 출력됩니다. 인식이 안되면 아무것도 나오지 않지요. 아래 그림 참조.

zxing으로 동적으로 QR코드를 해석해 볼 수 있습니다.

여기까지 간단하게 QR코드를 생성하고 다른 이미지와 합성하고, 그 QR코드를 동적으로 인식하는 것 까지 해봤습니다. 이 내용은 수년전 제 블로그에서 한번 다룬 내용을 최신의 버전에서 다시 테스트하면서 다시 작성되었습니다. 필요한 코드와 Java라이브러리는 아래에 압축파일로 제공해드립니다. 필요하신 분은 다운로드 하시길 바랍니다. 감사합니다.

 

cfml_qrcode.zip
3.13MB

Comments 0