diff --git a/font/devps/CSH b/font/devps/CSH
new file mode 100644
index 00000000..708f5ab3
--- /dev/null
+++ b/font/devps/CSH
@@ -0,0 +1,24 @@
+#
+#  Chinese Simplified, Hei style (Heiti)
+#  Adobe-GB1
+#
+
+name CSH
+internalname STHeiti-Regular-UniGB-UTF16-H
+spacewidth 250
+
+charset-range
+u2190..u21FF	1000	0	--- Arrows
+u2460..u24FF	1000	0	--- Enclosed Alphanumerics
+u25A0..u25FF	1000	0	--- Geometric Shapes
+u2600..u26FF	1000	0	--- Miscellaneous Symbols
+u3000..u303F	1000	0	--- CJK Symbols and Punctuation
+u3200..u32FF	1000	0	--- Enclosed CJK Letters and Months
+u3300..u33FF	1000	0	--- CJK Compatibility
+u3400..u4DBF	1000	0	--- CJK Unified Ideographs Extension A
+u4E00..u9FFF	1000	0	--- CJK Unified Ideographs
+uF900..uFAFF	1000	0	--- CJK Compatibility Ideographs
+uFF00..uFF60	1000	0	--- Fullwidth Forms
+uFFE0..uFFE5	1000	0	--- Fullwidth Forms
+u20000..u323AF	1000	0	--- CJK Unified Ideographs Extension B-H
+
diff --git a/font/devps/CSS b/font/devps/CSS
new file mode 100644
index 00000000..48d12439
--- /dev/null
+++ b/font/devps/CSS
@@ -0,0 +1,24 @@
+#
+#  Chinese Simplified, Song style (Songti)
+#  Adobe-GB1
+#
+
+name CSS
+internalname STSong-Light-UniGB-UTF16-H
+spacewidth 250
+
+charset-range
+u2190..u21FF	1000	0	--- Arrows
+u2460..u24FF	1000	0	--- Enclosed Alphanumerics
+u25A0..u25FF	1000	0	--- Geometric Shapes
+u2600..u26FF	1000	0	--- Miscellaneous Symbols
+u3000..u303F	1000	0	--- CJK Symbols and Punctuation
+u3200..u32FF	1000	0	--- Enclosed CJK Letters and Months
+u3300..u33FF	1000	0	--- CJK Compatibility
+u3400..u4DBF	1000	0	--- CJK Unified Ideographs Extension A
+u4E00..u9FFF	1000	0	--- CJK Unified Ideographs
+uF900..uFAFF	1000	0	--- CJK Compatibility Ideographs
+uFF00..uFF60	1000	0	--- Fullwidth Forms
+uFFE0..uFFE5	1000	0	--- Fullwidth Forms
+u20000..u323AF	1000	0	--- CJK Unified Ideographs Extension B-H
+
diff --git a/font/devps/CTH b/font/devps/CTH
new file mode 100644
index 00000000..50f70c33
--- /dev/null
+++ b/font/devps/CTH
@@ -0,0 +1,24 @@
+#
+#  Chinese Traditional, Hei style (Heiti)
+#  Adobe-CNS1
+#
+
+name CTH
+internalname MHei-Medium-UniCNS-UTF16-H
+spacewidth 250
+
+charset-range
+u2190..u21FF	1000	0	--- Arrows
+u2460..u24FF	1000	0	--- Enclosed Alphanumerics
+u25A0..u25FF	1000	0	--- Geometric Shapes
+u2600..u26FF	1000	0	--- Miscellaneous Symbols
+u3000..u303F	1000	0	--- CJK Symbols and Punctuation
+u3200..u32FF	1000	0	--- Enclosed CJK Letters and Months
+u3300..u33FF	1000	0	--- CJK Compatibility
+u3400..u4DBF	1000	0	--- CJK Unified Ideographs Extension A
+u4E00..u9FFF	1000	0	--- CJK Unified Ideographs
+uF900..uFAFF	1000	0	--- CJK Compatibility Ideographs
+uFF00..uFF60	1000	0	--- Fullwidth Forms
+uFFE0..uFFE5	1000	0	--- Fullwidth Forms
+u20000..u323AF	1000	0	--- CJK Unified Ideographs Extension B-H
+
diff --git a/font/devps/CTS b/font/devps/CTS
new file mode 100644
index 00000000..71fd20ae
--- /dev/null
+++ b/font/devps/CTS
@@ -0,0 +1,24 @@
+#
+#  Chinese Traditional, Song style (Songti)
+#  Adobe-CNS1
+#
+
+name CTS
+internalname MSung-Light-UniCNS-UTF16-H
+spacewidth 250
+
+charset-range
+u2190..u21FF	1000	0	--- Arrows
+u2460..u24FF	1000	0	--- Enclosed Alphanumerics
+u25A0..u25FF	1000	0	--- Geometric Shapes
+u2600..u26FF	1000	0	--- Miscellaneous Symbols
+u3000..u303F	1000	0	--- CJK Symbols and Punctuation
+u3200..u32FF	1000	0	--- Enclosed CJK Letters and Months
+u3300..u33FF	1000	0	--- CJK Compatibility
+u3400..u4DBF	1000	0	--- CJK Unified Ideographs Extension A
+u4E00..u9FFF	1000	0	--- CJK Unified Ideographs
+uF900..uFAFF	1000	0	--- CJK Compatibility Ideographs
+uFF00..uFF60	1000	0	--- Fullwidth Forms
+uFFE0..uFFE5	1000	0	--- Fullwidth Forms
+u20000..u323AF	1000	0	--- CJK Unified Ideographs Extension B-H
+
diff --git a/font/devps/JPG b/font/devps/JPG
new file mode 100644
index 00000000..4931dd98
--- /dev/null
+++ b/font/devps/JPG
@@ -0,0 +1,89 @@
+#
+#  Japanese, Gothic style
+#  Adobe-Japan1
+#
+
+name JPG
+internalname GothicBBB-Medium-UniJIS-UTF16-H
+spacewidth 250
+
+charset-range
+u2190..u21FF	1000	0	--- Arrows
+u2460..u24FF	1000	0	--- Enclosed Alphanumerics
+u25A0..u25FF	1000	0	--- Geometric Shapes
+u2600..u26FF	1000	0	--- Miscellaneous Symbols
+u3000..u303F	1000	0	--- CJK Symbols and Punctuation
+u3040..u309F	1000	0	--- Hiragana
+u30A0..u30FF	1000	0	--- Katakana
+u31F0..u31FF	1000	0	--- Katakana Phonetic Extensions
+u3200..u32FF	1000	0	--- Enclosed CJK Letters and Months
+u3300..u33FF	1000	0	--- CJK Compatibility
+u3400..u4DBF	1000	0	--- CJK Unified Ideographs Extension A
+u4E00..u9FFF	1000	0	--- CJK Unified Ideographs
+uF900..uFAFF	1000	0	--- CJK Compatibility Ideographs
+uFF00..uFF60	1000	0	--- Fullwidth Forms
+uFF61..uFF9F	500	0	--- Halfwidth Katakana
+uFFE0..uFFE5	1000	0	--- Fullwidth Forms
+u1AFF0..u1B16F	1000	0	--- Kana Extended-B .. Small Kana Extension
+u20000..u323AF	1000	0	--- CJK Unified Ideographs Extension B-H
+
+charset
+u304B_3099	1000	0	12364	---  u304C  HIRAGANA LETTER GA
+u304D_3099	1000	0	12366	---  u304E  HIRAGANA LETTER GI
+u304F_3099	1000	0	12368	---  u3050  HIRAGANA LETTER GU
+u3051_3099	1000	0	12370	---  u3052  HIRAGANA LETTER GE
+u3053_3099	1000	0	12372	---  u3054  HIRAGANA LETTER GO
+u3055_3099	1000	0	12374	---  u3056  HIRAGANA LETTER ZA
+u3057_3099	1000	0	12376	---  u3058  HIRAGANA LETTER ZI
+u3059_3099	1000	0	12378	---  u305A  HIRAGANA LETTER ZU
+u305B_3099	1000	0	12380	---  u305C  HIRAGANA LETTER ZE
+u305D_3099	1000	0	12382	---  u305E  HIRAGANA LETTER ZO
+u305F_3099	1000	0	12384	---  u3060  HIRAGANA LETTER DA
+u3061_3099	1000	0	12386	---  u3062  HIRAGANA LETTER DI
+u3064_3099	1000	0	12389	---  u3065  HIRAGANA LETTER DU
+u3066_3099	1000	0	12391	---  u3067  HIRAGANA LETTER DE
+u3068_3099	1000	0	12393	---  u3069  HIRAGANA LETTER DO
+u306F_3099	1000	0	12400	---  u3070  HIRAGANA LETTER BA
+u306F_309A	1000	0	12401	---  u3071  HIRAGANA LETTER PA
+u3072_3099	1000	0	12403	---  u3073  HIRAGANA LETTER BI
+u3072_309A	1000	0	12404	---  u3074  HIRAGANA LETTER PI
+u3075_3099	1000	0	12406	---  u3076  HIRAGANA LETTER BU
+u3075_309A	1000	0	12407	---  u3077  HIRAGANA LETTER PU
+u3078_3099	1000	0	12409	---  u3079  HIRAGANA LETTER BE
+u3078_309A	1000	0	12410	---  u307A  HIRAGANA LETTER PE
+u307B_3099	1000	0	12412	---  u307C  HIRAGANA LETTER BO
+u307B_309A	1000	0	12413	---  u307D  HIRAGANA LETTER PO
+u3046_3099	1000	0	12436	---  u3094  HIRAGANA LETTER VU
+u309D_3099	1000	0	12446	---  u309E  HIRAGANA VOICED ITERATION MARK
+u30AB_3099	1000	0	12460	---  u30AC  KATAKANA LETTER GA
+u30AD_3099	1000	0	12462	---  u30AE  KATAKANA LETTER GI
+u30AF_3099	1000	0	12464	---  u30B0  KATAKANA LETTER GU
+u30B1_3099	1000	0	12466	---  u30B2  KATAKANA LETTER GE
+u30B3_3099	1000	0	12468	---  u30B4  KATAKANA LETTER GO
+u30B5_3099	1000	0	12470	---  u30B6  KATAKANA LETTER ZA
+u30B7_3099	1000	0	12472	---  u30B8  KATAKANA LETTER ZI
+u30B9_3099	1000	0	12474	---  u30BA  KATAKANA LETTER ZU
+u30BB_3099	1000	0	12476	---  u30BC  KATAKANA LETTER ZE
+u30BD_3099	1000	0	12478	---  u30BE  KATAKANA LETTER ZO
+u30BF_3099	1000	0	12480	---  u30C0  KATAKANA LETTER DA
+u30C1_3099	1000	0	12482	---  u30C2  KATAKANA LETTER DI
+u30C4_3099	1000	0	12485	---  u30C5  KATAKANA LETTER DU
+u30C6_3099	1000	0	12487	---  u30C7  KATAKANA LETTER DE
+u30C8_3099	1000	0	12489	---  u30C9  KATAKANA LETTER DO
+u30CF_3099	1000	0	12496	---  u30D0  KATAKANA LETTER BA
+u30CF_309A	1000	0	12497	---  u30D1  KATAKANA LETTER PA
+u30D2_3099	1000	0	12499	---  u30D3  KATAKANA LETTER BI
+u30D2_309A	1000	0	12500	---  u30D4  KATAKANA LETTER PI
+u30D5_3099	1000	0	12502	---  u30D6  KATAKANA LETTER BU
+u30D5_309A	1000	0	12503	---  u30D7  KATAKANA LETTER PU
+u30D8_3099	1000	0	12505	---  u30D9  KATAKANA LETTER BE
+u30D8_309A	1000	0	12506	---  u30DA  KATAKANA LETTER PE
+u30DB_3099	1000	0	12508	---  u30DC  KATAKANA LETTER BO
+u30DB_309A	1000	0	12509	---  u30DD  KATAKANA LETTER PO
+u30A6_3099	1000	0	12532	---  u30F4  KATAKANA LETTER VU
+u30EF_3099	1000	0	12535	---  u30F7  KATAKANA LETTER VA
+u30F0_3099	1000	0	12536	---  u30F8  KATAKANA LETTER VI
+u30F1_3099	1000	0	12537	---  u30F9  KATAKANA LETTER VE
+u30F2_3099	1000	0	12538	---  u30FA  KATAKANA LETTER VO
+u30FD_3099	1000	0	12542	---  u30FE  KATAKANA VOICED ITERATION MARK
+
diff --git a/font/devps/JPM b/font/devps/JPM
new file mode 100644
index 00000000..52b3ead4
--- /dev/null
+++ b/font/devps/JPM
@@ -0,0 +1,89 @@
+#
+#  Japanese, Mincho style
+#  Adobe-Japan1
+#
+
+name JPM
+internalname Ryumin-Light-UniJIS-UTF16-H
+spacewidth 250
+
+charset-range
+u2190..u21FF	1000	0	--- Arrows
+u2460..u24FF	1000	0	--- Enclosed Alphanumerics
+u25A0..u25FF	1000	0	--- Geometric Shapes
+u2600..u26FF	1000	0	--- Miscellaneous Symbols
+u3000..u303F	1000	0	--- CJK Symbols and Punctuation
+u3040..u309F	1000	0	--- Hiragana
+u30A0..u30FF	1000	0	--- Katakana
+u31F0..u31FF	1000	0	--- Katakana Phonetic Extensions
+u3200..u32FF	1000	0	--- Enclosed CJK Letters and Months
+u3300..u33FF	1000	0	--- CJK Compatibility
+u3400..u4DBF	1000	0	--- CJK Unified Ideographs Extension A
+u4E00..u9FFF	1000	0	--- CJK Unified Ideographs
+uF900..uFAFF	1000	0	--- CJK Compatibility Ideographs
+uFF00..uFF60	1000	0	--- Fullwidth Forms
+uFF61..uFF9F	500	0	--- Halfwidth Katakana
+uFFE0..uFFE5	1000	0	--- Fullwidth Forms
+u1AFF0..u1B16F	1000	0	--- Kana Extended-B .. Small Kana Extension
+u20000..u323AF	1000	0	--- CJK Unified Ideographs Extension B-H
+
+charset
+u304B_3099	1000	0	12364	---  u304C  HIRAGANA LETTER GA
+u304D_3099	1000	0	12366	---  u304E  HIRAGANA LETTER GI
+u304F_3099	1000	0	12368	---  u3050  HIRAGANA LETTER GU
+u3051_3099	1000	0	12370	---  u3052  HIRAGANA LETTER GE
+u3053_3099	1000	0	12372	---  u3054  HIRAGANA LETTER GO
+u3055_3099	1000	0	12374	---  u3056  HIRAGANA LETTER ZA
+u3057_3099	1000	0	12376	---  u3058  HIRAGANA LETTER ZI
+u3059_3099	1000	0	12378	---  u305A  HIRAGANA LETTER ZU
+u305B_3099	1000	0	12380	---  u305C  HIRAGANA LETTER ZE
+u305D_3099	1000	0	12382	---  u305E  HIRAGANA LETTER ZO
+u305F_3099	1000	0	12384	---  u3060  HIRAGANA LETTER DA
+u3061_3099	1000	0	12386	---  u3062  HIRAGANA LETTER DI
+u3064_3099	1000	0	12389	---  u3065  HIRAGANA LETTER DU
+u3066_3099	1000	0	12391	---  u3067  HIRAGANA LETTER DE
+u3068_3099	1000	0	12393	---  u3069  HIRAGANA LETTER DO
+u306F_3099	1000	0	12400	---  u3070  HIRAGANA LETTER BA
+u306F_309A	1000	0	12401	---  u3071  HIRAGANA LETTER PA
+u3072_3099	1000	0	12403	---  u3073  HIRAGANA LETTER BI
+u3072_309A	1000	0	12404	---  u3074  HIRAGANA LETTER PI
+u3075_3099	1000	0	12406	---  u3076  HIRAGANA LETTER BU
+u3075_309A	1000	0	12407	---  u3077  HIRAGANA LETTER PU
+u3078_3099	1000	0	12409	---  u3079  HIRAGANA LETTER BE
+u3078_309A	1000	0	12410	---  u307A  HIRAGANA LETTER PE
+u307B_3099	1000	0	12412	---  u307C  HIRAGANA LETTER BO
+u307B_309A	1000	0	12413	---  u307D  HIRAGANA LETTER PO
+u3046_3099	1000	0	12436	---  u3094  HIRAGANA LETTER VU
+u309D_3099	1000	0	12446	---  u309E  HIRAGANA VOICED ITERATION MARK
+u30AB_3099	1000	0	12460	---  u30AC  KATAKANA LETTER GA
+u30AD_3099	1000	0	12462	---  u30AE  KATAKANA LETTER GI
+u30AF_3099	1000	0	12464	---  u30B0  KATAKANA LETTER GU
+u30B1_3099	1000	0	12466	---  u30B2  KATAKANA LETTER GE
+u30B3_3099	1000	0	12468	---  u30B4  KATAKANA LETTER GO
+u30B5_3099	1000	0	12470	---  u30B6  KATAKANA LETTER ZA
+u30B7_3099	1000	0	12472	---  u30B8  KATAKANA LETTER ZI
+u30B9_3099	1000	0	12474	---  u30BA  KATAKANA LETTER ZU
+u30BB_3099	1000	0	12476	---  u30BC  KATAKANA LETTER ZE
+u30BD_3099	1000	0	12478	---  u30BE  KATAKANA LETTER ZO
+u30BF_3099	1000	0	12480	---  u30C0  KATAKANA LETTER DA
+u30C1_3099	1000	0	12482	---  u30C2  KATAKANA LETTER DI
+u30C4_3099	1000	0	12485	---  u30C5  KATAKANA LETTER DU
+u30C6_3099	1000	0	12487	---  u30C7  KATAKANA LETTER DE
+u30C8_3099	1000	0	12489	---  u30C9  KATAKANA LETTER DO
+u30CF_3099	1000	0	12496	---  u30D0  KATAKANA LETTER BA
+u30CF_309A	1000	0	12497	---  u30D1  KATAKANA LETTER PA
+u30D2_3099	1000	0	12499	---  u30D3  KATAKANA LETTER BI
+u30D2_309A	1000	0	12500	---  u30D4  KATAKANA LETTER PI
+u30D5_3099	1000	0	12502	---  u30D6  KATAKANA LETTER BU
+u30D5_309A	1000	0	12503	---  u30D7  KATAKANA LETTER PU
+u30D8_3099	1000	0	12505	---  u30D9  KATAKANA LETTER BE
+u30D8_309A	1000	0	12506	---  u30DA  KATAKANA LETTER PE
+u30DB_3099	1000	0	12508	---  u30DC  KATAKANA LETTER BO
+u30DB_309A	1000	0	12509	---  u30DD  KATAKANA LETTER PO
+u30A6_3099	1000	0	12532	---  u30F4  KATAKANA LETTER VU
+u30EF_3099	1000	0	12535	---  u30F7  KATAKANA LETTER VA
+u30F0_3099	1000	0	12536	---  u30F8  KATAKANA LETTER VI
+u30F1_3099	1000	0	12537	---  u30F9  KATAKANA LETTER VE
+u30F2_3099	1000	0	12538	---  u30FA  KATAKANA LETTER VO
+u30FD_3099	1000	0	12542	---  u30FE  KATAKANA VOICED ITERATION MARK
+
diff --git a/font/devps/KOG b/font/devps/KOG
new file mode 100644
index 00000000..16150c4c
--- /dev/null
+++ b/font/devps/KOG
@@ -0,0 +1,28 @@
+#
+#  Korean, GoThic style
+#  Adobe-Korea1
+#
+
+name KOG
+internalname HYGoThic-Medium-UniKS-UTF16-H
+spacewidth 250
+
+charset-range
+u1100..u11FF	1000	0	--- Hangul Jamo
+u3130..u318F	1000	0	--- Hangul Compatibility Jamo
+uA960..uA97F	1000	0	--- Hangul Jamo Extended-A
+uAC00..uD7AF	1000	0	--- Hangul Syllables
+uD7B0..uD7FF	1000	0	--- Hangul Jamo Extended-B
+u2190..u21FF	1000	0	--- Arrows
+u2460..u24FF	1000	0	--- Enclosed Alphanumerics
+u25A0..u25FF	1000	0	--- Geometric Shapes
+u2600..u26FF	1000	0	--- Miscellaneous Symbols
+u3000..u303F	1000	0	--- CJK Symbols and Punctuation
+u3200..u32FF	1000	0	--- Enclosed CJK Letters and Months
+u3300..u33FF	1000	0	--- CJK Compatibility
+u3400..u4DBF	1000	0	--- CJK Unified Ideographs Extension A
+u4E00..u9FFF	1000	0	--- CJK Unified Ideographs
+uF900..uFAFF	1000	0	--- CJK Compatibility Ideographs
+uFF00..uFF60	1000	0	--- Fullwidth Forms
+uFFE0..uFFE6	1000	0	--- Fullwidth Forms
+
diff --git a/font/devps/KOM b/font/devps/KOM
new file mode 100644
index 00000000..7609b1b3
--- /dev/null
+++ b/font/devps/KOM
@@ -0,0 +1,28 @@
+#
+#  Korean, MyeongJo style
+#  Adobe-Korea1
+#
+
+name KOM
+internalname HYSMyeongJo-Medium-UniKS-UTF16-H
+spacewidth 250
+
+charset-range
+u1100..u11FF	1000	0	--- Hangul Jamo
+u3130..u318F	1000	0	--- Hangul Compatibility Jamo
+uA960..uA97F	1000	0	--- Hangul Jamo Extended-A
+uAC00..uD7AF	1000	0	--- Hangul Syllables
+uD7B0..uD7FF	1000	0	--- Hangul Jamo Extended-B
+u2190..u21FF	1000	0	--- Arrows
+u2460..u24FF	1000	0	--- Enclosed Alphanumerics
+u25A0..u25FF	1000	0	--- Geometric Shapes
+u2600..u26FF	1000	0	--- Miscellaneous Symbols
+u3000..u303F	1000	0	--- CJK Symbols and Punctuation
+u3200..u32FF	1000	0	--- Enclosed CJK Letters and Months
+u3300..u33FF	1000	0	--- CJK Compatibility
+u3400..u4DBF	1000	0	--- CJK Unified Ideographs Extension A
+u4E00..u9FFF	1000	0	--- CJK Unified Ideographs
+uF900..uFAFF	1000	0	--- CJK Compatibility Ideographs
+uFF00..uFF60	1000	0	--- Fullwidth Forms
+uFFE0..uFFE6	1000	0	--- Fullwidth Forms
+
diff --git a/src/devices/grops/ps.cpp b/src/devices/grops/ps.cpp
index b891169e..c176388a 100644
--- a/src/devices/grops/ps.cpp
+++ b/src/devices/grops/ps.cpp
@@ -200,13 +200,15 @@ ps_output &ps_output::put_delimiter(char c)
   return *this;
 }
 
