文具店 2025-05-01 10:00:00 端末取引ID:23 スタッフ:テスト 万年筆 ¥5,000 小計 ¥5,000 税 ¥500 合計 ¥5,500 No.123456789
HTMLのようなテーブルや回り込みはない。
文字幅を計算して必要な半角スペースを入れている。
万年筆 ¥5,000
日本語はShift-JISでエンコードしている。
Shift-JISでは、全角文字は2バイト、半角文字は1バイト。
半角文字の幅を1とするとバイト数と文字幅が一致する。
万年筆 (3文字 × 2バイト) = 6バイト = 6幅
aにグレイヴ・アクセントを付けた文字。
フランス語、イタリア語で使われる。
oとeが合わさった文字。
フランス語で使われる。
外国語に対応しているエンコードに変更することで印字できる。
→ 日本語と外国語が混在できない。
Unicode (UTF-8) に対応したプリンターが登場。
日本語部分が3バイト、「¥」が2バイトのためずれる。
→ Shift-JISに存在しない全角文字が多数あるため不正確。
拡張漢字、絵文字、特殊記号、全角記号など
フォントデータ (.ttf, .otf)) には文字の情報も含まれている。
レシートプリンターで使用されるフォントは専用のもの。
→ 探した限りではダウンロードできるデータはなかった。
東アジアの文字幅というUnicode標準の付属書がある。
従来の文字コードからの移行時に互換性を保つために定義された。
慣習的な文字幅に合わせた特性が割り当てられている。
互換分解特性 を持つ互換文字。
全角英数など。
半角カナなど。
互換文字以外のいわゆる全角であったもの。
ひらがな、カタカナ、漢字、句読点など。
互換文字以外の対応するいわゆる全角の文字が存在したもの。
半角英数など。
文脈によって文字幅が異なる文字。
ギリシア文字やキリル文字など。
全角でも半角でもない文字。
アラビア文字など。
Shift-JIS 約 8,000 文字 Unicode 約 150,000 文字
F (Fullwidth) 約 1,800 文字 W (Wide) 約 87,000 文字 H (Halfwidth) 約 600 文字 Na (Narrow) 約 10,000 文字 A (Ambiguous) 約 6,000 文字 N (Neutral) 約 44,000 文字
曖昧 (A) と中立 (N) は実際に印字してみるしかない。
ラテン文字 à 分数 ½
ギリシャ文字 α キリル文字 Ж 罫線 ┼ 数学記号 ∑ セクションマーク § パーミル ‰ オーム Ω ローマ数字 Ⅻ 矢印 ↔
ヘブライ文字 א マイクロ µ オングストローム Å
音声記号 ʃ 絵文字 💙 絵文字 🔟
完璧に判定する方法はないので妥協する。
一部の文字は文字幅を小さく計算される。
Shift-JISに存在しないが全角で印字されるもの。
https://github.com/ukitaka/EastAsianWidth.swift
public extension UnicodeScalar { public var isEastAsianWide: Bool { switch self.value { case 0x1100...0x115F: return true ... } } public var isFullwidth: Bool { return isEastAsianFullwidth || isEastAsianWide } }
extension String { func countByEastAsianWidth() -> Int { func accumulate(count: Int, unicodeScalar: UnicodeScalar) -> Int { if String(unicodeScalar).lengthOfBytes(using: .shiftJIS) > 0 { return count + String(unicodeScalar).lengthOfBytes(using: .shiftJIS) } else { return count + (unicodeScalar.isFullwidth ? 2 : 1) } } return self.unicodeScalars.reduce(0, accumulate) } }
日本語のみの時は実現できている。
コードの変更量も少ない。
調査・検証には多くの時間がかかっている。
事前には知識が足りていないため見積もりも難しい。
完璧に実現するのはとても難しい。
どこで妥協するかを決める必要がある。
以上