为Knoppix CD配置simsun等Windows字体


idea1.gif

原始需求

因为Windows的宋体(以及黑体、楷体和tahoma/verdana字体)很漂亮,很多朋友都用上了这些字体,
网上有很多文章都讲配置方法,Knoppix基于Debian,也没有什么特别的,配置方法是一样的。
但现在的问题是:
1、不想在光盘上带上这几个字体文件,一方面是因为版权方面的原因,另一方面是因为它们很大(simsun.ttf就有10M)
2、Knoppix本来就是在光盘上跑,内存吃紧(尤其对配置低一点的机器),配置了这些字体之后X系统又会慢一些

需求分析

所以我设想的解决方案是:
用户可以在启动时选择是否需要采用这些字体,
如果指定使用它们的话,就将这些字体文件软连接到某个临时目录下

然后创建fonts.dir, fonts.scale和fonts.cache-1.
可选功能: 自动将KDE、GNOME、Mozilla、OpenOffice的缺省中文字体设置为simsun

概要设计

想了一番之后,我计划的实现是这样:
用户可以在启动时指定winfonts=/mnt/hda?/windir/fonts这样的选项
也可以用winfonts=scan选项(但此时字体需要在某分区的\windows\fonts或者\winnt\fonts目录下)
Knoppix的自动配置系统如果发现有这个选项,就搜索simsun字体文件,
如果找到就在/etc/winfonts下创建到这些字体文件的软连接
然后创建X系统需要的fonts.dir, fonts.scale和fontconfig需要的fonts.cache-1

详细设计

考虑到虽然新的ttmkfdir已经可以正确识别simsun.ttf(或者simsun.ttc)里的字符集
但它没法生成一个智能型的fonts.dir [ FIXME ]
我们可以先在/etc/winfonts下提供编辑好的fonts.dir
同时因为它们放在/etc下,Knoppix对/etc下的文件做特殊处理,大都是到光盘上相应文件的软连接
如果这些文件有问题的话,我们还可以进行修改:
比如fonts.dir, 先删除到/KNOPPIX/etc/winfonts/font.dir的连接,
然后拷贝/KNOPPIX/etc/winfonts/font.dir到/etc/winfonts/font.dir,就可以修改了

只所以将这个目录创建在/etc下面,是因为这样我们的配置可以被saveconfig保存下来
下次用myconfig=...启动选项可以恢复出来,包括脚本动态产生的配置和手工修改的部分
(请参考: /usr/sbin/saveconfig)

还有就是将这个字体目录配置到字体系统里去,
对于XFree86自己而言,就是编辑/etc/X11/XF86Config-4.in,添加FontPath "/etc/winfonts"
(注意同时要添加Load "xtt",禁用Load "freetype")
对于fontconfig和libxft,要修改/etc/fonts/fonts.conf和/etc/X11/XftConfig
前者就是添加<dir>/etc/winfonts</dir>
后者就是添加dir "/etc/winfonts"
当我们不想采用windows的字体时,因为那几个字体文件的连接并不存在,所以没有影响

剩下的就是这段脚本在什么地方切入的问题了。
用过一阵Knoppix之后,我知道它的自动配置功能都在/etc/init.d/knoppix-autoconfig这里
Knoppix的大致工作流是:
  1. 本地化(localization,设置LANGUAGE)
  2. 配置desktop类型
  3. 将前面的配置写入/etc/sysconfig/i18n, /etc/sysconfig/keyboard, /etc/sysconfig/desktop, /etc/sysconfig/knoppix
  4. 硬件方面的选项(apm, pcmcia, usb, firewall, hotplug)配置
  5. 运行hwsetup检查硬件,然后进行配置(mouse、Soundcard、AGP等)
  6. 调用/usr/sbin/mkxf86config根据/etc/X11/XF86Config-4.in模板生成XF86Config-4
  7. 配置automount
  8. 配置交换分区或者交换文件、DMA,调用rebuildfstab生成/etc/fstab
  9. DHCP Broadcast
  10. 检查/cdrom/KNOPPIX/background /usr/local/lib/knoppix上提供的背景图片
  11. 加载home文件系统
  12. 如果启动时指定了floppy/myconfig选项,就读取原来保存的配置数据覆盖当前的配置
  13. 检查光盘上的addon(knoppix.sh)

所以我们可以把这段脚本放在knoppix-autoconfig的12之前,也可以直接放在光盘上保存为knoppix.sh

