文字列配置プログラムAdjustStringOvf.java
まず、指定範囲に入りきらない場合も強行する方向で作ります。指定範囲が十分な幅を持つ場合には予想通りに振る舞います。
指定範囲が十分な幅を持たない場合には、均等割付けは文字を重ねて印字し、その他は指定範囲からはみ出して印字します。これはこのようになることを望んだわけではなく、アルゴリズム上、自然にこうなります。文字列に対して常に十分な範囲を指定できるならば、このプログラムで十分です。また、均等割付けについてはこの方が都合の良い場合もあるかもしれません。
プログラムです。
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.image.BufferedImage;
public class AdjustStringOvf {
String str;
int cpct; //文字数
float wdmm; //width in mm
float remm = 0f; //remain in mm;
float gapmm = 0f;
Graphics2D g2;
FontMetrics fm;
float mm2pt = 72/25.4f;
float pt2mm = 25.4f/72;
/** areawidth に str を入れる時の余り remm を計算 */
public AdjustStringOvf(Graphics2D g2, String str, float areawidth) {
this.g2 = g2;
fm = g2.getFontMetrics();
this.str = (str!=null) ? str : ""; //nullなら""
wdmm = (areawidth>=0f) ? areawidth : 0f; //負なら0
cpct = str.codePointCount(0,str.length()); //strのcp数
remm = wdmm - fm.stringWidth(str)*pt2mm; //あまりを計算
gapmm = remm/(cpct-1);
}
/** 文字列・余り・文字数 をレポートする。(デバッグ用) */
public String report(){
String rstr = String.format("str=%s /remm=%f /str.length()=%d /cpct=%d",
str, remm, str.length(), cpct);
return rstr;
}
/** areawidth のなかに収まるかを答える */
public boolean isFitInto(){
return remm>=0;
}
/** 文字列の幅を引いたあまりをmmで返す */
public float getRemain(){
return remm;
}
/** 文字間の隙間をmmで返す */
public float getGap(){
return gapmm;
}
/** 文字列を左寄せで描画する */
public void drawLeft(float hm, float vm) {
g2.drawString(str,hm*mm2pt,vm*mm2pt);
}
/** 文字列を右寄せで描画する */
public void drawRight(float hm, float vm) {
g2.drawString(str,(hm+remm)*mm2pt,vm*mm2pt);
}
/** 文字列をセンタリングで描画する */
public void drawCenter(float hm, float vm) {
g2.drawString(str,(hm+remm/2)*mm2pt,vm*mm2pt);
}
/** 文字列を均等割付で描画する */
public void drawKintou(float hm, float vm) {
float hpp = 0;
int strlen = str.length();
if (cpct>1){
int i=0;
int nexti = 0;
int ct = 0;
while (strlen>i){
nexti = str.offsetByCodePoints(i,1);
g2.drawString(str.substring(i,nexti),(hm+gapmm*ct)*mm2pt+hpp,vm*mm2pt);
hpp = fm.stringWidth(str.substring(0,nexti)); //pt
i=nexti;
ct++;
}
}else{
drawCenter(hm, vm);
}
}
public static void main( String[] args ) {
float mm2pt = 72/25.4f;
BufferedImage buffimg = new BufferedImage(800,600,BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = buffimg.createGraphics();
float w = (args.length>0) ? Float.parseFloat(args[0]):50f;
String str="東西【丈`】南北";
System.out.println("文字列="+str+" /枠幅(mm)="+w);
float x = 30.1f;
float y = 30.2f;
float h = 16.3f;
AdjustStringOvf ads = new AdjustStringOvf(g2,str,w);
System.out.println(ads.report());
System.out.println(ads.getGap());
ads.drawKintou(x,y+h);
}
}
mainメソッドは簡易な確認のために付けてあります。環境が許せば、【丈`】をサロゲートペアを使う本来の字に入れ替えることができますが、このままでも問題ありません。以下は実行結果です。
adachi@debian64:/media/adachi/S1T/java/print04$ java AdjustStringOvf 文字列=東西【丈`】南北 /枠幅(mm)=50.0 str=東西【丈`】南北 /remm=28.833334 /str.length()=6 /cpct=5 7.2083335
AdjustStringOvf.javaを使って帳票を印刷する例
AdjustStringOvf.javaを使って文字を印字するプログラムです。
A4の用紙に「東西【丈`】南北」を、前項の図「AdjustStringOvfの動作イメージ」のように印刷します。
このプログラムも環境が許せば、【丈`】をサロゲートペアを使う本来の字に入れ替えて実行できます。画面への表示は行いませんので仮想端末がサロゲートペアに対応しなくても、印刷は可能です。
/** PrintableWithAdjustStringOvf.java
AdjustStringOvf.java を使うプログラム例。
PrintRequestAttributeSet と PrinterJobを使い
Service を使わない帳票印刷の典型例
2019-09-25 adachi
*/
import java.awt.*;
import java.awt.print.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
public class PrintableWithAdjustStringOvf implements Printable {
@Override
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (pageIndex != 0) return NO_SUCH_PAGE;
Graphics2D g2 = (Graphics2D)g;
g2.setFont(new Font(Font.SERIF, Font.PLAIN, 36));
float mm2pt = 72/25.4f;
String str="東西【丈`】南北";
float x = 20.1f;
float y = 20.2f;
float w = 100f;
float h = 16.3f;
float padx = 3f;
float pady = 2f;
g2.setColor(Color.BLACK);
AdjustStringOvf ads = new AdjustStringOvf(g2,str,w-padx*2);
ads.drawKintou(x+padx,y+h-pady); //均等割付
ads.drawLeft(x+padx,y+2*h-pady); //左寄せ
ads.drawRight(x+padx,y+3*h-pady); //右寄せ
ads.drawCenter(x+padx,y+4*h-pady); //センタリング
return PAGE_EXISTS;
}
public static void main(String[] args) {
Printable pable = new PrintableWithAdjustStringOvf();
PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet();
MediaSizeName msname = MediaSizeName.ISO_A4; //A4用紙
rqset.add(msname);
MediaSize msize = MediaSize.getMediaSizeForName(msname);
float mwidth = msize.getX(MediaPrintableArea.MM);
float mheight = msize.getY(MediaPrintableArea.MM);
float L = 10.1f;
float R = 10.2f;
float T = 10.3f;
float B = 10.4f;
rqset.add(new MediaPrintableArea(
L, T, (mwidth - L - R), (mheight - T - B),
MediaPrintableArea.MM));
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(pable);
if (pj.printDialog(rqset)) {
try { pj.print(rqset); }
catch (PrinterException e) {
System.err.println(e);
}
}
}
}
プログラムの核心部分は、次の部分です。文字列と印字領域の幅をmm単位で与えてインスタンスを作り、それに対して配置を指定します。
AdjustStringOvf ads = new AdjustStringOvf(g2,str,w-padx*2); ads.drawKintou(x+padx,y+h-pady); //均等割付 ads.drawLeft(x+padx,y+2*h-pady); //左寄せ ads.drawRight(x+padx,y+3*h-pady); //右寄せ ads.drawCenter(x+padx,y+4*h-pady); //センタリング
padxとpadyは余白、hは(x,y)が文字領域の左上を指していることを前提として下に移動するための量です。(x,y)が文字のベースラインの左端を指していることを前提とするならばもっと簡単にads.drawKintou(x,y)と書いてしまって構いません。
