なんじゃくにっき

プログラミングの話題中心。

GAE/JアプリでPDF出力(3) 〜バーコードをPDF出力〜

GAE/JアプリでPDF形式でバーコードを出力する。
今の所対応するコードはEAN(JAN), CODE39, CODE128。但しCODE128の制御文字は扱えない。
 
 ↓↓GAE上で暫定公開中↓↓
http://t2-pdf.appspot.com/
 

フレームワーク/ライブラリは T2FrameworkiText を使用。
昨日の記事で取り上げたRhinoは最初使っていたが、コードの量を減らすために最終的には使わないことにした。

以下、ソース。 


T2のNavigator。Directクラスを継承。contenttypeを指定できるようにしてある。
package examples.navigation;

import java.io.File;
import java.io.InputStream;
import javax.servlet.http.HttpServletResponse;

import org.t2framework.commons.util.Assertion;
import org.t2framework.t2.contexts.WebContext;
import org.t2framework.t2.navigation.Direct;

public class CustomDirect extends Direct{
protected String contextType;

public CustomDirect(byte[] bytes) {
super(bytes);
}

public CustomDirect(File file) {
super(file);
}

public CustomDirect(InputStream is) {
super(is);
}

protected CustomDirect setContextType(String contextType){
this.contextType = contextType;
return this;
}

public static CustomDirect from(File file, String contextType) {
return new CustomDirect(Assertion.notNull(file)).setContextType(contextType);
}

public static CustomDirect from(byte[] bytes, String contextType){
return new CustomDirect(Assertion.notNull(bytes)).setContextType(contextType);
}

public static CustomDirect from(InputStream is, String contextType){
return new CustomDirect(Assertion.notNull(is)).setContextType(contextType);
}

@Override
public void execute(WebContext context) throws Exception {
final HttpServletResponse response =
context.getResponse().getNativeResource();
response.setContentType(contextType);
super.writeTo(response, response.getOutputStream());
}
}

 

T2のPageクラス
package examples.page;

import java.io.ByteArrayOutputStream;

import org.t2framework.commons.util.Logger;
import org.t2framework.t2.annotation.composite.GET;
import org.t2framework.t2.annotation.core.Default;
import org.t2framework.t2.annotation.core.Page;
import org.t2framework.t2.annotation.core.RequestParam;
import org.t2framework.t2.spi.Navigation;

import com.lowagie.text.Document;
import com.lowagie.text.Font;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfWriter;

import examples.navigation.CustomDirect;
import examples.util.Barcodeutil;

@Page("/barcode")
public class BarcodePage {
@SuppressWarnings("unused")
private static Logger logger = Logger.getLogger(BarcodePage.class);

@SuppressWarnings("unchecked")
@GET
@Default
public Navigation barcode(@RequestParam("caption") String caption,
@RequestParam("codetype") String codetype, @RequestParam("addcd") String addcd,
@RequestParam("code") String code) throws Exception {

if (caption != null)
caption = caption.replace("&", "&");
code = code.replace("&", "&");
final boolean isAddCD = Boolean.parseBoolean(addcd);
System.out.println(code);
//ID2 = A7 Rotated
Document doc = new Document(PageSize.ID_3, 10, 10, 10, 10);
ByteArrayOutputStream byteout = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(doc, byteout);
doc.open();

// Add Caption
BaseFont basefont = BaseFont.createFont("HeiseiKakuGo-W5",
"UniJIS-UCS2-H", false);
doc.add(new Paragraph(caption, new Font(basefont, 12)));

// Add Barcode Image
Class clazz = Class.forName("com.lowagie.text.pdf." + codetype);
doc.add(Barcodeutil.getInstance().getImage(clazz, code, isAddCD, writer));

doc.close();

return CustomDirect.from(byteout.toByteArray(), "application/pdf");
}
}

 

バーコードを取り扱うためのSingletonクラス
package examples.util;

import com.lowagie.text.Image;
import com.lowagie.text.pdf.Barcode;
import com.lowagie.text.pdf.PdfWriter;

