Pillow can be written vertically in Japanese at least in Ver6 or later (+ raqm 0.7 or later). Please note that the content of this article is out of date.
I thought about that.
The main libraries used are as follows.
Describe the resolution of the generated image and the layout of columns in xml. There is no particular format on which it is based, and it is an original one that has been decided appropriately. Use the xml library to parse it. The text is written in basic plain text, and ruby and emphasis marks are written in html tags in the text. Use HTMLParser to parse this. I feel that one of the libraries can handle both, but I decided to use both for studying. Use Pillow, an image processing library, to draw characters. Of course, it can't be helped, but Pillow doesn't support vertical writing at all, so I do a lot of trial and error (cutting out) to write vertically.
The xml that determines the layout and the body text look like the following.
yosuruni_layout.xml
<novel width="1920" height="960" margin_up="0.1" margin_bottom="0.1" margin_left="0.05" margin_right="0.05">
<columnchain name="Main" fontsize="36" direction="VERTICAL" linespace="2.0" color="#101000">
<column refp="UP_RIGHT" reflh="MARGIN_RIGHT" reflv="MARGIN_UP" offsetx="LIVEAREA_H:0" offsety="LIVEAREA_V:0" sizew="LIVEAREA_H:1.0" sizeh="LIVEAREA_V:1.0"/>
</columnchain>
<text columnchain="Main" src="yosuruni.xml" />
</novel>
yosuruni.xml
My child, who was four years old, has become quite readable. I'm thinking of reading a lot of books from now on, but I haven't read much so far, so I didn't know what kind of book to choose.
As a result of various thoughts, I realized that I should write a story myself and have it read. Even though I haven't read a book in the first place, I wonder if I can write a story, but there is no doubt if I write "buttocks" or "shit" appropriately. Because I'm 4 years old.
By the way, when I started writing with a notepad application, it was completely horizontal writing.<d>Not like</d>.. If it is not written vertically, as a "reading material"<r val="Fun">atmosphere</r>I can't feel it, and it's not fun to watch. So I searched for an editor that can write vertically, but I couldn't find anything that looked like this.
If this happens, there is no choice but to make it. An application like an editor is impossible, but I think it can be done with a tool that converts plain text into a vertical image ...
In the layout, if you increase the column elements in the columnchain element, the columns will increase and the text will be flowed in the order of column description.
The layout and body text are basically parsed by the following routines.
Layout analysis.py
import xml.etree.ElementTree as ET
#Parse xml to get element tree
tree = ET.parse(xml_path)
#Get the root element. You get a novel element
novel_element = tree.getroot()
#Here, refer to the attribute of the novel element to get the setting value.
#Get the columnchain element inside the novel element
for cc_element in novel_element.iter("columnchain"):
#Here, refer to the attribute of the columnchain element to get the setting value.
#Get the column element inside the columnchain element
for c_element in cc_element.iter("column"):
#Here, refer to the attribute of the columnc element to get the set value.
#Get the text element inside the novel element
for text_element in novel_element.iter("text"):
#Here, refer to the attribute of the text element to get the setting value.
Body analysis.py
from html.parser import HTMLParser
class TextParser(HTMLParser):
def __init__(self):
super().__init__()
def handle_starttag(self, tag, attrs):
if tag == "ruby" or tag=="r":
#Detecting the start of ruby tag
if tag == "dot" or tag=="d":
#Detects the start of emphasis marks
def handle_endtag(self, tag):
if tag == "ruby" or tag == "dot" or tag == "r" or tag == "d":
#Detect the end of tag
def handle_data(self, data):
#Get data in tags. The text itself or ruby(Reading kana sentence)Get
class Text():
def __init__(self, source):
parser = TextParser()
parser.feed(source)
For example, the following is a vertically long layout with 3 columns. (Since it is an example, the resolution is low. The characters are 12 points at 320x720)
yosuruni_layout.xml
<novel width="320" height="720" margin_up="0.1" margin_bottom="0.1" margin_left="0.05" margin_right="0.05">
<columnchain name="Main" fontsize="12" direction="VERTICAL" linespace="2.0" color="#101000">
<column refp="UP_RIGHT" reflh="MARGIN_RIGHT" reflv="MARGIN_UP" offsetx="LIVEAREA_H:0" offsety="LIVEAREA_V:0" sizew="LIVEAREA_H:1.0" sizeh="LIVEAREA_V:0.3"/>
<column refp="UP_RIGHT" reflh="MARGIN_RIGHT" reflv="MARGIN_UP" offsetx="LIVEAREA_H:0" offsety="LIVEAREA_V:0.35" sizew="LIVEAREA_H:1.0" sizeh="LIVEAREA_V:0.3"/>
<column refp="UP_RIGHT" reflh="MARGIN_RIGHT" reflv="MARGIN_UP" offsetx="LIVEAREA_H:0" offsety="LIVEAREA_V:0.7" sizew="LIVEAREA_H:1.0" sizeh="LIVEAREA_V:0.3"/>
</columnchain>
<text columnchain="Main" src="yosuruni.xml" />
</novel>
$ python NovelFE.py yosuruni_layout.xml
This will output the following image.
If the resolution is low, the position of the characters may fluctuate slightly, which is annoying.
As mentioned above, Pillow allows horizontal writing, but not vertical writing. So I decided to write vertically. In other words, it draws characters one by one while shifting the position vertically.
I used Gensho Antic as the font. A font for comics that supports vertical writing.
However, when I try to draw it, it is natural,
Font glyphs for horizontal writing, such as parentheses, punctuation, and small "tsu", are used. In the Pillow library, there is no way to specify vertical writing, so the glyph for vertical writing is not used anyway.
As a quick and quick idea of what to do, I came up with the idea of tinkering with the font file itself and forcibly replacing the horizontal glyphs with vertical glyphs.
pip install fonttools
Install the Python font tools fonttools (ttx). If you specify a font file with the ttx command, it will be converted to xml.
% ttx GenEiAntiqueN-Medium.otf
Dumping "GenEiAntiqueN-Medium.otf" to "GenEiAntiqueN-Medium.ttx"...
Dumping 'GlyphOrder' table...
Dumping 'head' table...
Dumping 'hhea' table...
Dumping 'maxp' table...
Dumping 'OS/2' table...
Dumping 'name' table...
Dumping 'cmap' table...
Dumping 'post' table...
Dumping 'CFF ' table...
Dumping 'BASE' table...
Dumping 'GDEF' table...
Dumping 'GPOS' table...
Dumping 'GSUB' table...
Dumping 'VORG' table...
Dumping 'hmtx' table...
Dumping 'vhea' table...
Dumping 'vmtx' table...
The font used this time is in OpenType format, so check the outline of the specifications below.
Introduction to OpenType Specifications (Part 2) [Introduction to OpenType Specifications (Part 2)] (http://qiita.com/496_/items/4f8327fe741cf0c87736) [Introduction to OpenType Specifications (Part 1)] (http://qiita.com/496_/items/f6efb650dcf7e9d2dfe4)
From the above, the important xml generated by ttx are GSUB and cmap.
OpenType roughly contains glyph (font) data with IDs, The cmap table contains a correspondence table showing the glyph data IDs that correspond to the character (eg Unicode) codes. In addition, in the GSUB table, when the glyph used under a specific condition changes, the correspondence table of the change source glyph ID and the change destination glyph ID is shown.
Therefore, the correspondence table showing the glyph ID to be replaced in the case of vertical writing is extracted from the GSUB table, and based on that, the glyph ID of the correspondence table in the cmap table is replaced. Then you should be able to refer to the glyph for vertical writing unconditionally.
Let's write a script for conversion.
otfconv.py
import argparse
import xml.etree.ElementTree as ET
parser = argparse.ArgumentParser()
parser.add_argument("infile")
args = parser.parse_args()
tree = ET.parse(args.infile)
root = tree.getroot()
list_index = []
cid_replace_dic = {}
for gsub_elements in root.iter('GSUB'):
for featurerecords in gsub_elements.iter('FeatureRecord'):
for featuretags in featurerecords.iter('FeatureTag'):
if featuretags.attrib['value'] == "vert" or \
featuretags.attrib['value'] == "vrt2" or \
featuretags.attrib['value'] == "vtrt":
for lookuplistindexs in featurerecords.iter('LookupListIndex'):
if not lookuplistindexs.get('value') in list_index:
list_index.append(lookuplistindexs.get('value'))
for lookup in gsub_elements.iter('Lookup'):
if lookup.get('index') in list_index:
for substitution in lookup.iter('Substitution'):
cid_replace_dic[substitution.get('in')] = substitution.get('out')
for cmap in root.iter('cmap'):
for maps in cmap.iter('map'):
if maps.get('name') in cid_replace_dic.keys():
maps.set('name', cid_replace_dic[maps.get('name')])
tree.write("output.xml")
$ python otfconv.py GenEiAntiqueN-Medium.ttx
Hopefully an output.xml will be created, which you can ttx back into an OpenType file. By the way, when I did it, I got a conversion error unless I added the following line to the beginning. (That worked, so I haven't looked into too much detail.)
<?xml version="1.0" encoding="UTF-8"?>
Use ttx to switch back from xml to otf.
$ ttx -o TateFont.otf output.xml
Then
I was able to write like that.
I thought that at least ruby and kinsoku processing were necessary to make it into a novel. Also, as an extra point. Ruby is a little troublesome because if the character height of ruby exceeds the corresponding character height of the text, the character spacing of the text must be increased.
The other is multi-page support. If the text does not fit on one page, try to generate multiple images with the same layout.
Now you are ready to write a story.
Recommended Posts