#!/usr/bin/perl

# Copyright (c) 2003 Dohyun Kim <nomos at skuniv.ac.kr>
# Redistribution and use, with or without modification, are permitted
# provided that this coryright notice is retained. If you redistribute
# a modified version, please attach a note about what you have modified.
# 이 저작권 정보를 보존하는 한, 누구나 본 프로그램을 자유롭게 사용, 수정,
# 배포할 수 있습니다. 수정판을 배포하실 경우에는 수정사항을 명기해주시길
# 부탁드립니다.
#
# $Id: ttf2hlatexfont.pl,v 1.5 2005/01/11 09:05:40 nomos Exp $

use strict;
use Getopt::Std;
use File::Copy;
use File::Path;

# command line option을 읽어들임
my %args;
getopts("dopnc:i:",\%args);
unless($args{c}) {
    print <<"ENDOFHELP";
Usage:  $0 -c CONFIGFILE [-p] [-o] [-d]
        $0 -c CONFIGFILE -i TEXMFROOT [-p] [-o]

  -c CONFIGFILE
        obtain font-related configuration from CONFIGFILE and
        make TFMs and settings for HLaTeX. needs ttf2tfm 1.5 or higher
  -c CONFIGFILE -i TEXMFROOT
        install generated files to TEXMFROOT directory according to TDS
  -p    additionally do settings for PDFTeX. install them with -i
  -o    additionally make OFMs and settings for HLambda. install them with -i
        needs a patch to ttf2tfm(ttf2pk) from next URL:
        http://ftp.ktug.or.kr/KTUG/freetype/contrib/ttf2pk-1.5-20020430.patch
  -d    Disable auto-correction of \\MapHangulFamily{}{} in style file

CONFIGFILE syntax example:

    FOUNDRY: xy
    FONTmj: f=bt m=BTmedium.ttf b=BTbold.ttf l=BTlight.ttf
    FONTgt: f=dt m=DTmedium.ttf b=DTbold.ttf
    FONTtz: f=tz m=TZmedium.ttf
    ...
ENDOFHELP

  exit 1;
}

# define global variables
my %SERIES = (
        'm'=>['m','c'],
        'b'=>['b','bc'],
        'l'=>['l'],
        );
my $slant = 'o';
my $cshape = '-e .92';
my $oshape = '-s .167';
my $bshape = '-b .15'; # for dvipdfmx fakebold feature
my @Wsh = qw/w s h/;
my $sansfonts = 'jgt|jsr|jnv|gt|tz|pg|gr|bm|yt|gl'; # for curemaphangulfamily()

# variables for HLaTeX
my $texencoding = 'H';
my $texcmap = 'KSCms-UHC-H';
my $texsfd = 'UKS-HLaTeX';
my $texopt = '-q -L KS-HLaTeX -P 3 -E 1';
my $wansungopt = '-q -l -P 3 -E 5';
my $wansungsfd = 'KS-HLaTeX';
my $textexname = 'testlatex.tex';
my $texteststr = '^^b0^^a1^^b2^^e5^^b5^^cb^^b8^^b1^^ba^^f5^^bd^^db'.
'^^c0^^c1^^c3^^a7^^c5^^eb^^c8^^d1 ^^a8^^b1^^a1^^db^^aa^^a2^^a5^^d2'.
' ^^ca^^a1^^cc^^e5^^cf^^cb^^d2^^b1^^d4^^f5^^d7^^db^^da^^c1^^dd^^a7'.
'^^df^^eb^^e2^^d1^^e5^^b7^^e7^^fb^^ea^^e1^^ed^^c7^^f0^^ad^^f2^^f1'.
'^^f5^^d7^^f8^^bd^^fb^^a3^^fd^^e7';

# varibles for HLambda
my $omegaencoding = 'UHC';
my $omegacmap = 'unicode'; # dvipdfmx 20040912 up is recommended
my $omegasfd = 'Unicode';
my $omegaopt = '-q -P 3 -E 1';
my $omegatexname = 'testlambda.tex';
my $omegateststr = "123 ABC iiiii ^^8c^^63^^b9^^e6^^c4^^bf^^c7^^c7^^99^^f6 ".
"^^a3^^c1^^a3^^b1^^a8^^b1^^a1^^db^^aa^^a2^^a5^^d2 ".
"^^f9^^ca^^ec^^bb^^e3^^c1^^e3^^a7^^f1^^fd `` '' ` ' fi ff ffi fl --- -- -";
my $omegaprefix = 'o';

# 2005-01-10 nomos, variables for dhucs
my $ucsencoding = 'LUC';
# end of 2005-01-10 nomos, variables for dhucs

# 사용자 설정파일을 읽어 %FONTS(hash of hash)에 저장
my($foundry,%FONTS);
open FILE,$args{c} or die "$args{c}: $!\n";
while(<FILE>) {
    s/#.*//;
    if(/FOUNDRY:\s*(\w+)/) {
        $foundry = $1;
    }
    elsif(/OmegaCMap:\s*([\w_\-]+)/) {
#    $omegacmap = $1;
    }
    elsif(/OmegaSFD:\s*([\w_\-]+)/) {
#    $omegasfd = $1;
    }
    elsif(/FONT(\w+?):\s*(.*)/) {
        foreach my $field ( split /\s+/,$2 ) {
            my($key,$value) = split /=/,$field;
            $FONTS{$1}{$key} = $value;
        }
    }
}
close FILE;

my $styname = $foundry.'ttf.sty';
my $pkmap = $foundry.'ttf2pk.map';
my $pkcfg = 'ttf2pk.cfg';
my $cidmap = 'cid-'.$foundry.'ttf.map';
my $dvipdfmxcfg = 'dvipdfmx.cfg';
my $pdfmap = $foundry.'ttf-pdftex.map';
my $pdfcfg = 'pdftex.cfg';