public class Barcodeutil{
private static Barcodeutil instance = new Barcodeutil();
private Barcodeutil() {}

public static Barcodeutil getInstance() {
return instance;
}

public <T extends Barcode> Image getImage(Class<T> type,
String code, boolean isGenerateCheckSum, PdfWriter writer)
throws Exception{
T barcode = type.newInstance();
barcode.setGenerateChecksum(isGenerateCheckSum);
barcode.setCode(code);

if (type == com.lowagie.text.pdf.BarcodeEAN.class) {
switch (code.length()) {
case 13:
barcode.setCodeType(Barcode.EAN13);
break;
case 8:
barcode.setCodeType(Barcode.EAN8);
break;
}
}

return barcode.createImageWithBarcode(writer.getDirectContent(),
null, null);
}
}

 

コードチェック用JavaScript
var code39chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
var code128chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvexyz"
+ " !?#$%&'()*+,-./:;<=>?@[\]^_`{|}~"

function getCode39Checkdigit(arg){
var sum = 0;
for(i = 0; i < arg.length; i++){
var num = code39chars.indexOf(arg.charAt(i));
if (num < 0)
return null;
sum += num;
}
return code39chars.charAt(sum % 43);
}

function isValidCode39Code(arg){
return (getCode39Checkdigit(arg) != null);
}

function isValidCode128Code(arg){
for(i = 0; i < arg.length; i++)
if (code128chars.indexOf(arg.charAt(i)) < 0)
return false;

return true;
}

  


入力画面のJSP(といってもほぼ素のHTML+JavaScript
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/JavaScript" />
<title>T2 PDF</title>
<script type="text/javascript" src="js/barcodeutil.js"></script>
<script type="text/javascript">
<!--
var ErrIllegalChar = "Contains illegal character(s)";
var ErrIllegalLength = "EAN code length must be 8 or 13";

function onCodeTypeComboChange(){
document.getElementById("digitcheckbox").disabled =
(document.getElementById("CodeTypeCombo").value != "Barcode39");
}

function onSendButtonClick(){
var code = document.getElementById("CodeEdit").value;
if (code == "")
return;

var caption = document.getElementById("TitleEdit").value;
var codetype = document.getElementById("CodeTypeCombo").value;

if (codetype == "BarcodeEAN"){
if (code.match(new RegExp("[^0-9]"))){
alert(ErrIllegalChar);
return;
}
if ((code.length != 8)&&(code.length != 13)){
alert(ErrIllegalLength);
return;
}
} else if (codetype == "Barcode39"){
code = code.toUpperCase();
if (!isValidCode39Code(code)){
alert(ErrIllegalChar);
return;
}
} else {
if (!isValidCode128Code(code)){
alert(ErrIllegalChar);
return;
}
}

var targeturl = "/barcode";
var param = "?caption=" + encodeURIComponent(caption)
+ "&codetype=" + codetype
+ "&code=" + encodeURIComponent(code)
+ "&addcd=" + document.getElementById("digitcheckbox").checked;

window.open(targeturl + param, "_blank");
}
// -->
</script>

</head>
<body>
<p>Barcode Generator</p>
<form name="Form1" method="GET" action="">
<table>
<tr>
<td>Caption:</td>
<td>
<input type="text" id="TitleEdit" maxlength="30" />
</td>
</tr>
<tr>
<td>Codetype:</td>
<td>
<select id="CodeTypeCombo" onChange="onCodeTypeComboChange()">
<option value="BarcodeEAN" SELECTED>EAN</option>
<option value="Barcode39">CODE39</option>
<option value="Barcode128">CODE128</option>
</select>
</td>
</tr>
<tr>
<td>CheckDigit:</td>
<td>
<input TYPE="checkbox" id="digitcheckbox" value="true" disabled="true"/>AddCheckDigit
</td>
</tr>
<tr>
<td>Code:</td>
<td>
<input TYPE="text" id="CodeEdit" style="ime-mode:inactive" maxlength="30" />
</td>
</tr>
<tr>
<td></td>
<td>
<input type="button" name="SendButton" value="Generate Barcode" onClick="onSendButtonClick()">
</td>
</tr>
</table>
</form>
<p>Notes: This Application can't deal with Code128 control characters.</p>
</body>
</html>

 
 
これからやる(やりたい)こと:
 ・cssでデザインを整える
 ・JavaDoc書く
 ・SDLoaderで単体起動できるようにする
 ・Antの使い方を覚える
 
多分やらないこと
 ・JavaScriptでTextInputのonKeyEnter()で入力制限
  →ブラウザ毎に動きが違うから面倒