Version 3, last updated by wycc at August 17, 2010 22:58 UTC
Text Field Parser
Text node parser
We will use an example to explain the SVG text node parser.
<text font-size="20">
KDE
<tspan font-size=24>
Big
<tspan font-weight="bold">
BigBold
</tspan>
Big
</tspan>
</text>
In the pango, the attributes list will looks like
(0,15,font-size=20)
(3,15,font-size=24)
(6,12,font-weight=bold)
KDEBigBigBoldBig
Each attribute contains three fields. The first and the second are start and end position. The last one is the attribute for this range. In the SVG parser, we can not know the end until we see the close tag. Therefore, we need to generate the list in two phase. In the first phase, we will parser the tree recursively. During the parsing, we will build a list as
(0,0,font-size=20)
(3,0,font-size=24)
(6,0,font-weight=bold)
KDEBigBigBoldBig
This list will be kept in the memory since the end is not decided yet. In the end of each tag, we can now the length of the text of each tag and change the second field to the correct value.
In the second phase, we will generate codes according to the table.
I will use a pseudo code to explain this algorithm.
def translate_text(text):
attrs=[]
attr = [0,0,text]
attrs.append(attr)
The above code is used to initialize the attrs array. We will use it to keep the temporary attribute list whose end is 0. For the text node, we will generate a new attr and push it into the list.
for n in text.childNodes:
if n.localName == None: txt_strs = txt_strs + n.data
elif node.localName == 'tspan':
txt_strs = translate_tspan(n,txt_strs,attrs)
Then, we iterate all children to gather the text. If we meet tspan node, we will call translate_tspan, which is called recursively. The return value is the complete text string which is the input txt_strs and all text inside the tspan node. After we collect all text in all nodes, we will use it to update the attribute, like
attr[1] = len(txt_strs)-1
Finally, we can use this information to generate the text and its attribute.
generate_text(txt_strs,attr[2].getAttribute('x'),attr[2].getAttribute('y'))
for a in attrs:
translate_attribute(a)
The translate_tspan looks like the below code. It is very similar to the translate_text
def translate_tspan(tspan, txt_strs,attrs):
attr = [len(txt_strs),0,tspan]
attrs.append(attr)
for n in tspan:
if n.localName == None: txt_strs = txt_strs + n.data
elif n.localName == 'tspan':
txt_strs = translate_tspan(n,txt_strs,attrs)
attr[1] = len(txt_strs)-1
return txt_strs
Generate pango text object
The generate_text will generate a pango layout and call pango_layout_set_text. We use the PANGO_TEXT macro for this.
def generate_text(s,x,y):
print "PANGO_TEXT(\"%s\",%d,%d);\n" % (s,x,y)
def generate_attribute(a):
props = translate_style(a[2].getAttribute('style'))
for p in props:
if p.type = "size":
print "PANGO_ATTR_SIZE(%d,%d,%d);", p.value,a[0],a[1])
elif p.type == "font":
print "PANGO_ATTR_FONT(%d,%d,%d);", p.value,a[0],a[1])
....
The PANGO_TEXT will be expanded into
text = rdman_shape_text_new(rdman, "xxxxxx",x,y);
The PANGO_ATTR_SIZE will be expanded into
attr =pango_attr_size_new(20);
attr->start_index = 0;
attr->end_index = 15;
pango_cairo_attr_insert(text->attrs,attr);
The follwoing style are supported
font_size
The font size can be absolute size and relative size or length. We support only length now. It should be '12' or '12pt' or '12em'. It will be translated as PANGO_ATTR_SIZE
PANGO_SIZE(text,size,start,end)
This will be translated into the following lines by the mb_c_source.m4.
{
PangoAttribute *attr = pango_attr_size_new(size);
attr->start_index = start;
attr->end_index = end;
pango_cairo_attr_insert(text->attrs,attr);
}
font_style
The font style can be one of normal/italic/oblique/inherit. This can be translated into pango style
PANGO_STYLE(PANGO_STYLE_NORMAL, start, end) --> normal
PANGO_STYLE(PANGO_STYLE_ITALIC, start, end) --> italic
PANGO_STYLE(PANGO_STYLE_OBLIQUE, start, end) --> oblique
inherit will not generate any code.
This will be translated into the following lines by the mb_c_source.m4.
{
PangoAttribute *attr = pango_attr_style_new(PANGO_STYLE_NORMAL);
attr->start_index = start;
attr->end_index = end;
pango_cairo_attr_insert(text->attrs,attr);
}
font_family
The font family need to be translated into a specific font file. In the future, we will use the fontconfig to select the fonts. For now, let it to be fixed.
text_anchor
The anchor can be one of start/middle/end/inherit. It can be implemented by using the pango_layout_set_alignment. According to the SVG specification, each chunk(tspan) inside the text element can have different anchor. This determine how the text is put inside the text. In the MadButterfly, we don't support vertical placement. Therefore, only the X coordinate will be adjusted according to the anchor. The starting point of each tspan will be adjusted against to its natural position according to the text_anchor. If the anchor is start, the text will be placed at the natural position. If the anchor is middle, we will use the horizontal size of the text chunk to adjust to be
new_x = nat_x - tspan_width/2
It means that the text will be in the left to the natural position. If the text anchor is end, we will adjust the x to be
new_x = nat_x -tspan_width
This may not work as you expect. However, you should be able to under it by using the inkscape.
Unfortune, this pango_layout_set_alignment is not the same as the SVG. We need to translate it according to the SVG model. In addition, we need to think about how to design the MadButterfly API to change this dynamicly.
In order to implement this, we need to change the (x,y) of the text layout according to the anchor property. We can not use the set)alignment directly. It won't produce the correct result.
font_variant
normal/smallcap/inherit
We will not implement this for now since the inkscape has no such capability.
font_weight
normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit
The pango has only
- PANGO_WEIGHT_ULTRALIGHT = 200
- PANGO_WEIGHT_LIGHT = 300
- PANGO_WEIGHT_NORMAL = 400
- PANGO_WEIGHT_SEMIBOLD = 600
- PANGO_WEIGHT_BOLD = 700
- PANGO_WEIGHT_ULTRABOLD = 800
- PANGO_WEIGHT_HEAVY = 900
we will map 100 to 200, 500 to 600 and generate
PANGO_WEIGHT(PANGO_WEIGHT_NORMAL, start, end)
This will be translated into the following line sby the mb_c_source.m4.
{
PangoAttribute *attr = pango_attr_weight_new(PANGO_WEIGHT_NORMAL);
attr->start_index = start;
attr->end_index = end;
pango_cairo_attr_insert(text->attrs,attr);
}
direction
This will not be implemented for now. This is future enhancement.
bidi
This will not be implemented for now. This is future enhancement.