257 lines
6.1 KiB
Go
257 lines
6.1 KiB
Go
package charmap
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
|
|
"git.sdf.org/CRThaze/vtTools/encoding/codetable"
|
|
mnemonic "git.sdf.org/CRThaze/vtTools/encoding/decMSCMnemonics"
|
|
equiv "git.sdf.org/CRThaze/vtTools/equivalence"
|
|
|
|
"golang.org/x/text/encoding"
|
|
"golang.org/x/text/transform"
|
|
)
|
|
|
|
var (
|
|
ErrUnboundCodepage = errors.New("Codepage is not bound to a codespace")
|
|
)
|
|
|
|
type transformer interface {
|
|
transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
|
|
}
|
|
|
|
type Codepage struct {
|
|
charSet CharacterSet
|
|
codeSpace *codetable.GraphicCodeSpace
|
|
}
|
|
|
|
func NewCodepage(set CharacterSet, graphicTable *codetable.GraphicCodeSpace) Codepage {
|
|
return Codepage{
|
|
charSet: set,
|
|
codeSpace: graphicTable,
|
|
}
|
|
}
|
|
|
|
type DynamicCodepage struct {
|
|
Codepage
|
|
}
|
|
|
|
func NewDynamicCodepage(set CharacterSet) DynamicCodepage {
|
|
e := DynamicCodepage{
|
|
Codepage{charSet: set},
|
|
}
|
|
return e
|
|
}
|
|
|
|
func (p *DynamicCodepage) Bind(graphicTable *codetable.GraphicCodeSpace) {
|
|
if graphicTable != nil {
|
|
p.codeSpace = graphicTable
|
|
return
|
|
}
|
|
p.codeSpace = nil
|
|
}
|
|
|
|
func (p *DynamicCodepage) IsBoundTo(graphicTable *codetable.GraphicCodeSpace) bool {
|
|
if p.codeSpace != nil && p.codeSpace == graphicTable {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *DynamicCodepage) BoundTo() *codetable.GraphicCodeSpace {
|
|
return p.codeSpace
|
|
}
|
|
|
|
type ExtendedCodepage struct {
|
|
base Codepage
|
|
supplemental Codepage
|
|
}
|
|
|
|
func NewExtendedCodepage(sevenBitSet, eighthBitSet CharacterSet) ExtendedCodepage {
|
|
return ExtendedCodepage{
|
|
base: Codepage{
|
|
charSet: sevenBitSet,
|
|
codeSpace: &codetable.GLTable,
|
|
},
|
|
supplemental: Codepage{
|
|
charSet: eighthBitSet,
|
|
codeSpace: &codetable.GRTable,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (p Codepage) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
srcBuf := bytes.NewBuffer(src)
|
|
for r, sbn, e := srcBuf.ReadRune(); e != io.EOF; r, sbn, e = srcBuf.ReadRune() {
|
|
transBytes := make([]byte, 1, 1)
|
|
if e != nil {
|
|
err = e
|
|
return
|
|
} else if r == '\uFFFD' && sbn == 1 {
|
|
transBytes[0] = mnemonic.SUB // MSC/ASCII Substituion character.
|
|
} else {
|
|
transBytes[0] = p.SafeTrans(r)
|
|
}
|
|
nSrc += sbn
|
|
for i, b := range transBytes {
|
|
nextIndex := nDst + i
|
|
if len(dst) < nextIndex+1 {
|
|
err = transform.ErrShortDst
|
|
return
|
|
}
|
|
dst[nDst+i] = b
|
|
nDst++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (p ExtendedCodepage) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
srcBuf := bytes.NewBuffer(src)
|
|
for r, sbn, e := srcBuf.ReadRune(); e != io.EOF; r, sbn, e = srcBuf.ReadRune() {
|
|
transBytes := make([]byte, 1, 1)
|
|
if e != nil {
|
|
err = e
|
|
return
|
|
} else if r == '\uFFFD' && sbn == 1 {
|
|
transBytes[0] = mnemonic.SUB // MSC/ASCII Substituion character.
|
|
} else {
|
|
if tb, e := p.base.Trans(equiv.Normalize(r)); e == nil {
|
|
transBytes[0] = tb
|
|
} else {
|
|
transBytes[0] = p.supplemental.SafeTrans(r)
|
|
}
|
|
}
|
|
nSrc += sbn
|
|
for i, b := range transBytes {
|
|
nextIndex := nDst + i
|
|
if len(dst) < nextIndex+1 {
|
|
err = transform.ErrShortDst
|
|
return
|
|
}
|
|
dst[nDst+i] = b
|
|
nDst++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
type vtEncEncoder struct {
|
|
transform.NopResetter
|
|
charmap transformer
|
|
}
|
|
|
|
func (en vtEncEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
return en.charmap.transform(dst, src, atEOF)
|
|
}
|
|
|
|
type vtEncDecoder struct {
|
|
transform.NopResetter
|
|
charmap Codepage
|
|
}
|
|
|
|
func (d vtEncDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
// TODO: Implement decode by looking up rune in charmap.
|
|
return 0, 0, nil
|
|
}
|
|
|
|
func (p Codepage) NewDecoder() *encoding.Decoder {
|
|
return &encoding.Decoder{
|
|
Transformer: vtEncDecoder{},
|
|
}
|
|
}
|
|
|
|
func (p Codepage) NewEncoder() *encoding.Encoder {
|
|
return &encoding.Encoder{
|
|
Transformer: vtEncEncoder{},
|
|
}
|
|
}
|
|
|
|
func (p ExtendedCodepage) NewDecoder() *encoding.Decoder {
|
|
return &encoding.Decoder{
|
|
Transformer: vtEncDecoder{},
|
|
}
|
|
}
|
|
|
|
func (p ExtendedCodepage) NewEncoder() *encoding.Encoder {
|
|
return &encoding.Encoder{
|
|
Transformer: vtEncEncoder{},
|
|
}
|
|
}
|
|
|
|
func (p Codepage) Trans(r rune) (byte, error) {
|
|
if p.codeSpace == nil {
|
|
return 0, ErrUnboundCodepage
|
|
}
|
|
if b, ok := decMSCControlFull[r]; ok {
|
|
return b, nil
|
|
} else if xy := p.charSet.lookup.Get(r); xy != nil {
|
|
// Discarding the error since we are confident
|
|
// the coordinate will not be out of range.
|
|
b, _ = p.codeSpace.Get(xy[0], xy[1])
|
|
return b, nil
|
|
}
|
|
return 0, errors.New("No translation for rune.")
|
|
}
|
|
|
|
func (p Codepage) MustTrans(r rune) byte {
|
|
if b, err := p.Trans(r); err == nil {
|
|
return b
|
|
}
|
|
return mnemonic.SUB
|
|
}
|
|
|
|
func (p Codepage) SafeTrans(r rune) byte {
|
|
return p.MustTrans(equiv.Normalize(r))
|
|
}
|
|
|
|
// // TransDynamic first calls Trans. If Trans returns an error, then it attempts to find the rune
|
|
// // anywhere within the GraphicRepetoir. If it is able to find the rune, it adds the necessary
|
|
// // escape sequences to temporarily load the needed character set to properly display the matching
|
|
// // symbol. If none can be found, it retuns the same fall-back code-point as MustTrans.
|
|
// func (e vtEncEncoder) TransDynamic(r rune) []byte {
|
|
// if s, err := vt.Trans(r); err == nil {
|
|
// return s
|
|
// }
|
|
// if vt.graphicRegistry[2].register.designated {
|
|
// if vt.graphicRegistry[2].register.set.lookup.Has(r) {
|
|
// if bs, err := vt.SingleShiftRune(2, r); err == nil {
|
|
// return bs
|
|
// } else {
|
|
// panic(err)
|
|
// }
|
|
// }
|
|
// }
|
|
// if vt.graphicRegistry[3].register.designated {
|
|
// if vt.graphicRegistry[3].register.set.lookup.Has(r) {
|
|
// if bs, err := vt.SingleShiftRune(3, r); err == nil {
|
|
// return bs
|
|
// } else {
|
|
// panic(err)
|
|
// }
|
|
// }
|
|
// }
|
|
// for i, set := range vt.graphicRepetoire {
|
|
// if set == nil || !set.lookup.Has(r) {
|
|
// continue
|
|
// }
|
|
// // TODO: Don't leave this in the register.
|
|
// if err := vt.DesignateCharSet(i, 3); err != nil {
|
|
// panic(err)
|
|
// }
|
|
// if bs, err := vt.SingleShiftRune(3, r); err == nil {
|
|
// return bs
|
|
// } else {
|
|
// panic(err)
|
|
// }
|
|
// }
|
|
// return UNKNOWN[:]
|
|
// }
|
|
|
|
// // SafeTrans uses an equivalency table for the given rune to determine a similiar rune that is
|
|
// // present in the GraphicRepetoir. It then calls TransDynamic with that rune.
|
|
// func (e vtEncEncoder) SafeTransDynamic(r rune) []byte {
|
|
// return vt.TransDynamic(requiem.Equivalence.Normalize(r))
|
|
// }
|