-ps_output &ps_output::put_string(const char *s, int n)
+ps_output &ps_output::put_string(const wchar_t *s, int n, int is_utf16)
 {
   int len = 0;
   int i;
   for (i = 0; i < n; i++) {
-    char c = s[i];
-    if (is_ascii(c) && csprint(c)) {
+    wchar_t c = s[i];
+    if (is_utf16) {
+      len = (i+1)*4;
+    } else if (is_ascii(c) && csprint(c)) {
       if (c == '(' || c == ')' || c == '\\')
 	len += 2;
       else
@@ -215,7 +217,7 @@ ps_output &ps_output::put_string(const char *s, int n)
     else
       len += 4;
   }
-  if (len > n*2) {
+  if (len > n*2 || is_utf16) {
     if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
       putc('\n', fp);
       col = 0;
@@ -231,8 +233,13 @@ ps_output &ps_output::put_string(const char *s, int n)
 	putc('\n', fp);
 	col = 0;
       }
-      fprintf(fp, "%02x", s[i] & 0377);
-      col += 2;
+      if (is_utf16) {
+        fprintf(fp, "%04X", s[i] & 0xFFFF);
+        col += 4;
+      } else {
+        fprintf(fp, "%02x", s[i] & 0377);
+        col += 2;
+      }
     }
     putc('>', fp);
     col++;
@@ -527,7 +534,7 @@ class ps_printer : public printer {
   int paper_length;
   int equalise_spaces;
   enum { SBUF_SIZE = 256 };
-  char sbuf[SBUF_SIZE];
+  wchar_t sbuf[SBUF_SIZE];
   int sbuf_len;
   int sbuf_start_hpos;
   int sbuf_vpos;
@@ -561,7 +568,7 @@ class ps_printer : public printer {
   void set_style(const style &);
   void set_space_code(unsigned char);
   int set_encoding_index(ps_font *);
-  subencoding *set_subencoding(font *, glyph *, unsigned char *);
+  subencoding *set_subencoding(font *, glyph *, wchar_t *);
   char *get_subfont(subencoding *, const char *);
   void do_exec(char *, const environment *);
   void do_import(char *, const environment *);
@@ -658,10 +665,25 @@ int ps_printer::set_encoding_index(ps_font *f)
 }
 
 subencoding *ps_printer::set_subencoding(font *f, glyph *g,
-					 unsigned char *codep)
+					 wchar_t *code)
 {
   unsigned int idx = f->get_code(g);
-  *codep = idx % 256;
+  const char *psname = f->get_internal_name();
+
+  if (psname && strstr(psname,"-UTF16-")) {
+    /* Unicode, convert to UTF-16 */
+    if (idx<0x10000) {
+      code[0] = idx;
+      code[1] = 0;
+    } else {
+      code[0] = (idx - 0x10000) / 0x400 + 0xD800;
+      code[1] = (idx - 0x10000) % 0x400 + 0xDC00;
+    }
+    return NULL;
+  }
+
+  code[0] = idx % 256;
+  code[1] = 0;
   unsigned int num = idx >> 8;
   if (num == 0)
     return 0;
@@ -672,7 +694,7 @@ subencoding *ps_printer::set_subencoding(font *f, glyph *g,
   if (p == 0)
     p = subencodings = new subencoding(f, num, next_subencoding_index++,
 				       subencodings);
-  p->glyphs[*codep] = f->get_special_device_encoding(g);
+  p->glyphs[*code] = f->get_special_device_encoding(g);
   return p;
 }
 
@@ -692,8 +714,8 @@ void ps_printer::set_char(glyph *g, font *f, const environment *env, int w,
 {
   if (g == space_glyph || invis_count > 0)
     return;
-  unsigned char code;
-  subencoding *sub = set_subencoding(f, g, &code);
+  wchar_t code[2];
+  subencoding *sub = set_subencoding(f, g, code);
   style sty(f, sub, env->size, env->height, env->slant);
   if (sty.slant != 0) {
     if (sty.slant > 80 || sty.slant < -80) {
@@ -707,14 +729,16 @@ void ps_printer::set_char(glyph *g, font *f, const environment *env, int w,
 	&& sbuf_vpos == env->vpos
 	&& sbuf_color == *env->col) {
       if (sbuf_end_hpos == env->hpos) {
-	sbuf[sbuf_len++] = code;
+	sbuf[sbuf_len++] = code[0];
+	if (code[1]>0) sbuf[sbuf_len++] = code[1];
 	sbuf_end_hpos += w + sbuf_kern;
 	return;
       }
       if (sbuf_len == 1 && sbuf_kern == 0) {
 	sbuf_kern = env->hpos - sbuf_end_hpos;
 	sbuf_end_hpos = env->hpos + sbuf_kern + w;
-	sbuf[sbuf_len++] = code;
+	sbuf[sbuf_len++] = code[0];
+	if (code[1]>0) sbuf[sbuf_len++] = code[1];
 	return;
       }
       /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
@@ -727,7 +751,8 @@ void ps_printer::set_char(glyph *g, font *f, const environment *env, int w,
 	    sbuf_space_width = env->hpos - sbuf_end_hpos;
 	    sbuf_end_hpos = env->hpos + w + sbuf_kern;
 	    sbuf[sbuf_len++] = sbuf_space_code;
-	    sbuf[sbuf_len++] = code;
+	    sbuf[sbuf_len++] = code[0];
+	    if (code[1]>0) sbuf[sbuf_len++] = code[1];
 	    sbuf_space_count++;
 	    return;
 	  }
@@ -737,7 +762,8 @@ void ps_printer::set_char(glyph *g, font *f, const environment *env, int w,
 	  if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
 	    sbuf_end_hpos = env->hpos + w + sbuf_kern;
 	    sbuf[sbuf_len++] = sbuf_space_code;
-	    sbuf[sbuf_len++] = code;
+	    sbuf[sbuf_len++] = code[0];
+	    if (code[1]>0) sbuf[sbuf_len++] = code[1];
 	    sbuf_space_count++;
 	    if (diff == 1)
 	      sbuf_space_diff_count++;
@@ -751,7 +777,8 @@ void ps_printer::set_char(glyph *g, font *f, const environment *env, int w,
     flush_sbuf();
   }
   sbuf_len = 1;
-  sbuf[0] = code;
+  sbuf[0] = code[0];
+  if (code[1]>0) sbuf[sbuf_len++] = code[1];
   sbuf_end_hpos = env->hpos + w;
   sbuf_start_hpos = env->hpos;
   sbuf_vpos = env->vpos;
@@ -987,6 +1014,7 @@ void ps_printer::flush_sbuf()
     output_style = sbuf_style;
   }
   int extra_space = 0;
+  int is_utf16 = 0;
   if (output_hpos < 0 || output_vpos < 0)
     motion = ABSOLUTE;
   else {
@@ -1018,7 +1046,10 @@ void ps_printer::flush_sbuf()
     out.put_fix_number(extra_space);
   if (sbuf_kern != 0)
     out.put_fix_number(sbuf_kern);
-  out.put_string(sbuf, sbuf_len);
+  const char *psname = sbuf_style.f->get_internal_name();
+  if (psname && strstr(psname,"-UTF16-"))
+    is_utf16 = 1;
+  out.put_string(sbuf, sbuf_len, is_utf16);
   char command_array[] = {'A', 'B', 'C', 'D',
 			  'E', 'F', 'G', 'H',
 			  'I', 'J', 'K', 'L',
diff --git a/src/devices/grops/ps.h b/src/devices/grops/ps.h
index 5cef694f..d5f203bf 100644
--- a/src/devices/grops/ps.h
+++ b/src/devices/grops/ps.h
@@ -20,7 +20,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 class ps_output {
 public:
   ps_output(FILE *, int max_line_length);
-  ps_output &put_string(const char *, int);
+  ps_output &put_string(const wchar_t *, int, int);
   ps_output &put_number(int);
   ps_output &put_fix_number(int);
   ps_output &put_float(double);
diff --git a/src/include/font.h b/src/include/font.h
index 68a82faf..4f30e363 100644
--- a/src/include/font.h
+++ b/src/include/font.h
@@ -16,7 +16,7 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
+#define ENABLE_UCSRANGE
 // A function of this type can be registered to define the semantics of
 // arbitrary commands in a font DESC file.
 typedef void (*FONT_COMMAND_HANDLER)(const char *,	// command
@@ -287,6 +287,12 @@ private:
 			// font (if !is_unicode) or for just some characters
 			// (if is_unicode).  The indices of this array are
 			// font-specific, found as values in ch_index[].
+#ifdef ENABLE_UCSRANGE
+  // font metric for wchar
+  font_char_metric *wch;
+  // get font metric for wchar indexed by uc
+  font_char_metric *get_font_wchar_metric(int uc);
+#endif
   int ch_used;
   int ch_size;
   font_widths_cache *widths_cache;	// A cache of scaled character
diff --git a/src/libs/libgroff/font.cpp b/src/libs/libgroff/font.cpp
index 7655e0a3..baef2d43 100644
--- a/src/libs/libgroff/font.cpp
+++ b/src/libs/libgroff/font.cpp
@@ -42,6 +42,10 @@ struct font_char_metric {
   int italic_correction;
   int subscript_correction;
   char *special_device_coding;
+#ifdef ENABLE_UCSRANGE
+  struct font_char_metric *next;
+  int end_code;
+#endif
 };
 
 struct font_kern_list {
@@ -156,6 +160,20 @@ void text_file::fatal(const char *format,
     fatal_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
 }
 
+#ifdef ENABLE_UCSRANGE
+int glyph_to_ucs_codepoint(glyph *g)
+{
+  const char *nm = glyph_to_name(g);
+  if (nm != 0) {
+    if (check_unicode_name(nm) && strchr(nm, '_')==0) {
+      char *ignore;
+      return (int)strtol(nm + 1, &ignore, 16);
+    }
+  }
+  return -1;
+}
+#endif
+
 int glyph_to_unicode(glyph *g)
 {
   const char *nm = glyph_to_name(g);
@@ -204,6 +222,9 @@ font::font(const char *s) : ligatures(0), kern_hash_table(0),
   space_width(0), special(false), internalname(0), slant(0.0), zoom(0),
   ch_index(0), nindices(0), ch(0), ch_used(0), ch_size(0),
   widths_cache(0)
+#ifdef ENABLE_UCSRANGE
+  ,wch(0)
+#endif
 {
   name = new char[strlen(s) + 1];
   strcpy(name, s);
@@ -234,6 +255,15 @@ font::~font()
     widths_cache = widths_cache->next;
     delete tem;
   }
+#ifdef ENABLE_UCSRANGE
+  struct font_char_metric *wcp, *nwcp;
+  for (wcp = wch; wcp != 0; wcp = nwcp) {
+    nwcp = wcp->next;
+    if (wcp->special_device_coding)
+      delete [] wcp->special_device_coding;
+    delete wcp;
+  }
+#endif
 }
 
 static int scale_round(int n, int x, int y)
@@ -316,6 +346,14 @@ bool font::contains(glyph *g)
   // Explicitly enumerated glyph?
   if (idx < nindices && ch_index[idx] >= 0)
     return true;
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  if (uc>0) {
+    font_char_metric *wcp = get_font_wchar_metric(uc);
+    if (wcp != 0)
+      return true;
+  }
+#endif
   if (is_unicode) {
     // Unicode font
     // ASCII or Unicode character, or groff glyph name that maps to Unicode?
@@ -347,6 +385,20 @@ font_widths_cache::~font_widths_cache()
   delete[] width;
 }
 
+#ifdef ENABLE_UCSRANGE
+struct font_char_metric *
+font::get_font_wchar_metric(int uc)
+{
+  struct font_char_metric *wcp;
+  for (wcp = wch; wcp != 0; wcp = wcp->next) {
+    if (wcp->code <= uc && uc <= wcp->end_code) {
+      return wcp;
+    }
+  }
+  return 0;
+}
+#endif
+
 int font::get_width(glyph *g, int point_size)
 {
   int idx = glyph_to_index(g);
@@ -361,6 +413,14 @@ int font::get_width(glyph *g, int point_size)
     else
       real_size = int(point_size * double(zoom) / 1000.0 + .5);
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0 && !(idx < nindices && ch_index[idx] >= 0)) {
+    return scale(wcp->width, point_size);
+  }
+#endif
   if (idx < nindices && ch_index[idx] >= 0) {
     // Explicitly enumerated glyph
     int i = ch_index[idx];
@@ -412,6 +472,14 @@ int font::get_height(glyph *g, int point_size)
     // Explicitly enumerated glyph
     return scale(ch[ch_index[idx]].height, point_size);
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0) {
+    return scale(wcp->height, point_size);
+  }
+#endif
   if (is_unicode) {
     // Unicode font
     return 0;
@@ -428,6 +496,14 @@ int font::get_depth(glyph *g, int point_size)
     // Explicitly enumerated glyph
     return scale(ch[ch_index[idx]].depth, point_size);
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0) {
+    return scale(wcp->depth, point_size);
+  }
+#endif
   if (is_unicode) {
     // Unicode font
     return 0;
@@ -444,6 +520,14 @@ int font::get_italic_correction(glyph *g, int point_size)
     // Explicitly enumerated glyph
     return scale(ch[ch_index[idx]].italic_correction, point_size);
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0) {
+    return scale(wcp->italic_correction, point_size);
+  }
+#endif
   if (is_unicode) {
     // Unicode font
     return 0;
@@ -460,6 +544,14 @@ int font::get_left_italic_correction(glyph *g, int point_size)
     // Explicitly enumerated glyph
     return scale(ch[ch_index[idx]].pre_math_space, point_size);
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0) {
+    return scale(wcp->pre_math_space, point_size);
+  }
+#endif
   if (is_unicode) {
     // Unicode font
     return 0;
@@ -476,6 +568,14 @@ int font::get_subscript_correction(glyph *g, int point_size)
     // Explicitly enumerated glyph
     return scale(ch[ch_index[idx]].subscript_correction, point_size);
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0) {
+    return scale(wcp->subscript_correction, point_size);
+  }
+#endif
   if (is_unicode) {
     // Unicode font
     return 0;
@@ -550,6 +650,14 @@ int font::get_character_type(glyph *g)
     // Explicitly enumerated glyph
     return ch[ch_index[idx]].type;
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0) {
+    return wcp->type;
+  }
+#endif
   if (is_unicode) {
     // Unicode font
     return 0;
@@ -566,6 +674,14 @@ int font::get_code(glyph *g)
     // Explicitly enumerated glyph
     return ch[ch_index[idx]].code;
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0) {
+    return uc;
+  }
+#endif
   if (is_unicode) {
     // Unicode font
     // ASCII or Unicode character, or groff glyph name that maps to Unicode?
@@ -600,6 +716,13 @@ const char *font::get_special_device_encoding(glyph *g)
     // Explicitly enumerated glyph
     return ch[ch_index[idx]].special_device_coding;
   }
+#ifdef ENABLE_UCSRANGE
+  int uc  = glyph_to_ucs_codepoint(g);
+  font_char_metric *wcp;
+  if (uc>0) wcp = get_font_wchar_metric(uc);
+  if (wcp != 0)
+    return wcp->special_device_coding;
+#endif
   if (is_unicode) {
     // Unicode font
     return 0;
@@ -867,7 +990,12 @@ bool font::load(bool load_header_only)
     else if (strcmp(p, "special") == 0) {
       special = true;
     }
+#ifdef ENABLE_UCSRANGE
+    else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0 &&
+             strcmp(p, "charset-range") != 0) {
+#else
     else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
+#endif
       char *directive = p;
       p = strtok(0, "\n");
       handle_unknown_font_command(directive, trim_arg(p), t.path,
@@ -913,6 +1041,82 @@ bool font::load(bool load_header_only)
 	add_kern(g1, g2, n);
       }
     }
+#ifdef ENABLE_UCSRANGE
+    else if (strcmp(directive, "charset-range") == 0) {
+      if (load_header_only)
+	return true;
+      saw_charset_directive = true;
+      int had_range = 0;
+      for (;;) {
+	if (!t.next_line()) {
+	  directive = 0;
+	  break;
+	}
+	char *nm = strtok(t.buf, WS);
+	assert(nm != 0);
+	p = strtok(0, WS);
+	if (0 == p) {
+	  directive = nm;
+	  break;
+	}
+	int start_code = 0;
+	int end_code = 0;
+	int nrange = sscanf(nm, "u%X..u%X", &start_code, &end_code);
+	if (nrange == 2) {
+	  had_range = 1;
+	  font_char_metric *wcp = new font_char_metric;
+	  wcp->code = start_code;
+	  wcp->end_code = end_code;
+	  wcp->height = 0;
+	  wcp->depth = 0;
+	  wcp->pre_math_space = 0;
+	  wcp->italic_correction = 0;
+	  wcp->subscript_correction = 0;
+	  int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
+			      &wcp->width, &wcp->height, &wcp->depth,
+			      &wcp->italic_correction,
+			      &wcp->pre_math_space,
+			      &wcp->subscript_correction);
+	  if (nparms < 1) {
+	    t.error("missing or invalid width for character range '%1'", nm);
+	    return false;
+	  }
+	  p = strtok(0, WS);
+	  if (0 == p) {
+	    t.error("missing character type for '%1'", nm);
+	    return false;
+	  }
+	  int type;
+	  if (sscanf(p, "%d", &type) != 1) {
+	    t.error("invalid character type for '%1'", nm);
+	    return false;
+	  }
+	  if (type < 0 || type > 255) {
+	    t.error("character type '%1' out of range for '%2'", type,
+	            nm);
+	    return false;
+	  }
+	  wcp->type = type;
+
+	  p = strtok(0, WS);
+	  if ((0 == p) || (strcmp(p, "--") == 0)) {
+	    wcp->special_device_coding = 0;
+	  }
+	  else {
+	    wcp->special_device_coding = new char[strlen(p) + 1];
+	    strcpy(wcp->special_device_coding, p);
+	  }
+	  wcp->next = wch;
+	  wch = wcp;
+	  p = 0;
+	}
+      }
+      if (!had_range) {
+	t.error("no glyphs defined in font description");
+	return false;
+      }
+    }
+#endif
     else if (strcmp(directive, "charset") == 0) {
       if (load_header_only)
 	return true;
diff --git a/src/roff/groff/groff.am b/src/roff/groff/groff.am
index 3d472683..9f9f62f6 100644
--- a/src/roff/groff/groff.am
+++ b/src/roff/groff/groff.am
@@ -59,6 +59,7 @@ groff_TESTS = \
   src/roff/groff/tests/regression_savannah_58337.sh \
   src/roff/groff/tests/regression_savannah_59202.sh \
   src/roff/groff/tests/smoke-test_html_device.sh \
+  src/roff/groff/tests/smoke-test_ps_device.sh \
   src/roff/groff/tests/soquiet_works.sh \
   src/roff/groff/tests/string_case_xform_errors.sh \
   src/roff/groff/tests/string_case_xform_requests.sh \
diff --git a/src/roff/groff/tests/smoke-test_ps_device.sh b/src/roff/groff/tests/smoke-test_ps_device.sh
new file mode 100755
index 00000000..f5ae07c9
--- /dev/null
+++ b/src/roff/groff/tests/smoke-test_ps_device.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (C) 2022 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+groff="${abs_top_builddir:-.}/test-groff"
+
+
+# test for PostScript with Japanese, preprocessor
+echo "testing -Kutf8 -Tps -Z" >&2
+printf ".ft JPM\nあ安" | "$groff" -Kutf8 -Tps -Z | tr '\n' ';' | grep -q 'Cu3042;h10000;Cu5B89;h10000;'
+
+
+# test for PostScript with Japanese, UTF16 encoding
+echo "testing -Kutf8 -Tps" >&2
+printf ".ft JPM\nあ安" | "$groff" -Kutf8 -Tps | grep -q '/Ryumin-Light-UniJIS-UTF16-H SF<30425B89>'