# 사용자 설정을 체크
error("FOUNDRY must have 1 or 2 byte length.")
    if(!$foundry or length($foundry) >2);

error("`m' field of FONTmj is not defined in `$args{c}'.")
    unless $FONTS{mj}{'m'}; # \mjdefault는 반드시 있어야 함

foreach my $hlatex (keys %FONTS) {
# FONTxx의 xx는 영문 2글자만 허용
    error("`$hlatex' of `FONT$hlatex' must have 2 or 3 byte length.")
        unless(length($hlatex) == 2 or length($hlatex) == 3);

# \textrm 같은 라텍 고유 명령을 재정의하는 것을 방지
    error("`FONT$hlatex' is not a valid name.") if $hlatex =~
        /^(rm|sf|tt|md|bf|up|it|sc|sl|mu|pm|bb|yen|bar|kra|mho|ohm|won|div)$/;

# f 필드가 지정되지 않았다면 FONTxx의 xx를 f 필드값으로 사용
    $FONTS{$hlatex}{f} = $hlatex unless $FONTS{$hlatex}{f};
}

my $omegaprocess = 0;
##############################################
# unless -i option, make TFM/OFMs and settings
##############################################
unless($args{i}) {
    my $texheader = "\\documentclass[a4paper]{article}\n".
        "\\usepackage{hfont,${foundry}ttf}\n".
        "\\begin{document}\n";
    my $texfooter = "\\end{document}\n";
    my($iswan,$isttc) = (0,0);
    my(@StyMapHangul,@StyOther,@CidMap,@PkMap,@PdfMap,@TestTeX,@TestOmega);

#############################
# TeX-specific process begins
#############################
    foreach my $hlatex (keys %FONTS) { # loop for each FONTxx families
        my $family = $FONTS{$hlatex}{f};
        my $font = $foundry.$family;
        my %TeXFd;

        foreach my $mbl (keys %SERIES) { # loop for TTFs
            my $ttf = $FONTS{$hlatex}{$mbl};
            next unless $ttf;
# ttc라면 $isttc는 양수
            ++$isttc if $ttf =~ /\.ttc\b/i;

            my($wan,$cidmapcnt,$ttf2pkcnt) =
                maketfmandmap($font,$ttf,@{$SERIES{$mbl}});
# 완성형이면 $iswan은 양수
            $iswan += $wan;

# 2004-10-23 nomos 
            $mbl eq 'b' and $ttf eq $FONTS{$hlatex}{m} and
                @{$cidmapcnt} = addfakeboldoption(@{$cidmapcnt});
# end of 2004-10-23 nomos 

            push @CidMap,@{$cidmapcnt};
            push @PkMap,@{$ttf2pkcnt};

            push @PdfMap,getpdfmapdata($font.$SERIES{$mbl}[0],$ttf) if $args{p};

            foreach my $wsh (@Wsh) { # w/s/h 각 fd 내용을 %TeXFd에 저장
                push @{$TeXFd{$wsh}},gettexfd($font,$wsh,@{$SERIES{$mbl}});
            }

            push @TestTeX,gettexfilecnt($font,$mbl,$ttf,$hlatex);
        } # loop for ttfs ends here

        foreach my $wsh (keys %TeXFd) { # %TeXFd를 각 w/s/h .fd파일에 쓰기
            open FD,">$texencoding$wsh$font.fd" or
                die "$texencoding$wsh$font.fd: $!\n";
            print FD "\\DeclareFontFamily{$texencoding}{$wsh$font}",
                  "{\\hyphenchar\\font\\m\@ne}\n", @{$TeXFd{$wsh}};
            close FD;
        }

        if(keys %TeXFd) { # %TeXFd에 저장된 내용이 있다면 sty에 가족설정 쓰기
            push @StyMapHangul,
                 "\\MapHangulFamily{$family}{$font,$font,$font}\n";
            if($hlatex =~ /^(mj|gt|tz)$/) {
                push @StyOther,"\\renewcommand\\${hlatex}default{$family}\n";
            }
            else {
                push @StyOther, "\\DeclareRobustCommand\\${hlatex}family",
                     "{\\hfontfamily{$family}}\n",
                     "\\DeclareTextFontCommand\\text$hlatex",
                     "{\\${hlatex}family}\n";
            }
        }
    } # loop for families ends here

    push @StyOther,
         "\\DeclareErrorHFont{$texencoding}{\\mjdefault}{m}{n}{10}\n",
         "\\DeclareHFontSubstitution{$texencoding}{\\mjdefault}{m}{n}\n",
         "\\ifx\\pdfoutput\\undefined\n",
         "\\providecommand\\makethinhangul{\\hfontseries{c}}\n",
         "\\providecommand\\umakethinhangul{\\hfontseries{m}}\n",
         "\\else\n",
         "\\let\\makethinhangul\\relax\n",
         "\\let\\umakethinhangul\\relax\n",
         "\\fi\n",
         "\\else\n";

    open TEX,">$textexname" or die "$textexname: $!\n";
    print TEX $texheader,@TestTeX,$texfooter;
    close TEX;

    if($args{p} and $isttc) { # ttc가 하나라도 있으면 -p 옵션을 지움
        print '*'x70,"\n",
              "TTC(truetype collection) is defined in `$args{c}'.\n",
              "In this case -p option is meaningless or a source of trouble.\n",
              "Option -p is now cancelled!\n",
              "You may delete unnecessary *.enc's already geneated by ttf2tfm.\n",
              '*'x70,"\n";
        $args{p} = 0;
    }

##########################
# process for omega begins
##########################
    if($args{o} and $iswan) { # 완성인코딩이 하나라도 있으면 -o 옵션을 지움
        print '*'x70,"\n",
              "One or more Wansung TTF is defined in `$args{c}'.\n",
              "In this case -o option is meaningless or a source of trouble.\n",
              "Option -o is now cancelled!\n",
              '*'x70,"\n";
        $args{o} = 0;
    }

    unless($iswan) {
        $omegaprocess = 1;

        foreach my $hlatex (keys %FONTS) { # 글꼴가족들에 대하여 루프
            my $family = $FONTS{$hlatex}{f};
            my $font = $foundry.$family;
            my(@OmegaFd,@UCSFd);

            foreach my $mbl (keys %SERIES) { # 각 ttf에 대하여 루프
                my $ttf = $FONTS{$hlatex}{$mbl};
                next unless $ttf;

                my($cidmapcnt,$ttf2pkcnt) =
                    makeofmandmap($font,$ttf,@{$SERIES{$mbl}});

# 2004-10-23 nomos 
                $mbl eq 'b' and $ttf eq $FONTS{$hlatex}{m} and
                    @{$cidmapcnt} = addfakeboldoption(@{$cidmapcnt});
# end of 2004-10-23 nomos 

                push @CidMap,@{$cidmapcnt};
                push @PkMap,@{$ttf2pkcnt};

# 2005-01-10 nomos, for dhucs.sty
                push @PdfMap,
                    getpdfmapdata($omegaprefix.$font.$SERIES{$mbl}[0],$ttf)
                    if $args{p};
                push @UCSFd,getucsfd($font,@{$SERIES{$mbl}});
# end of 2005-01-10 nomos, for dhucs.sty

                if($args{o}) {
                    push @OmegaFd,getomegafd($font,@{$SERIES{$mbl}});
                    push @TestOmega,getomegafilecnt($font,$mbl,$ttf,$hlatex);
                }
            } # loop for ttfs ends here

# 2005-01-10 nomos, for dhucs.sty
            if(@UCSFd) {
                open FD,">$ucsencoding$font.fd" or
                    die "$ucsencoding$font.fd: $!\n";
                print FD "\\DeclareFontFamily{$ucsencoding}{$font}",
                      "{\\hyphenchar\\font\\m\@ne}\n",@UCSFd;
                close FD;
            }
# end of 2005-01-10 nomos, for dhucs.sty

            if($args{o} and @OmegaFd) { # fd와 sty에 설정 쓰기
                open FD,">$omegaencoding$font.fd" or
                    die "$omegaencoding$font.fd: $!\n";
                print FD
                    "\\DeclareFontFamily{$omegaencoding}{$font}{}\n",@OmegaFd;
                close FD;

                if($hlatex =~ /^(mj|gt|tz)$/) {
                    push @StyOther,"\\renewcommand\\${hlatex}default{$font}\n";
                }
                else {
                    push @StyOther,"\\DeclareRobustCommand\\${hlatex}family",
                         "{\\fontfamily{$font}\\selectfont}\n",
                         "\\DeclareTextFontCommand\\text$hlatex",
                         "{\\${hlatex}family}\n";
                }
            }
        } # loop for families ends here

        if($args{o}) {
            push @StyOther,
                 "\\renewcommand\\hfontfamily[1]",
                 "{\\fontfamily{$foundry\#1}\\selectfont\\ignorespaces}\n",
                 "\\newcommand\\hfontseries[1]",
                 "{\\fontseries{\#1}\\selectfont\\ignorespaces}\n",
                 "\\renewcommand{\\familydefault}{\\mjdefault}\n",
                 "\\DeclareErrorFont{$omegaencoding}{\\mjdefault}{m}{n}{10}\n",
                 "\\DeclareFontSubstitution{$omegaencoding}",
                 "{\\mjdefault}{m}{n}\n",
                 "\\providecommand\\makethinhangul",
                 "{\\fontseries{c}\\selectfont}\n",
                 "\\providecommand\\umakethinhangul",
                 "{\\fontseries{m}\\selectfont}\n";

            open TEX,">$omegatexname" or die "$omegatexname: $!\n";
            print TEX $texheader,@TestOmega,$texfooter;
            close TEX;
        }
    }

######################
# common process again
######################
    if($args{p}) {
        open MAP,">$pdfmap" or die "$pdfmap: $!\n";
        print MAP @PdfMap;
        close MAP;
    }

    open MAP,">$cidmap" or die "$cidmap: $!\n";
    print MAP @CidMap;
    close MAP;

    unless($args{n}) { # -n 옵션은 개발자를 위한 것. 주의요망
        open MAP,">$pkmap" or die "$pkmap: $!\n";
        print MAP @PkMap;
        close MAP;
    }

# sty 파일 생성
    printstyfile(@StyMapHangul,@StyOther);

# .cfg 파일들을 현재디렉토리에 저장
    unless(addtocfg("f $cidmap",$dvipdfmxcfg,'.')) {
        open CFG,">$dvipdfmxcfg" or die "$dvipdfmxcfg: $!\n";
        print CFG "f $cidmap\n";
        close CFG;
    }
    unless(addtocfg("map +$pkmap",$pkcfg,'.')) {
        open CFG,">$pkcfg" or die "$pkcfg: $!\n";
        print CFG "map +$pkmap\n";
        close CFG;
    }
    if($args{p} and !addtocfg("map +$pdfmap",$pdfcfg,'.')) {
        open CFG,">$pdfcfg" or die "$pdfcfg: $!\n";
        print CFG "output_format 1\n",
              "page_width 210 true mm\n",
              "page_height 297 true mm\n",
              "horigin 1 true in\n",
              "vorigin 1 true in\n",
              "map psfonts.map\n",
              "map +$pdfmap\n";
        close CFG;
    }

# MapHangulFamily{}{} 부분을 수정하자
    unless($args{d}) {
        @StyMapHangul = curemaphangulfamily(@StyMapHangul);

        print "\nI've tried to cure MapHangulFamily{}{} in $styname =>\n",
              @StyMapHangul,
              "<= You may edit this part of $styname to your taste.\n";

        move($styname,"$styname.orig") or
            warn "Couldn't rename $styname to $styname.orig: $!\n";

        printstyfile(@StyMapHangul,@StyOther);
    }

# 종료 메시지
    $textexname =~ s/\.tex//; $omegatexname =~ s/\.tex//;
    print "\nAll done ...\n",
          "Try `latex $textexname', `dvipdfmx $textexname' for testing.\a\n";
    print "Try `pdflatex $textexname' for testing.\n" if $args{p};
    print "Try `lambda $omegatexname',`dvipdfmx $omegatexname' for testing.\n"
        if $args{o};
}
##############################
# do installation if -i option
##############################
else {
# 옵션에 따른 현재디렉토리의 상태 점검
    error("Cannot find $styname, $pkmap or $cidmap in current directory.\n".
            "Run $0 without -i option before installing.")
        unless(-e $styname and -e $pkmap and -e $cidmap);

    error("Cannot find $pdfmap in current directory.\n".
            "Run $0 without -i but with -p option before installing\n".
            "or Try installation with -i but without -p option.")
        if($args{p} and !-e $pdfmap);

    if($args{o}) {
        my $isomega = 0;
        open STY,$styname or die "$styname: $!\n";
        while(<STY>) {
            if(/\\DeclareErrorFont\{$omegaencoding\}/) { # omega 설정이 되어있음
                $isomega = 1; last;
            }
        }
        close STY;
        error("Cannot find settings for HLambda.\n".
                "Run $0 without -i but with -o option before installing\n".
                "or Try installation with -i but without -o option.")
            unless $isomega;
    }

    my $texmf = $args{i};

# texmf directory 생성. 실패하면 사용자에게 진행여부를 물음
# mkpath() returns a list of all directories created
    unless(mkpath($texmf)) {
        print
        "Some error occured while making $texmf.\n",
        "It seems that the directory already exists.\n",
        "Installing into an existing directory is very DANGEROUS!\n",
        "Any file of the same name will be overwritten without notice.\n",
        "Do you really want to install files into $texmf? [yes/no]: ";
        my $answer = <STDIN>;
        exit 1 unless $answer =~ /yes/;
    }

# define subdirectories
    my $ttfd = "$texmf/fonts/truetype/${foundry}ttf";
    my $latexd = "$texmf/tex/latex/hlatex/${foundry}ttf";
    my $tfmd = "$texmf/fonts/tfm/${foundry}ttf";
    my $ttf2pkd = "$texmf/ttf2pk/${foundry}ttf";
    my $dvipdfmd = "$texmf/dvipdfm/${foundry}ttf";
    my $pdftexd = "$texmf/pdftex/${foundry}ttf";
    my $pdftexencd = "$pdftexd/enc";
    my $ofmd = "$texmf/fonts/ofm/${foundry}ttf";
    my $ovfd = "$texmf/fonts/ovf/${foundry}ttf";
    my $ovpd = "$texmf/fonts/ovp/${foundry}ttf";
    my $lambdad = "$texmf/omega/lambda/hlatex/${foundry}ttf";
    my $pdfcfgd = "$texmf/pdftex/config";
    my $ttf2pkcfgd = "$texmf/ttf2pk/base";
    my $dvipdfmxcfgd = "$texmf/dvipdfm/config";
# 2005-01-10 nomos, for dhucs.sty
    my $dhucsd = "$texmf/tex/latex/dhucs/${foundry}ttf";
# end of 2005-01-10 nomos, for dhucs.sty

# begin moving files
    mymove($latexd,$styname);
    mymove($ttf2pkd,$pkmap);
    mymove($dvipdfmd,$cidmap);
    mymove($pdftexd,$pdfmap) if $args{p};

    foreach my $hlatex (keys %FONTS) {
        next unless $FONTS{$hlatex}{f};
        my $font = $foundry.$FONTS{$hlatex}{f};
        my $ttcount = 0;

        foreach my $mbl (keys %SERIES) {
            my $ttf = $FONTS{$hlatex}{$mbl};
            ($ttf) = parsettf($ttf);
            next unless $ttf;
            ++$ttcount;

# ttf는 현재디렉토리에 없을 수도 있다
            mymove("$ttfd/$font",$ttf) if -e $ttf;

            if ($args{p}) {
                mymove("$pdftexencd/$font",glob("$font$SERIES{$mbl}[0]*.enc"));
                mymove("$pdftexencd/$font",
                    glob("$omegaprefix$font$SERIES{$mbl}[0]*.enc"));
            }

            foreach my $ser (@{$SERIES{$mbl}}) {
                mymove("$tfmd/$font",glob("$font$ser*.tfm"));
                mymove("$tfmd/$font",glob("$omegaprefix$font$ser*.tfm"));

                if($args{o}) {
                    mymove("$ofmd/$font",glob("$omegaprefix$font$ser*.ofm"));
                    mymove("$ovfd/$font",glob("$omegaprefix$font$ser*.ovf"));
                    mymove("$ovpd/$font",glob("$omegaprefix$font$ser*.ovp"));
                }
            }
        } # loop for ttfs ends here

# 사용자가 ttf를 하나이상 제대로 지정하였다면 $ttcount는 양수
        if($ttcount) {
            foreach my $wsh (@Wsh) {
                mymove($latexd,"$texencoding$wsh$font.fd");
            }
            mymove($lambdad,"$omegaencoding$font.fd") if $args{o};
# 2005-01-10 nomos, for dhucs.sty
            mymove($dhucsd,"$ucsencoding$font.fd")
                if -e "$ucsencoding$font.fd";
# end of 2005-01-10 nomos, for dhucs.sty
        }
    } # loop for families ends here

# .cfg 파일들을 적절한 곳에 생성
    addtocfg("f $cidmap",$dvipdfmxcfg,$dvipdfmxcfgd) or print
    "Failed! Add `f $cidmap' at the end of $dvipdfmxcfg manually.\n";

    addtocfg("map +$pkmap",$pkcfg,$ttf2pkcfgd) or print
    "Failed! Add `map +$pkmap' at th end of $pkcfg manually.\n";

    if($args{p}) {
        addtocfg("map +$pdfmap",$pdfcfg,$pdfcfgd) or print
        "Failed! Add `map +$pdfmap' at the end of $pdfcfg manually.\n";
    }

# 종료 메시지
    print
    "\nThe followings have to be done manually:\n",
    "1. register `$texmf' as the first TEXMF root directory\n",
    "   in texmf.cnf or in MikTeX Options.\n",
    "2. run `mktexlsr'.\n\n",
    "Happy TeX'ing!\a\n";
}