编码实现

为了方便测试,我将这个脚本写成了一个单独的文件,winfonts-config.sh
"============"前面的部分是从knoppix-autoconfig里摘取出来的,后面的部分可以取下来插入到该文件里面去
这里并没有自动配置KDE、GNOME、Mozilla的缺省字体等,也没有处理仿宋、黑体等字体,大家自己去做吧
(提示:修改/etc/skel/下的文件,比如KDE的缺省字体配置在/etc/skel/.kde/share/config/kdeglobals)

(PS: 下面的彩色效果是用vim产生的,如果你用gvim的话,点一下Syntax -> Convert to HTML就可以了
如果不是GUI版本,打开文件后在命令行执行:so $VIMRUNTIME/syntax/2html.vim )


#!/bin/bash
# search windows fonts and use them, if winfonts= option specified at boot time


# some stuff stolen from knoppix-autoconfig

# ANSI COLORS
CRE="^M^[[K"
NORMAL="^[[0;39m"
# RED: Failure or error message
RED="^[[1;31m"
# GREEN: Success message
GREEN="^[[1;32m"
# YELLOW: Descriptions
YELLOW="^[[1;33m"
# BLUE: System messages
BLUE="^[[1;34m"
# MAGENTA: Found devices or drivers
MAGENTA="^[[1;35m"
# CYAN: Questions
CYAN="^[[1;36m"
# BOLD WHITE: Hint
WHITE="^[[1;37m"

# Reread boot command line; echo last parameter's argument or return false.
getbootparam(){
stringinstring " $1=" "$CMDLINE" || return 1
result="${CMDLINE##*$1=}"
result="${result%%[ ]*}"
echo "$result"
return 0
}

# Check boot commandline for specified option
checkbootparam(){
stringinstring " $1" "$CMDLINE"
return "$?"
}

# Read in boot parameters
# This should work, but doesn't with Kernel 2.4.19-rc1
# CMDLINE="$(</proc/cmdline)" This should work, but doesn't with Kernel 2.4.19-rc1
# This works.
CMDLINE="$(cat /proc/cmdline)"

findfile(){
FOUND=""
# search all partitions for a file in the root directory
for i in /mnt/[sh]d[a-z] /mnt/[sh]d[a-z][1-9] /mnt/[sh]d[a-z][1-9]?*; do
# See if it's already mounted
[ -f "$i/$1" ] && { echo "$i/$1"; return 0; }
if [ -d "$i" ] && mount -r "$i" 2>/dev/null; then
[ -f "$i/$1" ] && FOUND="$i/$1"
umount -l "$i" 2>/dev/null
[ -n "$FOUND" ] && { echo "$FOUND"; return 0; }
fi
done
return 2
}

# Try to mount this filesystem read-only, without or with encryption
trymount(){
# Check if already mounted
case "$(cat /proc/mounts)" in *\ $2\ *) return 0;; esac
# Apparently, mount-aes DOES autodetect AES loopback files.
[ -b "$1" ] && { mount -t auto -o ro "$1" "$2" 2>/dev/null; RC="$?"; }
# We need to mount crypto-loop files with initial rw support
[ -f "$1" ] && { mount -t auto -o loop,rw "$1" "$2" 2>/dev/null; RC="$?"; }
# Mount succeeded?
[ "$RC" = "0" ] && return 0
echo ""
echo "${CYAN}Filesystem not autodetected, trying to mount $1 with AES256 encryption${NORMAL}"
a="y"
while [ "$a" != "n" -a "$a" != "N" ]; do
# We need to mount crypto-loop files with initial rw support
mount -t auto -o loop,rw,encryption=AES256 "$1" "$2" && return 0
echo -n "${RED}Mount failed, retry? [Y/n] ${NORMAL}"
read a
done
return 1
}

# The following part could be cut and pasted into /etc/init.d/knoppix-autoconfig
#==============================================================================
# Check for existence of windows\fonts directory if 'winfonts' option specified at boot time
WINFONTDIR="$(getbootparam winfonts)"
MYFONTSDEVICE=""
MYFONTSMOUNTPOINT=""
MYFONTSDIR=""