###################
### Subroutines ###
###################

### 2004-10-23 nomos
### add -b fakebold option to cid-xxxxx.map
sub addfakeboldoption {
    my @cidmaplines = @_;
    for(@cidmaplines) {
        s/$/ $bshape/;
    }
    return @cidmaplines;
}

########################################################
# MapHangulFamily부분을 받아 에러 점검하고 교정하여 리턴
########################################################
sub curemaphangulfamily {
    my(@maphangul) = @_;
    my $mjfamily = $FONTS{mj}{f};
    my $gtfamily = $FONTS{gt}{f} || $mjfamily;
    my @serifcured = ($mjfamily,$mjfamily,$mjfamily);
    my @sanscured = ($gtfamily,$gtfamily,$gtfamily);
    my $texlog = $textexname; $texlog =~ s/\.tex/\.log/;
    my(@ERRFNT,%SFONTS);


# latex을 돌려 에러난 폰트를 @ERRFNT에 저장
    system("latex -interaction=batchmode -no-mktex=tfm $textexname");
    open LOG,$texlog or return @maphangul;
    while(<LOG>) {
        if(/^! Font H\/(\w)(\w+?)\/.* \(TFM\) /) {
            push @{$ERRFNT[0]}, $2   if $1 eq 'w';
            push @{$ERRFNT[1]}, $2   if $1 eq 's';
            push @{$ERRFNT[2]}, $2   if $1 eq 'h';
        }
    }
    close LOG;

# @ERRFNT에서 중복항목을 없앰
    foreach my $i (0..2) {
        my %seen = ();
        foreach (@{$ERRFNT[$i]}) {
            $seen{$_}++;
        }
        @{$ERRFNT[$i]} = keys %seen;
    }

# @maphangul에서 %SFONTS 추출
    foreach (@maphangul) {
        if(/\\MapHangulFamily\{(\w+)\}\{(\w+),(\w+),(\w+)\}/) {
            push @{$SFONTS{$1}},$2,$3,$4;
        }
    }

# FONTmj에 에러가 있으면 uhc 이용
    foreach my $i (0..2) {
        foreach my $errfnt (@{$ERRFNT[$i]}) {
            $serifcured[$i] = 'type1uhc' if $errfnt eq "$foundry$mjfamily";
        }
    }

# FONTgt에 에러가 있으면 FONTmj 이용
    if($gtfamily ne $mjfamily) {
        foreach my $i (0..2) {
            foreach my $errfnt (@{$ERRFNT[$i]}) {
                $sanscured[$i] = $serifcured[$i]
                    if $errfnt eq "$foundry$gtfamily";
            }
        }
    }

# %SFONTS와 @ERRFNT를 비교하여
# sans계열에 에러 있으면 FONTgt이용, serif계열이면 FONTmj이용
    foreach my $fam (keys %SFONTS) {
        foreach my $i (0..2) {
            foreach my $errfnt (@{$ERRFNT[$i]}) {
                if($SFONTS{$fam}[$i] eq $errfnt) {
                    if($fam =~ /^($sansfonts)$/) {
                        $SFONTS{$fam}[$i] = $sanscured[$i] eq 'type1uhc' ?
                            'wgt' : $SFONTS{$sanscured[$i]}[$i];
                    } else {
                        $SFONTS{$fam}[$i] = $serifcured[$i] eq 'type1uhc' ?
                            'wmj' : $SFONTS{$serifcured[$i]}[$i];
                    }
                }
            }
        }

### 2004-03-22-nomos: for curing omega fd files
        next unless $args{o};
        next if $SFONTS{$fam}[0] eq 'wmj' or $SFONTS{$fam}[0] eq 'wgt';
        next if $SFONTS{$fam}[0] eq $SFONTS{$fam}[1] and
            $SFONTS{$fam}[0] eq $SFONTS{$fam}[2];
        if($fam =~ /^($sansfonts)$/) {
            cureomegafd($SFONTS{$fam}[0],"$foundry$gtfamily");
        }
        else  {
            cureomegafd($SFONTS{$fam}[0],"$foundry$mjfamily");
        }
### end of 2004-03-22-nomos

    }

# 새로운 @maphangul 리턴
    @maphangul = ();
    foreach my $fam (keys %SFONTS) {
        push @maphangul,"\\MapHangulFamily{$fam}{";
        foreach my $i (0..2) {
            push @maphangul,$SFONTS{$fam}[$i];
            push @maphangul,',' if $i < 2;
        }
        push @maphangul,"}\n";
    }
    return @maphangul;
}