if [ -n "$WINFONTDIR" ]; then
case "$WINFONTDIR" in
/dev/*)
MYFONTSDEVICE="${WINFONTDIR##/dev/}"
MYFONTSDEVICE="/dev/${MYFONTSDEVICE%%/*}"
MYFONTSMOUNTPOINT="/mnt/${MYFONTSDEVICE##/dev/}"
MYFONTSDIR="/mnt/${WINFONTDIR##/dev/}"
;;
/mnt/*)
MYFONTSDEVICE="${WINFONTDIR##/mnt/}"
MYFONTSDEVICE="/dev/${MYFONTSDEVICE%%/*}"
MYFONTSMOUNTPOINT="/mnt/${MYFONTSDEVICE##/dev/}"
MYFONTSDIR="$WINFONTDIR"
;;
[Ss][Cc][Aa][Nn])
MYFONTSDIR="$(findfile winnt/fonts/tahoma.ttf)"
[ -n "$MYFONTSDIR" ] || MYFONTSDIR="$(findfile windows/fonts/tahoma.ttf)"
[ -n "$MYFONTSDIR" ] && MYFONTSDIR="$(dirname $MYFONTSDIR)"
MYFONTSDEVICE="${MYFONTSDIR##/mnt/}"
MYFONTSDEVICE="/dev/${MYFONTSDEVICE%%/*}"
MYFONTSMOUNTPOINT="/mnt/${MYFONTSDEVICE##/dev/}"
;;
*)
echo "${CRE}${RED}Invalid ${CYAN}winfonts=${RED} option '$WINFONTDIR' specified (must start with /dev/ or /mnt/ or 'scan').${NORMAL}"
echo "${CRE}${RED}Option ignored.${NORMAL}"
;;
esac
fi

if [ -n "$MYFONTSDIR" ]; then
if trymount "$MYFONTSDEVICE" "$MYFONTSMOUNTPOINT"; then
[ -f "$MYFONTSMOUNTPOINT/WINNT/Fonts" ] && MYFONTSDIR="$MYFONTSMOUNTPOINT/WINNT/Fonts"
while read device mountpoint fs relax; do
case "$mountpoint" in
*$MYFONTSMOUNTPOINT*)
case "$fs" in
*[Nn][Tt][Ff][Ss]*) options="ro" ;;
*[Ff][Aa][Tt]*|*[Mm][Ss][Dd][Oo][Ss]) options="rw" ;;
esac
if mount -o remount,${options} "$MYFONTSMOUNTPOINT"; then
echo " ${GREEN}Mounted ${YELLOW}$MYFONTSDEVICE${GREEN} to use ${YELLOW} Windows fonts. ${NORMAL}"
fi
break
;;
esac
done <<EOT
$(cat /proc/mounts)
EOT
fi
fi

if [ -d ${MYFONTSDIR} ]; then
echo -n " ${GREEN}Tring to use ${YELLOW}simsun/tahoma/verdana ${GREEN} font from $MYFONTSDIR ... "

# this dir has already been put into /etc/X11/XF86Config-4.in,
# /etc/fonts/fonts.conf and /etc/X11/XftConfig
localfontsdir=/etc/winfonts

# link to real font files
rm -rf $localfontsdir
[ -d $localfontsdir ] || mkdir -p $localfontsdir
[ -f ${MYFONTSDIR}/simsun.ttc ] && ln -s ${MYFONTSDIR}/simsun.ttc ${localfontsdir}/simsun.ttf \
|| ln -s ${MYFONTSDIR}/simsun.ttf ${localfontsdir}/simsun.ttf || echo "simsun font not found.${NORMAL}"
for font in tahoma tahomabd verdana verdanab verdanai verdanaz; do
[ -f ${MYFONTSDIR}/${font}.ttf ] && ln -s ${MYFONTSDIR}/${font}.ttf ${localfontsdir}/${font}.ttf
done

FOODIR=/usr/local/share/zhfonts/winfonts
# regenerate fonts.dir and fonts.scale if we haven't prepared one
if [ -f ${FOODIR}/fonts.dir ]; then
cp ${FOODIR}/fonts.dir $localfontsdir
cp ${FOODIR}/fonts.scale $localfontsdir
else
(cd ${localfontsdir} && ttmkfdir > fonts.dir && cp fonts.dir fonts.scale )
fi
# regenerate fonts.cache-1 for libxft, if we there isn't one
[ -f ${FOODIR}/fonts.cache-1 ] \
&& cp ${FOODIR}/fonts.cache $localfontsdir \
|| fc-cache $localfontsdir

echo "Done.${NORMAL}"