### 2004-03-22-nomos: for curing omega fd files
sub cureomegafd {
    my($ovpmain,$ovpsupp) = @_;
    return if $ovpmain eq $ovpsupp;

    foreach my $key (keys %SERIES) {
        foreach my $ser (@{$SERIES{$key}}) {
            foreach my $sla ('',$slant) {
                my $ovp1 = "$omegaprefix$ovpmain$ser$sla";
                my $ovp2 = "$omegaprefix$ovpsupp$ser$sla";

                next unless -e "$ovp1.ovp";
                next unless -e "$ovp2.ovp";

                my($lastmapcnt, $newchar, $mapfound, @isovp1chars,
                        @ovp1head, @ovp1chars, @mapaddr, @ovp2arr,
                        @ovp2maps, @ovp2chars, $beginchar);

                open OVP,"$ovp1.ovp" or die "$ovp1.ovp: $!\n";
                while(<OVP>) {
                    if(/^\(MAPFONT D (\d+)/) {
                        $lastmapcnt = $1;
                    }
                    elsif(/^\(CHARACTER H (\w+)/) {
                        $isovp1chars[hex $1] = 1;
                        $beginchar = 1;
                    }

                    if($beginchar) {
                        push @ovp1chars,$_;
                    }
                    else {
                        push @ovp1head,$_ unless /^\(VTITLE /;
                    }
                }
                close OVP;

                open OVP,"$ovp2.ovp" or die "$ovp2.ovp: $!\n";
                @ovp2arr = <OVP>;
                close OVP;

                for(@ovp2arr) {
                    if(/^\(CHARACTER H (\w+)/) {
                        $mapfound = 0;
                        $newchar = $isovp1chars[hex $1] ? 0 : 1;
                        $newchar and push @ovp2chars,$_;
                    }
                    elsif($newchar and /^\s+\(SELECTFONT D (\d+)\)/) {
                        $mapaddr[$1] = 1;
                        $mapfound = 1;
                        $ovp2chars[$#ovp2chars] .= $_;
                    }
                    elsif($newchar and !$mapfound and /^\s+\)$/) {
                        $mapaddr[0] = 1;
                        $ovp2chars[$#ovp2chars] .= $_;
                    }
                    elsif($newchar) {
                        $ovp2chars[$#ovp2chars] .= $_;
                    }
                }

                for(@ovp2arr) {
                    last if(/^\(CHARACTER H /);
                    if(/^\(MAPFONT D (\d+)/) {
                        $mapfound = 0;
                        if($mapaddr[$1]) {
                            $mapfound = 1;
                            $lastmapcnt++;
                            push @ovp2maps,"(MAPFONT D $lastmapcnt\n";
                            $mapaddr[$1] = $lastmapcnt;
                        }
                    }
                    elsif($mapfound) {
                        push @ovp2maps,$_;
                    }
                }

                for(@ovp2chars) {
                    if(/\(SELECTFONT D/) {
                        s/SELECTFONT D (\d+)/SELECTFONT D $mapaddr[$1]/;
                    }
                    else {
                        s{\(Map.*\(SETCHAR}
                        {\(Map\n\(SELECTFONT D $mapaddr[0]\)\n\(SETCHAR}s;
                    }
                }

                open OVP,">$ovp1.ovp" or die "$ovp1.ovp: $!\n";
                print OVP "(VTITLE Created by `$0')\n",
                      @ovp1head, @ovp2maps,
                      @ovp1chars, @ovp2chars;
                close OVP;

                print "ovp2ovf $ovp1.ovp $ovp1.ovf $ovp1.ofm\n";
                system("ovp2ovf $ovp1.ovp $ovp1.ovf $ovp1.ofm");
            }
        }
    }
} # end of 2004-03-22-nomos

##################
# 스타일 파일 쓰기
##################
sub printstyfile {
    my(@stydata) = @_;

    open STY,">$styname" or die "$styname: $!\n";
    print STY "%%% $styname\n",
          "%%%\n",
          "%%% Automatically created by $0\n",
          "%%% at ",scalar localtime,"\n",
          "%%%\n",
          "\\ProvidesPackage{${foundry}ttf}\n",
          "\\ifx\\clearocplists\\undefined\n",
          @stydata,
          "\\fi\n",
          "\\def\\hfontfamilynameprefix{$foundry}\n",
          "\\endinput\n";
    close STY;
}

####################################################################
# kpsewhich의 도움을 받아 $cfg를 찾고 맨끝줄에 $line 설정을 추가하여
# $destdir에 저장함. 성공하면 1을 반환, 실패하면 0을 반환
####################################################################
sub addtocfg {
    my($line,$cfg,$destdir) = @_;
    print "making $destdir/$cfg with `$line'... ";

# 현재디렉토리의 $cfg는 kpsewhich의 작업에 방해가 됨
    if(-e $cfg) {
        move($cfg,"$cfg.exa") or return 0;
    }

# kpsewhich가 성공하길 기원하자
    my $progname = $cfg;
    $progname =~ s/dvipdfmx/dvipdfm/;
    $progname =~ s/\.cfg//;
    my $kpathcfg =
        `kpsewhich -format="other text files" -progname=$progname $cfg`;
    chomp $kpathcfg;
    return 0 unless $kpathcfg;

# 발견된 $cfg를 읽어들임
# 단, 추가하려는 줄과 같은 줄이 있으면 무시
    my @Lines = ();
    open(CFG,$kpathcfg) or return 0;
    while(<CFG>) {
        next if /^\Q$line/;
        next if /^% ${foundry}ttf -- added by/;
        push @Lines,$_;
    }
    close CFG;

# .cfg를 $destdir에 쓰고 맨끝에 $line 추가
    mkpath $destdir;
    open(CFG,">$destdir/$cfg") or return 0;
    print CFG @Lines,"\n\% ${foundry}ttf -- added by $0\n$line\n";
    close CFG;

# 여기까지 도달하면 성공
    print "Success. But check out the file.\n";
    return 1;
}

########################################
# 목적지 디렉토리와 파일이름 배열을 받아
# 디렉토리를 만들고 파일들을 이동
########################################
sub mymove {
    my($dest,@Files) = @_;

    mkpath $dest;
    foreach my $file (@Files) {
        print "$dest/$file\n";
        move($file,$dest) or die "$file: $!\n";
    }
}

########################################
# latex용 tfm 생성.
# 반환값은 cidmap과 ttf2pkmap의 레퍼런스
########################################
sub maketfmandmap {
    my($font,$ttf,@Series) = @_;
    my($findex,$cmap,@cidmapdata,@ttf2pkdata);
    my $iswan = 0;
    ($ttf,$findex,$cmap) = parsettf($ttf);

    foreach my $ser (@Series) { # eg. for 'm' and 'c'
        foreach my $sla ('',$slant) { # for null and 'o'
            my $font = $font.$ser.$sla;
            my($opt,$sfd,$tfmcmd);

            my $shape = '';
            $shape .= " $cshape" if $ser =~ /c/;
            $shape .= " $oshape" if $sla eq 'o';

# 한번 완성형임이 확인되면 차후로는 완성형 옵션을 이용토록 함
            unless($iswan) {
                $opt = "$texopt$shape -f $findex";
                $sfd = $texsfd;
            }
            else {
                $opt = "$wansungopt$shape -f $findex";
                $sfd = $wansungsfd;
            }
# pdftex은 slanted, extended 변형 처리못함
            $opt .= ' -w' if($args{p} and $shape eq '');

            $tfmcmd = "ttf2tfm $ttf $opt $font\@$sfd\@";

            print "$tfmcmd\n";
            unless($args{n}) {
                my $ttf2tfmresult = `$tfmcmd`;

                if($? >> 8){ # ttf2tfm이 실패했다면 완성 인코딩 옵션으로 재시도
                    $opt = "$wansungopt$shape -f $findex";
                    $opt .= ' -w' if($args{p} and $shape eq '');
                    $tfmcmd = "ttf2tfm $ttf $opt $font\@$wansungsfd\@";
                    print "... Trying Wansung TTF option ...\n$tfmcmd\n";
                    $ttf2tfmresult = `$tfmcmd`;
                    $? >> 8 and die; # 이번에도 실패하면 프로그램 종료
                    ++$iswan; # 성공했다면 완성형 TTF
                }

                push @ttf2pkdata,$ttf2tfmresult;
            }
            push @cidmapdata,"$font\@$wansungsfd\@\t$cmap\t:$findex:$ttf$shape\n";
        }
    }

    return($iswan,\@cidmapdata,\@ttf2pkdata);
}

#######################################
# pdftex의 map파일에 들어갈 내용을 리턴
#######################################
sub getpdfmapdata {
    my($font,$ttf) = @_;
    my(@Data,@PLANES);
    ($ttf) = parsettf($ttf);

    if($omegaprocess) {
        for my $i(0 .. 255) {
            $PLANES[$i] = sprintf "%02x",$i;
        }
    }
    else {
        @PLANES = ("04".."07",0 .. 29);
    }

    foreach my $plane (@PLANES) {
        push @Data,"$font$plane\t<$font$plane.enc\t<$ttf\n"
            if -e "$font$plane.enc";
    }

    return @Data;
}

#########################################
# lambda용 tfm/ofm을 만들고
# cidmap과 ttf2pkmap 내용의 레퍼런스 리턴
#########################################
sub makeofmandmap {
    my($font,$ttf,@Series) = @_;
    my($findex,$cmap,@cidmapdata,@ttf2pkdata);
    $font = $omegaprefix.$font;
    ($ttf,$findex,$cmap) = parsettf($ttf);

    foreach my $ser (@Series) {
        foreach my $sla ('',$slant) {
            my $font = $font.$ser.$sla;

            my $shape = '';
            $shape .= " $cshape" if $ser =~ /c/;
            $shape .= " $oshape" if $sla eq 'o';

# 2005-01-10 nomos, for pdftex under dhucs.sty
            my $opt = "$omegaopt$shape -f $findex";
            $opt .= " -o $font" if $args{o};
# pdftex은 slanted, extended 변형 처리못함
            $opt .= ' -w' if($args{p} and $shape eq '');
# end of 2005-01-10 nomos, for pdftex under dhucs.sty

            my $tfmcmd = "ttf2tfm $ttf $opt $font\@$omegasfd\@";

            print "$tfmcmd\n";
            unless($args{n}) {
                my $ttf2tfmresult = `$tfmcmd`;
                $? >> 8 and die;
                push @ttf2pkdata,$ttf2tfmresult;
            }
            if($args{o}) {
                my $ofmcmd = "ovp2ovf $font.ovp $font.ovf $font.ofm";
                print "$ofmcmd\n";
                unless($args{n}) {
                    system("$ofmcmd") == 0 or die;
                }
            }

#      my $ucs = $cmap eq 'Identity-H' ? '/UCS' : '';
#      push @cidmapdata,"$font\@$omegasfd\@\t$cmap\t:$findex:$ttf$ucs$shape\n";
            push @cidmapdata,"$font\@$omegasfd\@\t$cmap\t:$findex:$ttf$shape\n";
        }
    }

    return(\@cidmapdata,\@ttf2pkdata);
}

########################################
# hlatex의 .fd 파일에 들어갈 내용을 반환
########################################
sub gettexfd {
    my($font,$wsh,@Series) = @_;
    my @texfd;

    foreach my $ser (@Series) {
        push @texfd,"\n",
             "\\DeclareFontShape{$texencoding}{$wsh$font}{$ser}{n}",
             "{<->$font$ser}{}\n",
             "\\DeclareFontShape{$texencoding}{$wsh$font}{$ser}{sl}",
             "{<->$font$ser$slant}{}\n",
             "\\DeclareHFontShape{$texencoding}{$wsh$font}{$ser}{it}",
             "{<->hssub*$wsh$font/$ser/sl}{}\n";
    }

    return @texfd;
}

#######################################
# omega용 .fd 파일에 들어갈 내용을 반환
#######################################
sub getomegafd {
    my($font,@Series) = @_;
    my $ff = $omegaprefix.$font;
    my @omegafd;

    foreach my $ser (@Series) {
        push @omegafd,"\n",
             "\\DeclareFontShape{$omegaencoding}{$font}{$ser}{n}",
             "{<->$ff$ser}{}\n",
             "\\DeclareFontShape{$omegaencoding}{$font}{$ser}{sl}",
             "{<->$ff$ser$slant}{}\n",
             "\\DeclareFontShape{$omegaencoding}{$font}{$ser}{it}",
             "{<->ssub*$font/$ser/sl}{}\n";
    }

    return @omegafd;
}

# 2005-01-10 nomos, for dhucs.sty
sub getucsfd {
    my($font,@Series) = @_;
    my $ff = $omegaprefix.$font;
    my @ucsfd;

    foreach my $ser (@Series) {
        push @ucsfd,"\n",
             "\\DeclareFontShape{$ucsencoding}{$font}{$ser}{n}",
             "{<-> $ucsencoding * $ff$ser}{}\n",
             "\\DeclareFontShape{$ucsencoding}{$font}{$ser}{sl}",
             "{<-> $ucsencoding * $ff$ser$slant}{}\n",
             "\\DeclareFontShape{$ucsencoding}{$font}{$ser}{it}",
             "{<-> $ucsencoding * $ff$ser$slant}{}\n";
        if($ser eq 'b') {
            my $bxser = 'bx';
            push @ucsfd,"\n",
                 "\\DeclareFontShape{$ucsencoding}{$font}{$bxser}{n}",
                 "{<-> ${ucsencoding}ssub * $font/$ser/n}{}\n",
                 "\\DeclareFontShape{$ucsencoding}{$font}{$bxser}{sl}",
                 "{<-> ${ucsencoding}ssub * $font/$ser/sl}{}\n",
                 "\\DeclareFontShape{$ucsencoding}{$font}{$bxser}{it}",
                 "{<-> ${ucsencoding}ssub * $font/$ser/it}{}\n";
        }
    }

    return @ucsfd;
}
# end of 2005-01-10 nomos, for dhucs.sty


######################################
# latex 테스트 파일에 들어갈 내용 반환
######################################
sub gettexfilecnt {
    my($font,$mbl,$ttf,$hlatex) = @_;
    my(@texcnt,$findex);

    ($ttf,$findex) = parsettf($ttf);
    $ttf =~ s/_/\\_/g;
    $ttf .= "($findex)" if $findex;

    push @texcnt,"$font$SERIES{$mbl}[0]($ttf):\\text$hlatex\{";
    push @texcnt,"\\bfseries" if $mbl eq 'b';
    push @texcnt,"\\hfontseries{l}" if $mbl eq 'l';
    push @texcnt,"\\\\$texteststr}\n\n";

    return @texcnt;
}

#######################################
# lambda 테스트 파일에 들어갈 내용 반환
#######################################
sub getomegafilecnt {
    my($font,$mbl,$ttf,$hlatex) = @_;
    my(@texcnt,$findex,$cmap);

    ($ttf,$findex,$cmap) = parsettf($ttf);
    $ttf =~ s/_/\\_/g;
    $ttf .= "($findex)" if $findex;
    $ttf .= "[$cmap]" if $cmap ne $omegacmap;

    push @texcnt,"$font$SERIES{$mbl}[0]($ttf):\\text$hlatex\{";
    push @texcnt,"\\bfseries" if $mbl eq 'b';
    push @texcnt,"\\fontseries{l}\\selectfont" if $mbl eq 'l';
    push @texcnt,"\\\\$omegateststr}\n\n";

    return @texcnt;
}

#############################################
# 사용자가 지정한 truetype 이름과 설정을 파싱
#############################################
sub parsettf {
    my($ttf) = @_;
    my $findex = 0;
    my $cmap = $omegaprocess ? $omegacmap : $texcmap;

    if($ttf =~ /\((\d)\)/) {
        $findex = $1;
        $ttf =~ s/\($1\)//;
    }

    if($ttf =~ /\[([\w\-_\.]+?)\]/) {
#    $cmap = $1 if $omegaprocess;
        $ttf =~ s/\[$1\]//;
    }

    return($ttf,$findex,$cmap);
}

################################
# 에러메시지 출력. 프로그램 종료
################################
sub error {
    my $message = shift;

    print "\n$message\nProgram Aborted!!!\n";
    exit 1;
}

# EOF
