Merge lp:~inkscape.dev/inkscape/writing_modes into lp:~inkscape.dev/inkscape/trunk

Proposed by Tavmjong Bah
Status: Merged
Merged at revision: 14487
Proposed branch: lp:~inkscape.dev/inkscape/writing_modes
Merge into: lp:~inkscape.dev/inkscape/trunk
Diff against target: 2674 lines (+1247/-352)
22 files modified
share/icons/icons.svg (+347/-4)
src/desktop-style.cpp (+57/-5)
src/desktop-style.h (+1/-0)
src/display/nr-style.cpp (+0/-2)
src/display/nr-style.h (+0/-1)
src/libnrtype/FontInstance.cpp (+209/-29)
src/libnrtype/Layout-TNG-Compute.cpp (+200/-149)
src/libnrtype/Layout-TNG-Input.cpp (+10/-46)
src/libnrtype/Layout-TNG-OutIter.cpp (+9/-6)
src/libnrtype/Layout-TNG-Output.cpp (+46/-14)
src/libnrtype/Layout-TNG-Scanline-Maker.h (+7/-7)
src/libnrtype/Layout-TNG-Scanline-Makers.cpp (+14/-18)
src/libnrtype/Layout-TNG.h (+88/-15)
src/libnrtype/TextWrapper.cpp (+2/-2)
src/libnrtype/font-instance.h (+28/-5)
src/libnrtype/one-box.h (+1/-1)
src/style-enums.h (+31/-10)
src/style-internal.h (+0/-1)
src/style.cpp (+7/-4)
src/style.h (+2/-0)
src/widgets/text-toolbar.cpp (+186/-33)
src/widgets/toolbox.cpp (+2/-0)
To merge this branch: bzr merge lp:~inkscape.dev/inkscape/writing_modes
Reviewer Review Type Date Requested Status
Inkscape Developers Pending
Review via email: mp+276053@code.launchpad.net

Description of the change

* Latin characters in vertical text will be drawn by default sideways.
This is the default direction in both SVG 1.1 and in CSS 3 Writing
Modes. Currently Inkscape draws the glyphs upright.

* Punctuation characters in CJK vertical text will be orientated
correctly.

* The Text Toolbar can be used to set 'text-orientation' which controls
how characters are orientated in vertical text (it has no effect on
horizontal text). The three choices are 'mixed' where the glyphs are
drawn in their Unicode defined natural orientation, 'upright' where all
glyphs are drawn upright, and 'sideways' where all glyphs are drawn
sideways.

* The new CSS 3 values for 'writing-mode' will be recognized. The
values written out are still the SVG 1.1 values except for the new
'vertical-lr' value which has no equivalent SVG 1.1 value (and is
required for Mongolian). A new button appears on the Text Toolbar to
select this value. (The SVG 1.1 values are deprecated but must still be
recognized by SVG renderers.)

* Fonts without vertical metrics will have a vertical advance of 1em.
The CSS specification explicitly does not define what advance to use
but the SVG 1.1 spec suggests 1em and the W3C Japanese text document
specified an advance of 1em.

* In laying out multi-line text, any 'leading' value from the font is
ignored. CSS dictates that the font-size corresponds to the em height
of a font which is defined as 'ascent' + 'descent'. The change to
ignore the "internal" font 'leading' may make small visual changes in
how multi-line text appears.

* In flowed text, Inkscape has been using a "scaled" value of 'ascent'
to position the first line. This has been changed to follow the correct
CSS prescription of using the value of 'half-leading' + normal 'ascent'
for horizontal text (to find the alphabetic baseline) and 'half
-leading' + half em width for vertical text (to find the center
baseline). (The 'leading' in this case is the extra space needed
between lines as dictated by the 'line-height' property. It is not the
'internal' font 'leading'.)

To post a comment you must log in.
14432. By Tavmjong Bah

Vertical baseline depends on block 'text-orientation' value.

14433. By Tavmjong Bah

Rearrange code to make handling of baseline clearer.

14434. By Tavmjong Bah

'direction' is an inherited property, remove 'set' requirement.

14435. By Tavmjong Bah

Replace leading by more useful x-height. Use OS/2 font metrics when available.
Use otmMacAscent/otmMacDescent rather than otmAscent/otmDescent.
Actually both should be available as they have different purposes...

14436. By Tavmjong Bah

Read/write 'dominant-baseline' property.

14437. By Tavmjong Bah

Find font metrics when font is initialized. Fill baseline table.
Separately track typographic ascent/descent and maximum ascent/descent.

14438. By Tavmjong Bah

Turn FontMetrics into a class. Add maximum ascent/descent variables.

14439. By Tavmjong Bah

Code cleanup. Remove/simplify some functions.

14440. By Tavmjong Bah

Use maximum ascent and descent for glyphs to ensure that glyphs that extend
outside the em-box are fully drawn.

14441. By Tavmjong Bah

Implement rendering of 'dominant-baseline' property.

14442. By Tavmjong Bah

Swap text-before-edge and text-after-edge baseline values.

14443. By Tavmjong Bah

Remove unintended change that added C++11 flag.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'share/icons/icons.svg'
2--- share/icons/icons.svg 2015-07-27 00:04:29 +0000
3+++ share/icons/icons.svg 2015-11-24 09:40:36 +0000
4@@ -869,6 +869,45 @@
5 <linearGradient y2="217.2552" x2="173.0313" y1="207.7552" x1="161.2865" gradientTransform="matrix(1.102855,0,0,1.148517,-175.0966,-238.3557)" gradientUnits="userSpaceOnUse" id="linearGradient20190" xlink:href="#linearGradient3480" inkscape:collect="always" />
6 <linearGradient y2="218.5416" x2="173.724" y1="209.14059" x1="163.85941" gradientTransform="matrix(1.102855,0,0,1.148517,-174.5451,-237.7814)" gradientUnits="userSpaceOnUse" id="linearGradient20197" xlink:href="#linearGradient1887" inkscape:collect="always" />
7 <linearGradient y2="217.2552" x2="173.0313" y1="207.7552" x1="161.2865" gradientTransform="matrix(1.102855,0,0,1.148517,-175.0966,-238.3557)" gradientUnits="userSpaceOnUse" id="linearGradient20199" xlink:href="#linearGradient3480" inkscape:collect="always" />
8+ <linearGradient
9+ inkscape:collect="always"
10+ xlink:href="#linearGradient6524"
11+ id="linearGradient10133"
12+ gradientUnits="userSpaceOnUse"
13+ x1="860.239"
14+ y1="45.30668"
15+ x2="843.9635"
16+ y2="28.96459" />
17+ <linearGradient
18+ inkscape:collect="always"
19+ xlink:href="#linearGradient6524"
20+ id="linearGradient10161"
21+ gradientUnits="userSpaceOnUse"
22+ x1="860.239"
23+ y1="45.30668"
24+ x2="843.9635"
25+ y2="28.96459"
26+ gradientTransform="translate(-256,405)" />
27+ <linearGradient
28+ inkscape:collect="always"
29+ xlink:href="#linearGradient6524"
30+ id="linearGradient11127"
31+ gradientUnits="userSpaceOnUse"
32+ gradientTransform="translate(-226,405)"
33+ x1="860.239"
34+ y1="45.30668"
35+ x2="843.9635"
36+ y2="28.96459" />
37+ <linearGradient
38+ inkscape:collect="always"
39+ xlink:href="#linearGradient6524"
40+ id="linearGradient11155"
41+ gradientUnits="userSpaceOnUse"
42+ gradientTransform="translate(-196,405)"
43+ x1="860.239"
44+ y1="45.30668"
45+ x2="843.9635"
46+ y2="28.96459" />
47 </defs>
48 <sodipodi:namedview inkscape:guide-bbox="true" inkscape:current-layer="svg1" inkscape:grid-bbox="true" inkscape:pageopacity="1.0000000" pagecolor="#e8e8e4" snaptoguides="true" showguides="true" inkscape:window-y="27" inkscape:window-x="0" inkscape:window-height="960" inkscape:window-width="1280" inkscape:cy="336.2393" inkscape:cx="537.9384" inkscape:zoom="6.033032" gridtolerance="6" snaptogrid="false" showgrid="false" id="base" inkscape:document-units="px" inkscape:grid-points="true" guidetolerance="8" fill="#8ab3de" stroke="#646464" inkscape:object-nodes="false" objecttolerance="11" inkscape:snap-bbox="false" inkscape:snap-nodes="true" inkscape:bbox-nodes="false" inkscape:bbox-paths="false" inkscape:snap-global="false" inkscape:snap-center="false" inkscape:snap-midpoints="false" inkscape:snap-intersection-paths="true" inkscape:object-paths="false" inkscape:snap-object-midpoints="true" inkscape:window-maximized="1" inkscape:snap-grids="true" inkscape:snap-smooth-nodes="false" inkscape:snap-text-baseline="false" inkscape:snap-page="true" inkscape:snap-bbox-midpoints="false" inkscape:snap-bbox-edge-midpoints="false">
49 <inkscape:grid type="xygrid" id="grid9252" originx="0px" originy="0px" spacingx="0.5px" spacingy="0.5px" empspacing="2" visible="true" enabled="true" snapvisiblegridlinesonly="true" />
50@@ -1824,6 +1863,310 @@
51 <path style="fill:#5a5a5a;fill-rule:evenodd" d="M 851.4754,45 849,42 851,42 851,27 852,27 852,42 854,42 851.4754,45 Z" id="path6550" sodipodi:nodetypes="cccccccc" inkscape:connector-curvature="0" />
52 <use xlink:href="#path6550" height="1250" width="1250" transform="translate(-5.9754)" id="use5600" y="0" x="0" />
53 </g>
54+<g
55+ transform="translate(-196,375)"
56+ inkscape:label="#writing_mode_tb_lr"
57+ id="format-text-direction-vertical-lr">
58+ <path
59+ inkscape:connector-curvature="0"
60+ style="color:#000000;fill:#000000;fill-opacity:0.06666673;fill-rule:evenodd"
61+ d="m 841,26 22,0 1,1 0,20 -2,2 -20,0 -1,-1 0,-22 z"
62+ id="path10109"
63+ sodipodi:nodetypes="cccccccc" />
64+ <path
65+ inkscape:connector-curvature="0"
66+ id="path10111"
67+ d="m 840,26 1,0 0,20 -1,0 0,-20 z"
68+ style="color:#000000;fill:#000000;fill-rule:evenodd" />
69+ <rect
70+ style="color:#000000;fill:#000000;fill-rule:evenodd"
71+ id="rect10113"
72+ width="1"
73+ height="20"
74+ x="861"
75+ y="26" />
76+ <rect
77+ style="color:#000000;fill:#000000;fill-rule:evenodd"
78+ id="rect10115"
79+ width="1"
80+ height="20"
81+ x="-26"
82+ y="841"
83+ transform="matrix(0,-1,1,0,0,0)" />
84+ <rect
85+ style="color:#000000;fill:#000000;fill-rule:evenodd"
86+ id="rect10117"
87+ width="1"
88+ height="20"
89+ x="-47"
90+ y="841"
91+ transform="matrix(0,-1,1,0,0,0)" />
92+ <rect
93+ style="color:#000000;fill:url(#linearGradient10133);fill-rule:evenodd"
94+ id="rect10119"
95+ width="20"
96+ height="20"
97+ x="841"
98+ y="26" />
99+ <path
100+ inkscape:connector-curvature="0"
101+ id="path10121"
102+ d="m 842,46 -1,0 0,-20 20,0 0,1 -19,0 0,19 z"
103+ style="fill:#ffffff;fill-rule:evenodd" />
104+ <path
105+ inkscape:connector-curvature="0"
106+ sodipodi:nodetypes="cccc"
107+ id="path10123"
108+ d="m 843.5,34.7482 0,-4 c 0,-3 5,-3 5,0 l 0,4"
109+ style="fill:none;stroke:#383838;stroke-width:1.00000012px;stroke-linecap:round" />
110+ <path
111+ inkscape:connector-curvature="0"
112+ id="path10125"
113+ d="m 843.3571,31.9982 5.1429,0"
114+ style="fill:none;stroke:#383838;stroke-width:1.00000012px" />
115+ <path
116+ inkscape:connector-curvature="0"
117+ sodipodi:nodetypes="cccccccc"
118+ id="path10127"
119+ d="m 845.5492,45 2.4754,-3.0009 -2,0 0,-7 -1,0 0,7 -2,0 2.5246,3.0009 z"
120+ style="fill:#5a5a5a;fill-rule:evenodd" />
121+ <path
122+ inkscape:connector-curvature="0"
123+ sodipodi:nodetypes="cccccccc"
124+ id="path10129"
125+ d="m 851.4754,45 -2.4754,-3 2,0 0,-15 1,0 0,15 2,0 -2.5246,3 z"
126+ style="fill:#5a5a5a;fill-rule:evenodd" />
127+ <use
128+ x="0"
129+ y="0"
130+ id="use10131"
131+ transform="translate(6,0)"
132+ width="1250"
133+ height="1250"
134+ xlink:href="#path6550" />
135+</g>
136+<g
137+ id="text-orientation-auto"
138+ inkscape:label="#text_orientation_auto">
139+ <path
140+ inkscape:connector-curvature="0"
141+ style="color:#000000;fill:#000000;fill-opacity:0.06666673;fill-rule:evenodd"
142+ d="m 585,431 22,0 1,1 0,20 -2,2 -20,0 -1,-1 0,-22 z"
143+ id="path10137"
144+ sodipodi:nodetypes="cccccccc" />
145+ <path
146+ inkscape:connector-curvature="0"
147+ id="path10139"
148+ d="m 584,431 1,0 0,20 -1,0 0,-20 z"
149+ style="color:#000000;fill:#000000;fill-rule:evenodd" />
150+ <rect
151+ style="color:#000000;fill:#000000;fill-rule:evenodd"
152+ id="rect10141"
153+ width="1"
154+ height="20"
155+ x="605"
156+ y="431" />
157+ <rect
158+ style="color:#000000;fill:#000000;fill-rule:evenodd"
159+ id="rect10143"
160+ width="1"
161+ height="20"
162+ x="-431"
163+ y="585"
164+ transform="matrix(0,-1,1,0,0,0)" />
165+ <rect
166+ style="color:#000000;fill:#000000;fill-rule:evenodd"
167+ id="rect10145"
168+ width="1"
169+ height="20"
170+ x="-452"
171+ y="585"
172+ transform="matrix(0,-1,1,0,0,0)" />
173+ <rect
174+ style="color:#000000;fill:url(#linearGradient10161);fill-rule:evenodd"
175+ id="rect10147"
176+ width="20"
177+ height="20"
178+ x="585"
179+ y="431" />
180+ <path
181+ inkscape:connector-curvature="0"
182+ id="path10149"
183+ d="m 586,451 -1,0 0,-20 20,0 0,1 -19,0 0,19 z"
184+ style="fill:#ffffff;fill-rule:evenodd" />
185+ <path
186+ inkscape:connector-curvature="0"
187+ sodipodi:nodetypes="cccc"
188+ id="path10151"
189+ d="m 595.30711,449.12856 4,0 c 3,0 3,-5 0,-5 l -4,0"
190+ style="fill:none;stroke:#383838;stroke-width:1.00000012px;stroke-linecap:round" />
191+ <path
192+ inkscape:connector-curvature="0"
193+ id="path10153"
194+ d="m 598.05711,449.27146 0,-5.1429"
195+ style="fill:none;stroke:#383838;stroke-width:1.00000012px" />
196+ <use
197+ x="0"
198+ y="0"
199+ id="use10159"
200+ transform="translate(-261,405)"
201+ width="1250"
202+ height="1250"
203+ xlink:href="#path6550" />
204+ <path
205+ id="path11017"
206+ style="color:#000000;solid-opacity:1;fill:none;stroke:#383838;stroke-width:1.00000012px;stroke-linecap:round;stroke-linejoin:round"
207+ d="m 595,438.5 7.5,0.0854 m -6,-2.5854 4.20941,0 -1.70941,1.5 0.0234,3 c -0.0234,0.5 -0.52344,1 -1.02344,1 l -1,0 m 2.00004,-7.72791 -0.5,-0.77209 m -3.5,2.00274 0,-1.00274 7.5,0 -0.0207,0.98202"
208+ inkscape:connector-curvature="0" />
209+</g>
210+<g
211+ id="text-orientation-upright"
212+ inkscape:label="#text_orientation_upright">
213+ <path
214+ sodipodi:nodetypes="cccccccc"
215+ id="path11105"
216+ d="m 615,431 22,0 1,1 0,20 -2,2 -20,0 -1,-1 0,-22 z"
217+ style="color:#000000;fill:#000000;fill-opacity:0.06666673;fill-rule:evenodd"
218+ inkscape:connector-curvature="0" />
219+ <path
220+ style="color:#000000;fill:#000000;fill-rule:evenodd"
221+ d="m 614,431 1,0 0,20 -1,0 0,-20 z"
222+ id="path11107"
223+ inkscape:connector-curvature="0" />
224+ <rect
225+ y="431"
226+ x="635"
227+ height="20"
228+ width="1"
229+ id="rect11109"
230+ style="color:#000000;fill:#000000;fill-rule:evenodd" />
231+ <rect
232+ transform="matrix(0,-1,1,0,0,0)"
233+ y="615"
234+ x="-431"
235+ height="20"
236+ width="1"
237+ id="rect11111"
238+ style="color:#000000;fill:#000000;fill-rule:evenodd" />
239+ <rect
240+ transform="matrix(0,-1,1,0,0,0)"
241+ y="615"
242+ x="-452"
243+ height="20"
244+ width="1"
245+ id="rect11113"
246+ style="color:#000000;fill:#000000;fill-rule:evenodd" />
247+ <rect
248+ y="431"
249+ x="615"
250+ height="20"
251+ width="20"
252+ id="rect11115"
253+ style="color:#000000;fill:url(#linearGradient11127);fill-rule:evenodd" />
254+ <path
255+ style="fill:#ffffff;fill-rule:evenodd"
256+ d="m 616,451 -1,0 0,-20 20,0 0,1 -19,0 0,19 z"
257+ id="path11117"
258+ inkscape:connector-curvature="0" />
259+ <path
260+ style="fill:none;stroke:#383838;stroke-width:1.00000012px;stroke-linecap:round"
261+ d="m 630.92855,449.75 0,-4 c 0,-3 -5,-3 -5,0 l 0,4"
262+ id="path11119"
263+ sodipodi:nodetypes="cccc"
264+ inkscape:connector-curvature="0" />
265+ <path
266+ style="fill:none;stroke:#383838;stroke-width:1.00000012px"
267+ d="m 631.07145,447 -5.1429,0"
268+ id="path11121"
269+ inkscape:connector-curvature="0" />
270+ <use
271+ xlink:href="#path6550"
272+ height="1250"
273+ width="1250"
274+ transform="translate(-231,405)"
275+ id="use11123"
276+ y="0"
277+ x="0" />
278+ <path
279+ inkscape:connector-curvature="0"
280+ d="m 625,438.5 7.5,0.0854 m -6,-2.5854 4.20941,0 -1.70941,1.5 0.0234,3 c -0.0234,0.5 -0.52344,1 -1.02344,1 l -1,0 m 2.00004,-7.72791 -0.5,-0.77209 m -3.5,2.00274 0,-1.00274 7.5,0 -0.0207,0.98202"
281+ style="color:#000000;solid-opacity:1;fill:none;stroke:#383838;stroke-width:1.00000012px;stroke-linecap:round;stroke-linejoin:round"
282+ id="path11125" />
283+</g>
284+<g
285+ id="text-orientation-sideways"
286+ inkscape:label="#text_orientation_sideways">
287+ <path
288+ inkscape:connector-curvature="0"
289+ style="color:#000000;fill:#000000;fill-opacity:0.06666673;fill-rule:evenodd"
290+ d="m 645,431 22,0 1,1 0,20 -2,2 -20,0 -1,-1 0,-22 z"
291+ id="path11133"
292+ sodipodi:nodetypes="cccccccc" />
293+ <path
294+ inkscape:connector-curvature="0"
295+ id="path11135"
296+ d="m 644,431 1,0 0,20 -1,0 0,-20 z"
297+ style="color:#000000;fill:#000000;fill-rule:evenodd" />
298+ <rect
299+ style="color:#000000;fill:#000000;fill-rule:evenodd"
300+ id="rect11137"
301+ width="1"
302+ height="20"
303+ x="665"
304+ y="431" />
305+ <rect
306+ style="color:#000000;fill:#000000;fill-rule:evenodd"
307+ id="rect11139"
308+ width="1"
309+ height="20"
310+ x="-431"
311+ y="645"
312+ transform="matrix(0,-1,1,0,0,0)" />
313+ <rect
314+ style="color:#000000;fill:#000000;fill-rule:evenodd"
315+ id="rect11141"
316+ width="1"
317+ height="20"
318+ x="-452"
319+ y="645"
320+ transform="matrix(0,-1,1,0,0,0)" />
321+ <rect
322+ style="color:#000000;fill:url(#linearGradient11155);fill-rule:evenodd"
323+ id="rect11143"
324+ width="20"
325+ height="20"
326+ x="645"
327+ y="431" />
328+ <path
329+ inkscape:connector-curvature="0"
330+ id="path11145"
331+ d="m 646,451 -1,0 0,-20 20,0 0,1 -19,0 0,19 z"
332+ style="fill:#ffffff;fill-rule:evenodd" />
333+ <path
334+ inkscape:connector-curvature="0"
335+ sodipodi:nodetypes="cccc"
336+ id="path11147"
337+ d="m 655.30711,449.12856 4,0 c 3,0 3,-5 0,-5 l -4,0"
338+ style="fill:none;stroke:#383838;stroke-width:1.00000012px;stroke-linecap:round" />
339+ <path
340+ inkscape:connector-curvature="0"
341+ id="path11149"
342+ d="m 658.05711,449.27146 0,-5.1429"
343+ style="fill:none;stroke:#383838;stroke-width:1.00000012px" />
344+ <use
345+ x="0"
346+ y="0"
347+ id="use11151"
348+ transform="translate(-201,405)"
349+ width="1250"
350+ height="1250"
351+ xlink:href="#path6550" />
352+ <path
353+ id="path11153"
354+ style="color:#000000;solid-opacity:1;fill:none;stroke:#383838;stroke-width:1.00000012px;stroke-linecap:round;stroke-linejoin:round"
355+ d="m 657.49703,433.49703 -0.0854,7.5 m 2.5854,-6 0,4.20941 -1.5,-1.70941 -3,0.0234 c -0.5,-0.0234 -1,-0.52344 -1,-1.02344 l 0,-1 m 7.72791,2.00004 0.77209,-0.5 m -2.00274,-3.5 1.00274,0 0,7.5 -0.98202,-0.0207"
356+ inkscape:connector-curvature="0" />
357+</g>
358 <g id="align-horizontal-right-to-anchor" transform="matrix(1,0,0,0.998006,1017.995,-110.6741)" inkscape:label="#al_left_out">
359 <g id="g5060">
360 <rect style="color:#000000;fill:none;stroke:url(#linearGradient5531);stroke-width:1.0009987;stroke-linejoin:round;stroke-miterlimit:0" id="rect5048" width="6.008899" height="5.011291" x="-27.50193" y="175.5202" />
361@@ -3958,11 +4301,11 @@
362 <path style="fill:#ffffff;fill-rule:evenodd;stroke:url(#linearGradient5799-9);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round" d="m 9.5,0.5 4,4 -4,0 0,-4 z" id="path4681-4" />
363 </g>
364 <g transform="translate(236.3605,-335.8446)" id="flatten_simplify" inkscape:label="#flatten_simplify">
365-<rect y="536.7323" x="282.5811" height="15.721" width="15.721" id="rect13989" style="color:#000000;display:inline;fill:none;stroke:none;stroke-width:1;marker:none" />
366-<path style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" d="M 530.5332 205.6113 A 1.9925 2.080405 0 0 0 528.6133 207.1426 C 527.6704 207.4134 526.7115 207.8353 525.7988 208.5332 C 525.5735 208.7058 525.3617 208.8398 525.1465 208.9844 A 1.9925 2.080405 0 0 0 523.8516 208.4824 A 1.9925 2.080405 0 0 0 521.8672 210.3984 C 520.9451 210.5699 520.1488 210.5515 519.4629 210.5469 L 519.4512 212.0469 C 520.1403 212.0516 521.1103 212.0612 522.2676 211.8223 A 1.9925 2.080405 0 0 0 523.8516 212.6445 A 1.9925 2.080405 0 0 0 525.8438 210.5645 A 1.9925 2.080405 0 0 0 525.8281 210.3281 C 526.1184 210.1416 526.4096 209.954 526.709 209.7246 C 527.3764 209.2142 528.069 208.8737 528.7617 208.6484 A 1.9925 2.080405 0 0 0 530.5332 209.7734 A 1.9925 2.080405 0 0 0 532.4453 208.2656 C 533.1733 208.2979 533.843 208.3555 534.3984 208.3555 L 534.3984 206.8555 C 533.9353 206.8555 533.1903 206.7721 532.3047 206.7402 A 1.9925 2.080405 0 0 0 530.5332 205.6113 z M 530.5332 206.7246 A 0.9260918 0.9669491 0 0 1 531.459 207.6914 A 0.9260918 0.9669491 0 0 1 530.5332 208.6582 A 0.9260918 0.9669491 0 0 1 529.6055 207.6914 A 0.9260918 0.9669491 0 0 1 530.5332 206.7246 z M 523.8516 209.5957 A 0.9260918 0.9669491 0 0 1 524.7773 210.5645 A 0.9260918 0.9669491 0 0 1 523.8516 211.5312 A 0.9260918 0.9669491 0 0 1 522.9258 210.5645 A 0.9260918 0.9669491 0 0 1 523.8516 209.5957 z " transform="translate(-236.3605,335.8446)" id="path14280" />
367+<rect y="536.7323" x="282.5811" height="15.721" width="15.721" id="rect13989" style="color:#000000;fill:none" />
368+<path style="color:#000000;solid-opacity:1;fill:#000000;fill-rule:evenodd;stroke-width:1.5" d="M 530.5332 205.6113 A 1.9925 2.080405 0 0 0 528.6133 207.1426 C 527.6704 207.4134 526.7115 207.8353 525.7988 208.5332 C 525.5735 208.7058 525.3617 208.8398 525.1465 208.9844 A 1.9925 2.080405 0 0 0 523.8516 208.4824 A 1.9925 2.080405 0 0 0 521.8672 210.3984 C 520.9451 210.5699 520.1488 210.5515 519.4629 210.5469 L 519.4512 212.0469 C 520.1403 212.0516 521.1103 212.0612 522.2676 211.8223 A 1.9925 2.080405 0 0 0 523.8516 212.6445 A 1.9925 2.080405 0 0 0 525.8438 210.5645 A 1.9925 2.080405 0 0 0 525.8281 210.3281 C 526.1184 210.1416 526.4096 209.954 526.709 209.7246 C 527.3764 209.2142 528.069 208.8737 528.7617 208.6484 A 1.9925 2.080405 0 0 0 530.5332 209.7734 A 1.9925 2.080405 0 0 0 532.4453 208.2656 C 533.1733 208.2979 533.843 208.3555 534.3984 208.3555 L 534.3984 206.8555 C 533.9353 206.8555 533.1903 206.7721 532.3047 206.7402 A 1.9925 2.080405 0 0 0 530.5332 205.6113 z M 530.5332 206.7246 A 0.9260918 0.9669491 0 0 1 531.459 207.6914 A 0.9260918 0.9669491 0 0 1 530.5332 208.6582 A 0.9260918 0.9669491 0 0 1 529.6055 207.6914 A 0.9260918 0.9669491 0 0 1 530.5332 206.7246 z M 523.8516 209.5957 A 0.9260918 0.9669491 0 0 1 524.7773 210.5645 A 0.9260918 0.9669491 0 0 1 523.8516 211.5312 A 0.9260918 0.9669491 0 0 1 522.9258 210.5645 A 0.9260918 0.9669491 0 0 1 523.8516 209.5957 z " transform="translate(-236.3605,335.8446)" id="path14280" />
369 </g>
370 <g inkscape:label="#interactive_simplify" id="interactive_simplify" transform="translate(215.9205,-334.4098)">
371-<rect style="color:#000000;display:inline;fill:none;stroke:none;stroke-width:1;marker:none" id="rect17937-7" width="15.721" height="15.721" x="284.9688" y="535.6099" />
372-<path style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" d="m 290.1793,536.2979 c -0.7351,-0.02 -1.3963,0.2336 -1.8416,0.7418 -0.4453,0.5079 -0.6453,1.1819 -0.7277,1.8733 -0.165,1.3827 0.089,2.9888 0.3519,4.5721 0.2625,1.5832 0.5439,3.1441 0.5465,4.1601 0,0.5079 -0.082,0.854 -0.1453,0.9691 -0.064,0.115 -0.041,0.1361 -0.3174,0.1282 -0.6247,-0.018 -0.8383,-0.2559 -1.06,-0.7845 -0.2217,-0.5286 -0.3197,-1.3858 -0.2995,-2.2749 0.041,-1.7782 0.1181,-2.1383 0.1181,-2.1383 l -1.3819,-0.012 c 0,0 -0.034,0.44 -0.081,2.4977 -0.023,1.0288 -0.041,1.7085 0.3488,2.6369 0.3896,0.9284 1.2262,1.6826 2.3192,1.7143 0.6352,0.018 1.2607,-0.3347 1.5691,-0.8922 0.3084,-0.5575 0.3684,-1.1856 0.3668,-1.8493 0,-1.3269 -0.3139,-2.9046 -0.5719,-4.461 -0.258,-1.5566 -0.4523,-3.0895 -0.3384,-4.044 0.057,-0.4774 0.1784,-0.773 0.3189,-0.9332 0.1403,-0.1602 0.3293,-0.2738 0.7905,-0.2615 0.4549,0.012 0.691,0.1248 0.822,0.2393 0.131,0.1144 0.2075,0.2532 0.262,0.5316 0.109,0.5566 -0.012,1.5867 -0.2126,2.6612 -0.2001,1.0747 -0.4614,2.1843 -0.4372,3.2167 0.012,0.5163 0.098,1.0527 0.4133,1.5075 0.3153,0.4549 0.8398,0.7126 1.3954,0.7452 0.4606,0.027 0.881,-0.051 1.2367,-0.2735 0.3557,-0.2231 0.6121,-0.5902 0.7531,-0.9588 0.282,-0.7375 0.2377,-1.4817 0.2126,-2.157 -0.025,-0.6754 -0.041,-1.301 0.021,-1.5725 0.031,-0.1358 0.056,-0.155 0.057,-0.1555 6e-4,-6e-4 0.022,-0.034 0.2006,-0.029 0.218,0 0.2382,0.043 0.271,0.074 0.033,0.031 0.092,0.1224 0.1947,0.323 0.1022,0.2006 0.2482,0.5079 0.5375,0.7743 0.2893,0.2664 0.7035,0.4386 1.1723,0.4614 1.6094,-0.2758 2.284,-1.0042 3.4062,-0.084 l 0,-1.6613 c -1.4875,-0.7361 -2.2939,-0.161 -3.3448,0.1059 -0.2401,-0.012 -0.2878,-0.059 -0.3309,-0.099 -0.043,-0.04 -0.1003,-0.1294 -0.1991,-0.3231 -0.099,-0.1938 -0.2357,-0.493 -0.5181,-0.7606 -0.2825,-0.2676 -0.6959,-0.4362 -1.1528,-0.4495 -0.4137,-0.012 -0.8221,0.1078 -1.1305,0.388 -0.3084,0.2805 -0.4715,0.6683 -0.5524,1.0255 -0.162,0.7142 -0.091,1.4129 -0.067,2.0545 0.024,0.6418 -0.01,1.2067 -0.093,1.4289 -0.043,0.1112 -0.066,0.1378 -0.1273,0.176 -0.061,0.038 -0.1963,0.09 -0.4716,0.074 -0.2756,-0.016 -0.3064,-0.07 -0.3429,-0.1231 -0.037,-0.053 -0.098,-0.2166 -0.1048,-0.535 -0.015,-0.6367 0.2002,-1.72 0.4073,-2.8321 0.2071,-1.1119 0.4237,-2.265 0.2096,-3.3586 -0.1071,-0.5468 -0.3582,-1.0977 -0.792,-1.4767 -0.434,-0.379 -1.0001,-0.5617 -1.6605,-0.5794 z" id="path14318-5-3" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccccccccccccccsccccccccccccsccccccccccccccccccccc" />
373+<rect style="color:#000000;fill:none" id="rect17937-7" width="15.721" height="15.721" x="284.9688" y="535.6099" />
374+<path style="color:#000000;solid-opacity:1;fill:#000000;stroke-width:1.3" d="m 290.1793,536.2979 c -0.7351,-0.02 -1.3963,0.2336 -1.8416,0.7418 -0.4453,0.5079 -0.6453,1.1819 -0.7277,1.8733 -0.165,1.3827 0.089,2.9888 0.3519,4.5721 0.2625,1.5832 0.5439,3.1441 0.5465,4.1601 0,0.5079 -0.082,0.854 -0.1453,0.9691 -0.064,0.115 -0.041,0.1361 -0.3174,0.1282 -0.6247,-0.018 -0.8383,-0.2559 -1.06,-0.7845 -0.2217,-0.5286 -0.3197,-1.3858 -0.2995,-2.2749 0.041,-1.7782 0.1181,-2.1383 0.1181,-2.1383 l -1.3819,-0.012 c 0,0 -0.034,0.44 -0.081,2.4977 -0.023,1.0288 -0.041,1.7085 0.3488,2.6369 0.3896,0.9284 1.2262,1.6826 2.3192,1.7143 0.6352,0.018 1.2607,-0.3347 1.5691,-0.8922 0.3084,-0.5575 0.3684,-1.1856 0.3668,-1.8493 0,-1.3269 -0.3139,-2.9046 -0.5719,-4.461 -0.258,-1.5566 -0.4523,-3.0895 -0.3384,-4.044 0.057,-0.4774 0.1784,-0.773 0.3189,-0.9332 0.1403,-0.1602 0.3293,-0.2738 0.7905,-0.2615 0.4549,0.012 0.691,0.1248 0.822,0.2393 0.131,0.1144 0.2075,0.2532 0.262,0.5316 0.109,0.5566 -0.012,1.5867 -0.2126,2.6612 -0.2001,1.0747 -0.4614,2.1843 -0.4372,3.2167 0.012,0.5163 0.098,1.0527 0.4133,1.5075 0.3153,0.4549 0.8398,0.7126 1.3954,0.7452 0.4606,0.027 0.881,-0.051 1.2367,-0.2735 0.3557,-0.2231 0.6121,-0.5902 0.7531,-0.9588 0.282,-0.7375 0.2377,-1.4817 0.2126,-2.157 -0.025,-0.6754 -0.041,-1.301 0.021,-1.5725 0.031,-0.1358 0.056,-0.155 0.057,-0.1555 6e-4,-6e-4 0.022,-0.034 0.2006,-0.029 0.218,0 0.2382,0.043 0.271,0.074 0.033,0.031 0.092,0.1224 0.1947,0.323 0.1022,0.2006 0.2482,0.5079 0.5375,0.7743 0.2893,0.2664 0.7035,0.4386 1.1723,0.4614 1.6094,-0.2758 2.284,-1.0042 3.4062,-0.084 l 0,-1.6613 c -1.4875,-0.7361 -2.2939,-0.161 -3.3448,0.1059 -0.2401,-0.012 -0.2878,-0.059 -0.3309,-0.099 -0.043,-0.04 -0.1003,-0.1294 -0.1991,-0.3231 -0.099,-0.1938 -0.2357,-0.493 -0.5181,-0.7606 -0.2825,-0.2676 -0.6959,-0.4362 -1.1528,-0.4495 -0.4137,-0.012 -0.8221,0.1078 -1.1305,0.388 -0.3084,0.2805 -0.4715,0.6683 -0.5524,1.0255 -0.162,0.7142 -0.091,1.4129 -0.067,2.0545 0.024,0.6418 -0.01,1.2067 -0.093,1.4289 -0.043,0.1112 -0.066,0.1378 -0.1273,0.176 -0.061,0.038 -0.1963,0.09 -0.4716,0.074 -0.2756,-0.016 -0.3064,-0.07 -0.3429,-0.1231 -0.037,-0.053 -0.098,-0.2166 -0.1048,-0.535 -0.015,-0.6367 0.2002,-1.72 0.4073,-2.8321 0.2071,-1.1119 0.4237,-2.265 0.2096,-3.3586 -0.1071,-0.5468 -0.3582,-1.0977 -0.792,-1.4767 -0.434,-0.379 -1.0001,-0.5617 -1.6605,-0.5794 z" id="path14318-5-3" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccccccccccccccsccccccccccccsccccccccccccccccccccc" />
375 </g>
376 </svg>
377
378=== modified file 'src/desktop-style.cpp'
379--- src/desktop-style.cpp 2015-10-28 20:43:30 +0000
380+++ src/desktop-style.cpp 2015-11-24 09:40:36 +0000
381@@ -500,7 +500,7 @@
382
383 SPIPaint *paint_res = isfill? &style_res->fill : &style_res->stroke;
384 bool paintImpossible = true;
385- paint_res->set = TRUE;
386+ paint_res->set = true;
387
388 SVGICCColor* iccColor = 0;
389
390@@ -1144,7 +1144,7 @@
391 different = true; // different styles
392 }
393
394- set = TRUE;
395+ set = true;
396 style_res->font_weight.value = style_res->font_weight.computed = style->font_weight.computed;
397 style_res->font_style.value = style_res->font_style.computed = style->font_style.computed;
398 style_res->font_stretch.value = style_res->font_stretch.computed = style->font_stretch.computed;
399@@ -1256,6 +1256,56 @@
400 }
401
402
403+/**
404+ * Write to style_res the average writing modes style of objects.
405+ */
406+int
407+objects_query_writing_modes (const std::vector<SPItem*> &objects, SPStyle *style_res)
408+{
409+ bool different = false;
410+ bool set = false;
411+
412+ int texts = 0;
413+
414+ for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) {
415+ SPObject *obj = *i;
416+
417+ if (!isTextualItem(obj)) {
418+ continue;
419+ }
420+
421+ SPStyle *style = obj->style;
422+ if (!style) {
423+ continue;
424+ }
425+
426+ texts ++;
427+
428+ if (set &&
429+ ( ( style_res->writing_mode.computed != style->writing_mode.computed ) ||
430+ ( style_res->text_orientation.computed != style->text_orientation.computed ) ) ) {
431+ different = true; // different styles
432+ }
433+
434+ set = true;
435+ style_res->writing_mode.computed = style->writing_mode.computed;
436+ style_res->text_orientation.computed = style->text_orientation.computed;
437+ }
438+
439+ if (texts == 0 || !set)
440+ return QUERY_STYLE_NOTHING;
441+
442+ if (texts > 1) {
443+ if (different) {
444+ return QUERY_STYLE_MULTIPLE_DIFFERENT;
445+ } else {
446+ return QUERY_STYLE_MULTIPLE_SAME;
447+ }
448+ } else {
449+ return QUERY_STYLE_SINGLE;
450+ }
451+}
452+
453 int
454 objects_query_fontfeaturesettings (const std::vector<SPItem*> &objects, SPStyle *style_res)
455 {
456@@ -1293,7 +1343,7 @@
457 style_res->font_feature_settings.value = NULL;
458 }
459
460- style_res->font_feature_settings.set = TRUE;
461+ style_res->font_feature_settings.set = true;
462 style_res->font_feature_settings.value = g_strdup(style->font_feature_settings.value);
463 }
464
465@@ -1449,7 +1499,7 @@
466 style_res->font_family.value = NULL;
467 }
468
469- style_res->font_family.set = TRUE;
470+ style_res->font_family.set = true;
471 style_res->font_family.value = g_strdup(style->font_family.value);
472 }
473
474@@ -1508,7 +1558,7 @@
475 style_res->font_specification.value = NULL;
476 }
477
478- style_res->font_specification.set = TRUE;
479+ style_res->font_specification.set = true;
480 style_res->font_specification.value = g_strdup(style->font_specification.value);
481 }
482 }
483@@ -1728,6 +1778,8 @@
484 return objects_query_fontfeaturesettings (list, style);
485 } else if (property == QUERY_STYLE_PROPERTY_FONTNUMBERS) {
486 return objects_query_fontnumbers (list, style);
487+ } else if (property == QUERY_STYLE_PROPERTY_WRITINGMODES) {
488+ return objects_query_writing_modes (list, style);
489 } else if (property == QUERY_STYLE_PROPERTY_BASELINES) {
490 return objects_query_baselines (list, style);
491
492
493=== modified file 'src/desktop-style.h'
494--- src/desktop-style.h 2015-06-22 14:13:38 +0000
495+++ src/desktop-style.h 2015-11-24 09:40:36 +0000
496@@ -51,6 +51,7 @@
497 QUERY_STYLE_PROPERTY_FONTFEATURESETTINGS, // font feature settings (OpenType features)
498 QUERY_STYLE_PROPERTY_FONTNUMBERS, // size, spacings
499 QUERY_STYLE_PROPERTY_BASELINES, // baseline-shift
500+ QUERY_STYLE_PROPERTY_WRITINGMODES, // writing-mode, text-orientation
501 QUERY_STYLE_PROPERTY_MASTEROPACITY, // opacity
502 QUERY_STYLE_PROPERTY_BLEND, // blend
503 QUERY_STYLE_PROPERTY_BLUR // blur
504
505=== modified file 'src/display/nr-style.cpp'
506--- src/display/nr-style.cpp 2014-12-23 09:25:08 +0000
507+++ src/display/nr-style.cpp 2015-11-24 09:40:36 +0000
508@@ -68,7 +68,6 @@
509 , tspan_width(0)
510 , ascender(0)
511 , descender(0)
512- , line_gap(0)
513 , underline_thickness(0)
514 , underline_position(0)
515 , line_through_thickness(0)
516@@ -330,7 +329,6 @@
517 tspan_width = style->text_decoration_data.tspan_width;
518 ascender = style->text_decoration_data.ascender;
519 descender = style->text_decoration_data.descender;
520- line_gap = style->text_decoration_data.line_gap;
521 underline_thickness = style->text_decoration_data.underline_thickness;
522 underline_position = style->text_decoration_data.underline_position;
523 line_through_thickness = style->text_decoration_data.line_through_thickness;
524
525=== modified file 'src/display/nr-style.h'
526--- src/display/nr-style.h 2014-12-21 14:29:02 +0000
527+++ src/display/nr-style.h 2015-11-24 09:40:36 +0000
528@@ -115,7 +115,6 @@
529 float tspan_width;
530 float ascender;
531 float descender;
532- float line_gap;
533 float underline_thickness;
534 float underline_position;
535 float line_through_thickness;
536
537=== modified file 'src/libnrtype/FontInstance.cpp'
538--- src/libnrtype/FontInstance.cpp 2014-10-06 00:18:46 +0000
539+++ src/libnrtype/FontInstance.cpp 2015-11-24 09:40:36 +0000
540@@ -21,6 +21,7 @@
541 #include FT_BBOX_H
542 #include FT_TRUETYPE_TAGS_H
543 #include FT_TRUETYPE_TABLES_H
544+#include FT_GLYPH_H
545 #include <pango/pangoft2.h>
546 #include <2geom/pathvector.h>
547 #include <2geom/path-sink.h>
548@@ -183,6 +184,20 @@
549 theFace(0)
550 {
551 //printf("font instance born\n");
552+ _ascent = _ascent_max = 0.8;
553+ _descent = _descent_max = 0.2;
554+ _xheight = 0.5;
555+
556+ // Default baseline values, alphabetic is reference
557+ _baselines[ SP_CSS_BASELINE_AUTO ] = 0.0;
558+ _baselines[ SP_CSS_BASELINE_ALPHABETIC ] = 0.0;
559+ _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] = -_descent;
560+ _baselines[ SP_CSS_BASELINE_HANGING ] = 0.8 * _ascent;
561+ _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = 0.8 * _xheight;
562+ _baselines[ SP_CSS_BASELINE_CENTRAL ] = 0.5 - _descent;
563+ _baselines[ SP_CSS_BASELINE_MIDDLE ] = 0.5 * _xheight;
564+ _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] = _ascent;
565+ _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] = -_descent;
566 }
567
568 font_instance::~font_instance(void)
569@@ -259,6 +274,7 @@
570 FT_Select_Charmap(theFace,ft_encoding_unicode) && FT_Select_Charmap(theFace,ft_encoding_symbol);
571 }
572 #endif
573+ FindFontMetrics();
574 }
575 }
576
577@@ -374,10 +390,10 @@
578 GLYPHMETRICS metrics;
579 DWORD bufferSize=GetGlyphOutline (parent->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
580 double scale=1.0/parent->fontSize;
581- n_g.h_advance=metrics.gmCellIncX*scale;
582- n_g.v_advance=otm.otmTextMetrics.tmHeight*scale;
583- n_g.h_width=metrics.gmBlackBoxX*scale;
584- n_g.v_width=metrics.gmBlackBoxY*scale;
585+ n_g.h_advance = metrics.gmCellIncX * scale;
586+ n_g.v_advance = otm.otmTextMetrics.tmHeight * scale;
587+ n_g.h_width = metrics.gmBlackBoxX * scale;
588+ n_g.v_width = metrics.gmBlackBoxY * scale;
589 if ( bufferSize == GDI_ERROR) {
590 // shit happened
591 } else if ( bufferSize == 0) {
592@@ -459,7 +475,13 @@
593 n_g.v_advance=((double)theFace->glyph->metrics.vertAdvance)/((double)theFace->units_per_EM);
594 n_g.v_width=((double)theFace->glyph->metrics.height)/((double)theFace->units_per_EM);
595 } else {
596- n_g.v_width=n_g.v_advance=((double)theFace->height)/((double)theFace->units_per_EM);
597+ // CSS3 Writing modes dictates that if vertical font metrics are missing we must
598+ // synthisize them. No method is specified. The SVG 1.1 spec suggests using the em
599+ // height (which is not theFace->height as that includes leading). The em height
600+ // is ascender + descender (descender positive). Note: The "Requirements for
601+ // Japanese Text Layout" W3C document says that Japanese kanji should be "set
602+ // solid" which implies that vertical (and horizontal) advance should be 1em.
603+ n_g.v_width=n_g.v_advance= 1.0;
604 }
605 if ( theFace->glyph->format == ft_glyph_format_outline ) {
606 FT_Outline_Funcs ft2_outline_funcs = {
607@@ -501,7 +523,7 @@
608 }
609 }
610
611-bool font_instance::FontMetrics(double &ascent,double &descent,double &leading)
612+bool font_instance::FontMetrics(double &ascent,double &descent,double &xheight)
613 {
614 if ( pFont == NULL ) {
615 return false;
616@@ -510,32 +532,17 @@
617 if ( theFace == NULL ) {
618 return false;
619 }
620-#ifdef USE_PANGO_WIN32
621- OUTLINETEXTMETRIC otm;
622- if ( !GetOutlineTextMetrics(parent->hScreenDC,sizeof(otm),&otm) ) {
623- return false;
624- }
625- double scale=1.0/parent->fontSize;
626- ascent=fabs(otm.otmAscent*scale);
627- descent=fabs(otm.otmDescent*scale);
628- leading=fabs(otm.otmLineGap*scale);
629- //otmSubscriptSize, otmSubscriptOffset, otmSuperscriptSize, otmSuperscriptOffset,
630-#else
631- if ( theFace->units_per_EM == 0 ) {
632- return false; // bitmap font
633- }
634- ascent=fabs(((double)theFace->ascender)/((double)theFace->units_per_EM));
635- descent=fabs(((double)theFace->descender)/((double)theFace->units_per_EM));
636- leading=fabs(((double)theFace->height)/((double)theFace->units_per_EM));
637- leading-=ascent+descent;
638-#endif
639+
640+ ascent = _ascent;
641+ descent = _descent;
642+ xheight = _xheight;
643+
644 return true;
645 }
646
647-bool font_instance::FontDecoration(
648- double &underline_position, double &underline_thickness,
649- double &linethrough_position, double &linethrough_thickness
650-){
651+bool font_instance::FontDecoration( double &underline_position, double &underline_thickness,
652+ double &linethrough_position, double &linethrough_thickness)
653+{
654 if ( pFont == NULL ) {
655 return false;
656 }
657@@ -662,6 +669,179 @@
658 return 0;
659 }
660
661+// Internal function to find baselines
662+void font_instance::FindFontMetrics() {
663+
664+ // CSS2 recommends using the OS/2 values sTypoAscender and sTypoDescender for the Typographic
665+ // ascender and descender values:
666+ // http://www.w3.org/TR/CSS2/visudet.html#sTypoAscender
667+ // On Windows, the typographic ascender and descender are taken from the otmMacAscent and
668+ // otmMacDescent values:
669+ // http://microsoft.public.win32.programmer.gdi.narkive.com/LV6k4BDh/msdn-documentation-outlinetextmetrics-clarification
670+ // The otmAscent and otmDescent values are the maxiumum ascent and maxiumum descent of all the
671+ // glyphs in a font.
672+ if ( theFace ) {
673+
674+#ifdef USE_PANGO_WIN32
675+
676+ if ( GetOutlineTextMetrics(parent->hScreenDC,sizeof(otm),&otm) ) {
677+ double scale=1.0/parent->fontSize;
678+ _ascent = fabs(otm.otmMacAscent * scale);
679+ _descent = fabs(otm.otmMacDescent * scale);
680+ _xheight = fabs(otm.otmXHeight * scale);
681+ _ascent_max = fabs(otm.otmAscent * scale);
682+ _descent_max = fabs(otm.otmDescent * scale);
683+
684+ // In CSS em size is ascent + descent... which should be 1. If not,
685+ // adjust so it is.
686+ double em = _ascent + _descent;
687+ if( em > 0 ) {
688+ _ascent /= em;
689+ _descent /= em;
690+ }
691+
692+ // May not be necessary but if OS/2 table missing or not version 2 or higher,
693+ // xheight might be zero.
694+ if( _xheight == 0.0 ) {
695+ _xheight = 0.5;
696+ }
697+
698+ // Baselines defined relative to alphabetic.
699+ _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] = -_descent; // Recommendation
700+ _baselines[ SP_CSS_BASELINE_HANGING ] = 0.8 * _ascent; // Guess
701+ _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = 0.8 * _xheight; // Guess
702+ _baselines[ SP_CSS_BASELINE_CENTRAL ] = 0.5 - _descent; // Definition
703+ _baselines[ SP_CSS_BASELINE_MIDDLE ] = 0.5 * _xheight; // Definition
704+ _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] = _ascent; // Definition
705+ _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] = -_descent; // Definition
706+
707+
708+ MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
709+ GLYPHMETRICS metrics;
710+ int retval;
711+
712+ // Better math baseline:
713+ // Try center of minus sign
714+ retval = GetGlyphOutline (parent->hScreenDC, 0x2212, GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
715+ // If no minus sign, try hyphen
716+ if( retval <= 0 )
717+ retval = GetGlyphOutline (parent->hScreenDC, '-', GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
718+
719+ if( retval > 0 ) {
720+ double math = (metrics.gmptGlyphOrigin.y + 0.5 * metrics.gmBlackBoxY) * scale;
721+ _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = math;
722+ }
723+
724+ // Find hanging baseline... assume it is at top of 'म'.
725+ retval = GetGlyphOutline (parent->hScreenDC, 0x092E, GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
726+ if( retval > 0 ) {
727+ double hanging = metrics.gmptGlyphOrigin.y * scale;
728+ _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = hanging;
729+ }
730+ }
731+
732+#else
733+
734+ if ( theFace->units_per_EM != 0 ) { // If zero then it's a bitmap font.
735+
736+ TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table( theFace, FT_SFNT_OS2 );
737+ if( os2 ) {
738+ _ascent = fabs(((double)os2->sTypoAscender) / ((double)theFace->units_per_EM));
739+ _descent = fabs(((double)os2->sTypoDescender)/ ((double)theFace->units_per_EM));
740+ } else {
741+ _ascent = fabs(((double)theFace->ascender) / ((double)theFace->units_per_EM));
742+ _descent = fabs(((double)theFace->descender) / ((double)theFace->units_per_EM));
743+ }
744+ _ascent_max = fabs(((double)theFace->ascender) / ((double)theFace->units_per_EM));
745+ _descent_max = fabs(((double)theFace->descender) / ((double)theFace->units_per_EM));
746+
747+ // In CSS em size is ascent + descent... which should be 1. If not,
748+ // adjust so it is.
749+ double em = _ascent + _descent;
750+ if( em > 0 ) {
751+ _ascent /= em;
752+ _descent /= em;
753+ }
754+
755+ // x-height
756+ if( os2 && os2->version >= 0x0002 && os2->version != 0xffffu ) {
757+ // Only os/2 version 2 and above have sxHeight, 0xffff marks "old Mac fonts" without table
758+ _xheight = fabs(((double)os2->sxHeight) / ((double)theFace->units_per_EM));
759+ } else {
760+ // Measure 'x' height in font. Recommended option by XSL standard if no sxHeight.
761+ FT_UInt index = FT_Get_Char_Index( theFace, 'x' );
762+ if( index != 0 ) {
763+ FT_Load_Glyph( theFace, index, FT_LOAD_NO_SCALE );
764+ _xheight = (fabs)(((double)theFace->glyph->metrics.height/(double)theFace->units_per_EM));
765+ } else {
766+ // No 'x' in font!
767+ _xheight = 0.5;
768+ }
769+ }
770+
771+ // Baselines defined relative to alphabetic.
772+ _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] = -_descent; // Recommendation
773+ _baselines[ SP_CSS_BASELINE_HANGING ] = 0.8 * _ascent; // Guess
774+ _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = 0.8 * _xheight; // Guess
775+ _baselines[ SP_CSS_BASELINE_CENTRAL ] = 0.5 - _descent; // Definition
776+ _baselines[ SP_CSS_BASELINE_MIDDLE ] = 0.5 * _xheight; // Definition
777+ _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] = _ascent; // Definition
778+ _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] = -_descent; // Definition
779+
780+ // Better math baseline:
781+ // Try center of minus sign
782+ FT_UInt index = FT_Get_Char_Index( theFace, 0x2212 ); //'−'
783+ // If no minus sign, try hyphen
784+ if( index == 0 )
785+ index = FT_Get_Char_Index( theFace, '-' );
786+
787+ if( index != 0 ) {
788+ FT_Load_Glyph( theFace, index, FT_LOAD_NO_SCALE );
789+ FT_Glyph aglyph;
790+ FT_Get_Glyph( theFace->glyph, &aglyph );
791+ FT_BBox acbox;
792+ FT_Glyph_Get_CBox( aglyph, FT_GLYPH_BBOX_UNSCALED, &acbox );
793+ double math = (acbox.yMin + acbox.yMax)/2.0/(double)theFace->units_per_EM;
794+ _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] = math;
795+ // std::cout << "Math baseline: - bbox: y_min: " << acbox.yMin
796+ // << " y_max: " << acbox.yMax
797+ // << " math: " << math << std::endl;
798+ }
799+
800+ // Find hanging baseline... assume it is at top of 'म'.
801+ index = FT_Get_Char_Index( theFace, 0x092E ); // 'म'
802+ if( index != 0 ) {
803+ FT_Load_Glyph( theFace, index, FT_LOAD_NO_SCALE );
804+ FT_Glyph aglyph;
805+ FT_Get_Glyph( theFace->glyph, &aglyph );
806+ FT_BBox acbox;
807+ FT_Glyph_Get_CBox( aglyph, FT_GLYPH_BBOX_UNSCALED, &acbox );
808+ double hanging = (double)acbox.yMax/(double)theFace->units_per_EM;
809+ _baselines[ SP_CSS_BASELINE_HANGING ] = hanging;
810+ // std::cout << "Hanging baseline: प: " << hanging << std::endl;
811+ }
812+ }
813+#endif
814+ // const gchar *family = pango_font_description_get_family(descr);
815+ // std::cout << "Font: " << (family?family:"null") << std::endl;
816+ // std::cout << " ascent: " << _ascent << std::endl;
817+ // std::cout << " descent: " << _descent << std::endl;
818+ // std::cout << " x-height: " << _xheight << std::endl;
819+ // std::cout << " max ascent: " << _ascent_max << std::endl;
820+ // std::cout << " max descent: " << _descent_max << std::endl;
821+ // std::cout << " Baselines:" << std::endl;
822+ // std::cout << " alphabetic: " << _baselines[ SP_CSS_BASELINE_ALPHABETIC ] << std::endl;
823+ // std::cout << " ideographic: " << _baselines[ SP_CSS_BASELINE_IDEOGRAPHIC ] << std::endl;
824+ // std::cout << " hanging: " << _baselines[ SP_CSS_BASELINE_HANGING ] << std::endl;
825+ // std::cout << " math: " << _baselines[ SP_CSS_BASELINE_MATHEMATICAL ] << std::endl;
826+ // std::cout << " central: " << _baselines[ SP_CSS_BASELINE_CENTRAL ] << std::endl;
827+ // std::cout << " middle: " << _baselines[ SP_CSS_BASELINE_MIDDLE ] << std::endl;
828+ // std::cout << " text_before: " << _baselines[ SP_CSS_BASELINE_TEXT_BEFORE_EDGE ] << std::endl;
829+ // std::cout << " text_after: " << _baselines[ SP_CSS_BASELINE_TEXT_AFTER_EDGE ] << std::endl;
830+ }
831+}
832+
833+
834 /*
835 Local Variables:
836 mode:c++
837
838=== modified file 'src/libnrtype/Layout-TNG-Compute.cpp'
839--- src/libnrtype/Layout-TNG-Compute.cpp 2015-10-12 11:48:06 +0000
840+++ src/libnrtype/Layout-TNG-Compute.cpp 2015-11-24 09:40:36 +0000
841@@ -23,33 +23,6 @@
842
843 #define TRACE(_args) IFTRACE(g_print _args)
844
845-// ******* enum conversion tables
846-
847-// These enums are probably from the SVG 1.0 era where one could interpret 'writing-mode' as setting direction.
848-// SVG 1.1 makes it clear that 'direction' should be used. 'direction' has only two values 'ltr' and 'rtl'.
849-// The first two values for the 'writing-mode' enum just happen to match the first two value of 'direction' so the
850-// existing code worked when 'writing-mode' was changed to 'direction'.
851-// static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_pango_direction[] = {
852-// {SP_CSS_WRITING_MODE_LR_TB, PANGO_DIRECTION_LTR},
853-// {SP_CSS_WRITING_MODE_RL_TB, PANGO_DIRECTION_RTL},
854-// {SP_CSS_WRITING_MODE_TB_LR, PANGO_DIRECTION_LTR}}; // this is correct
855-
856-// static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_my_direction[] = {
857-// {SP_CSS_WRITING_MODE_LR_TB, Layout::LEFT_TO_RIGHT},
858-// {SP_CSS_WRITING_MODE_RL_TB, Layout::RIGHT_TO_LEFT},
859-// {SP_CSS_WRITING_MODE_TB_LR, Layout::LEFT_TO_RIGHT}}; // this is correct
860-
861-// Proper 'direction' enums
862-static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_pango_direction[] = {
863- {SP_CSS_DIRECTION_LTR, PANGO_DIRECTION_LTR},
864- {SP_CSS_DIRECTION_RTL, PANGO_DIRECTION_RTL}};
865-
866-static Layout::EnumConversionItem const enum_convert_spstyle_direction_to_my_direction[] = {
867- {SP_CSS_DIRECTION_LTR, Layout::LEFT_TO_RIGHT},
868- {SP_CSS_DIRECTION_RTL, Layout::RIGHT_TO_LEFT}};
869-
870-
871-
872 /** \brief private to Layout. Does the real work of text flowing.
873
874 This class does a standard greedy paragraph wrapping algorithm.
875@@ -140,9 +113,10 @@
876 unsigned input_index; /// index into Layout::_input_stream
877 Glib::ustring::const_iterator input_stream_first_character;
878 double font_size;
879- LineHeight line_height; /// This is not the CSS line-height attribute!
880+ FontMetrics line_height; /// This is not the CSS line-height attribute!
881 double line_height_multiplier; /// calculated from the font-height css property
882 double baseline_shift; /// calculated from the baseline-shift css property
883+ SPCSSTextOrientation text_orientation;
884 unsigned text_bytes;
885 unsigned char_index_in_para; /// the index of the first character in this span in the paragraph, for looking up char_attributes
886 SVGLength x, y, dx, dy, rotate; // these are reoriented copies of the <tspan> attributes. We change span when we encounter one.
887@@ -220,9 +194,8 @@
888
889 void _buildPangoItemizationForPara(ParagraphInfo *para) const;
890
891- static void _computeFontLineHeight(font_instance *font, double font_size,
892- SPStyle const *style, LineHeight *line_height,
893- double *line_height_multiplier);
894+ // Returns line_height_multiplier
895+ static double _computeFontLineHeight( SPStyle const *style );
896
897 unsigned _buildSpansForPara(ParagraphInfo *para) const;
898
899@@ -270,7 +243,7 @@
900 bool _goToNextWrapShape();
901
902 bool _findChunksForLine(ParagraphInfo const &para, UnbrokenSpanPosition *start_span_pos,
903- std::vector<ChunkInfo> *chunk_info, LineHeight *line_height);
904+ std::vector<ChunkInfo> *chunk_info, FontMetrics *line_height);
905
906 static inline PangoLogAttr const &_charAttributes(ParagraphInfo const &para,
907 UnbrokenSpanPosition const &span_pos)
908@@ -282,7 +255,7 @@
909 UnbrokenSpanPosition const &start_span_pos,
910 ScanlineMaker::ScanRun const &scan_run,
911 std::vector<ChunkInfo> *chunk_info,
912- LineHeight *line_height) const;
913+ FontMetrics *line_height) const;
914
915 /** computes the width of a single UnbrokenSpan (pointed to by span->start.iter_span)
916 and outputs its vital statistics into the other fields of \a span.
917@@ -300,8 +273,11 @@
918 span->setZero();
919
920 if (span->start.iter_span->dx._set && span->start.char_byte == 0){
921- if(para.direction == RIGHT_TO_LEFT){ span->width -= span->start.iter_span->dx.computed; }
922- else { span->width += span->start.iter_span->dx.computed; }
923+ if(para.direction == RIGHT_TO_LEFT){
924+ span->width -= span->start.iter_span->dx.computed;
925+ } else {
926+ span->width += span->start.iter_span->dx.computed;
927+ }
928 }
929
930 if (span->start.iter_span->pango_item_index == -1) {
931@@ -382,10 +358,22 @@
932 double char_width = 0.0;
933 while (span->end_glyph_index < (unsigned)span->end.iter_span->glyph_string->num_glyphs
934 && span->end.iter_span->glyph_string->log_clusters[span->end_glyph_index] <= (int)span->end.char_byte) {
935- if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT)
936- char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true);
937- else
938+ if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) {
939+ // Vertical text
940+
941+ if( text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS ||
942+ (text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED &&
943+ para.pango_items[span->end.iter_span->pango_item_index].item->analysis.gravity == 0) ) {
944+ // Sideways orientation
945+ char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, false);
946+ } else {
947+ // Upright orientation
948+ char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true);
949+ }
950+ } else {
951+ // Horizontal text
952 char_width += font_size_multiplier * span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].geometry.width;
953+ }
954 span->end_glyph_index++;
955 }
956 if (char_attributes.is_cursor_position)
957@@ -474,7 +462,7 @@
958 are ready to output the final result to #_flow. This method takes its
959 input parameters and does that.
960 */
961- void _outputLine(ParagraphInfo const &para, LineHeight const &line_height, std::vector<ChunkInfo> const &chunk_info)
962+ void _outputLine(ParagraphInfo const &para, FontMetrics const &line_height, std::vector<ChunkInfo> const &chunk_info)
963 {
964 TRACE(("Start _outputLine\n"));
965 if (chunk_info.empty()) {
966@@ -486,7 +474,17 @@
967 TRACE(("found line fit; creating output\n"));
968 Layout::Line new_line;
969 new_line.in_paragraph = _flow._paragraphs.size() - 1;
970- new_line.baseline_y = _scanline_maker->yCoordinate() + line_height.ascent;
971+ new_line.baseline_y = _scanline_maker->yCoordinate();
972+ if( !_flow._input_wrap_shapes.empty() ) {
973+ // Flowed text
974+ if( _block_progression == RIGHT_TO_LEFT || _block_progression == LEFT_TO_RIGHT ) {
975+ // Vertical text, use em box center as baseline
976+ new_line.baseline_y += 0.5 * line_height.emSize();
977+ } else {
978+ new_line.baseline_y += line_height.getTypoAscent();
979+ }
980+ }
981+
982 new_line.in_shape = _current_shape_index;
983 _flow._lines.push_back(new_line);
984
985@@ -527,17 +525,16 @@
986 // If <tspan> "y" attribute is set, use it (initial "y" attributes in
987 // <tspans> other than the first have already been stripped for <tspans>
988 // marked with role="line", see sp-text.cpp: SPText::_buildLayoutInput).
989+ // NOTE: for vertical text, "y" is the user-space "x" value.
990 if( it_chunk->broken_spans.front().start.iter_span->y._set ) {
991
992 // Use set "y" attribute
993 new_line.baseline_y = it_chunk->broken_spans.front().start.iter_span->y.computed;
994-
995 // Save baseline
996 _flow._lines.back().baseline_y = new_line.baseline_y;
997
998- // Save new <tspan> y coordinate
999- _scanline_maker->setNewYCoordinate(new_line.baseline_y - line_height.ascent);
1000-
1001+ // Set the initial y coordinate of the next line.
1002+ _scanline_maker->setNewYCoordinate(new_line.baseline_y);
1003 }
1004
1005 // Reset relative y_offset ("dy" attribute is relative but should be reset at
1006@@ -556,21 +553,21 @@
1007 }
1008 _flow._chunks.push_back(new_chunk);
1009
1010- double x;
1011+ double current_x;
1012 double direction_sign;
1013 Direction previous_direction = para.direction;
1014 double counter_directional_width_remaining = 0.0;
1015 float glyph_rotate = 0.0;
1016 if (para.direction == LEFT_TO_RIGHT) {
1017 direction_sign = +1.0;
1018- x = 0.0;
1019+ current_x = 0.0;
1020 } else {
1021 direction_sign = -1.0;
1022 if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){
1023- x = it_chunk->scanrun_width;
1024+ current_x = it_chunk->scanrun_width;
1025 }
1026 else {
1027- x = it_chunk->text_width;
1028+ current_x = it_chunk->text_width;
1029 }
1030 }
1031
1032@@ -583,7 +580,7 @@
1033 if (it_span->start.char_byte == 0) {
1034 // Start of an unbroken span, we might have dx, dy or rotate still to process
1035 // (x and y are done per chunk)
1036- if (unbroken_span.dx._set) x += unbroken_span.dx.computed;
1037+ if (unbroken_span.dx._set) current_x += unbroken_span.dx.computed;
1038 if (unbroken_span.dy._set) _y_offset += unbroken_span.dy.computed;
1039 if (unbroken_span.rotate._set) glyph_rotate = unbroken_span.rotate.computed * (M_PI/180);
1040 }
1041@@ -601,6 +598,7 @@
1042 new_span.in_input_stream_item = unbroken_span.input_index;
1043 new_span.baseline_shift = 0.0;
1044 new_span.block_progression = _block_progression;
1045+ new_span.text_orientation = unbroken_span.text_orientation;
1046 if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font))
1047 {
1048 new_span.font->Ref();
1049@@ -609,12 +607,12 @@
1050 new_span.input_stream_first_character = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte);
1051 } else { // a control code
1052 new_span.font = NULL;
1053- new_span.font_size = new_span.line_height.ascent + new_span.line_height.descent;
1054+ new_span.font_size = new_span.line_height.emSize();
1055 new_span.direction = para.direction;
1056 }
1057
1058 if (new_span.direction == para.direction) {
1059- x -= counter_directional_width_remaining;
1060+ current_x -= counter_directional_width_remaining;
1061 counter_directional_width_remaining = 0.0;
1062 } else if (new_span.direction != previous_direction) {
1063 // measure width of spans we need to switch round
1064@@ -630,10 +628,10 @@
1065 }
1066 counter_directional_width_remaining += direction_sign * (it_following_span->width + it_following_span->whitespace_count * add_to_each_whitespace);
1067 }
1068- x += counter_directional_width_remaining;
1069+ current_x += counter_directional_width_remaining;
1070 counter_directional_width_remaining = 0.0; // we want to go increasingly negative
1071 }
1072- new_span.x_start = x;
1073+ new_span.x_start = current_x;
1074
1075 if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) {
1076 // the span is set up, push the glyphs and chars
1077@@ -673,10 +671,12 @@
1078 }
1079
1080 // create the Layout::Glyph
1081+ PangoGlyphInfo *unbroken_span_glyph_info = &unbroken_span.glyph_string->glyphs[glyph_index];
1082 Layout::Glyph new_glyph;
1083- new_glyph.glyph = unbroken_span.glyph_string->glyphs[glyph_index].glyph;
1084+ new_glyph.glyph = unbroken_span_glyph_info->glyph;
1085 new_glyph.in_character = _flow._characters.size();
1086 new_glyph.rotation = glyph_rotate;
1087+ new_glyph.orientation = ORIENTATION_UPRIGHT; // Only effects vertical text
1088
1089 // We may have scaled font size to fit textLength; now, if
1090 // @lengthAdjust=spacingAndGlyphs, this scaling must be only horizontal,
1091@@ -686,30 +686,68 @@
1092 else
1093 new_glyph.vertical_scale = 1.0;
1094
1095- /* put something like this back in when we do glyph-rotation-horizontal/vertical
1096- if (new_span.block_progression == LEFT_TO_RIGHT || new_span.block_progression == RIGHT_TO_LEFT) {
1097- new_glyph.x += new_span.line_height.ascent;
1098- new_glyph.y -= unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier * 0.5;
1099- new_glyph.width = new_span.line_height.ascent + new_span.line_height.descent;
1100- } else */
1101+ // Position glyph --------------------
1102+ new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier;
1103+ new_glyph.y =_y_offset;
1104+
1105+ // y-coordinate is flipped between vertical and horizontal text... delta_y is common offset but applied with opposite sign
1106+ double delta_y = unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier + unbroken_span.baseline_shift;
1107+ SPCSSBaseline dominant_baseline = _flow._blockBaseline();
1108
1109 if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) {
1110- new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier + new_span.line_height.ascent;
1111- new_glyph.y = _y_offset -
1112- unbroken_span.baseline_shift +
1113- (unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset -
1114- unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * 0.5) * font_size_multiplier;
1115- new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, true);
1116+ // Vertical text
1117+
1118+ // Default dominant baseline is determined by overall block (i.e. <text>) 'text-orientation' value.
1119+ if( _flow._blockTextOrientation() != SP_CSS_TEXT_ORIENTATION_SIDEWAYS ) {
1120+ if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_CENTRAL;
1121+ } else {
1122+ if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC;
1123+ }
1124+
1125+ new_glyph.y += delta_y;
1126+
1127+ // TODO: Should also check 'glyph_orientation_vertical' if 'text-orientation' is unset...
1128+ if( new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_SIDEWAYS ||
1129+ (new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_MIXED &&
1130+ para.pango_items[unbroken_span.pango_item_index].item->analysis.gravity == 0) ) {
1131+
1132+ // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage. zzzzzzz
1133+ new_glyph.orientation = ORIENTATION_SIDEWAYS;
1134+
1135+ new_glyph.y -= new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ];
1136+ new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false);
1137+
1138+ } else {
1139+ // Upright orientation
1140+
1141+ new_glyph.x += new_span.line_height.ascent;
1142+
1143+ // Glyph reference point is center (shift: left edge to center glyph)
1144+ new_glyph.y -= unbroken_span_glyph_info->geometry.width * 0.5 * font_size_multiplier;
1145+ new_glyph.y -= new_span.font_size * (para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ] -
1146+ para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ SP_CSS_BASELINE_CENTRAL ] );
1147+
1148+ new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, true);
1149+ if( new_glyph.width == 0 ) {
1150+ new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier;
1151+ }
1152+
1153+ }
1154 } else {
1155- new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier;
1156- new_glyph.y = _y_offset -
1157- unbroken_span.baseline_shift +
1158- unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier;
1159- new_glyph.width = unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier;
1160+ // Horizontal text
1161+
1162+ if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC;
1163+
1164+ new_glyph.y -= delta_y;
1165+ new_glyph.y += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ];
1166+
1167+ new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier;
1168 if ((new_glyph.width == 0) && (para.pango_items[unbroken_span.pango_item_index].font))
1169- new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, false);
1170+ new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false);
1171 // for some reason pango returns zero width for invalid glyph characters (those empty boxes), so go to freetype for the info
1172 }
1173+
1174+
1175 if (new_span.direction == RIGHT_TO_LEFT) {
1176 // pango wanted to give us glyphs in visual order but we refused, so we need to work
1177 // out where the cluster start is ourselves
1178@@ -718,13 +756,15 @@
1179 if (unbroken_span.glyph_string->glyphs[rtl_index].attr.is_cluster_start && rtl_index != glyph_index)
1180 break;
1181 if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT)
1182+ // Vertical text
1183 cluster_width += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[rtl_index].glyph, true);
1184 else
1185+ // Horizontal text
1186 cluster_width += font_size_multiplier * unbroken_span.glyph_string->glyphs[rtl_index].geometry.width;
1187 }
1188 new_glyph.x -= cluster_width;
1189 }
1190- _flow._glyphs.push_back(new_glyph);
1191+ _flow._glyphs.push_back(new_glyph);
1192
1193 // create the Layout::Character(s)
1194 double advance_width = new_glyph.width;
1195@@ -781,15 +821,15 @@
1196 advance_width *= direction_sign;
1197 if (new_span.direction != para.direction) {
1198 counter_directional_width_remaining -= advance_width;
1199- x -= advance_width;
1200+ current_x -= advance_width;
1201 x_in_span_last -= advance_width;
1202 } else {
1203- x += advance_width;
1204+ current_x += advance_width;
1205 x_in_span_last += advance_width;
1206 }
1207 }
1208 } else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) {
1209- x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width;
1210+ current_x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width;
1211 }
1212
1213 new_span.x_end = new_span.x_start + x_in_span_last;
1214@@ -978,6 +1018,7 @@
1215 #if PANGO_VERSION_CHECK(1,37,1)
1216 TRACE((" ... compiled for font features\n"));
1217 #endif
1218+
1219 Glib::ustring para_text;
1220 PangoAttrList *attributes_list;
1221 unsigned input_index;
1222@@ -999,10 +1040,10 @@
1223 } else if (_flow._input_stream[input_index]->Type() == TEXT_SOURCE) {
1224 Layout::InputStreamTextSource *text_source = static_cast<Layout::InputStreamTextSource *>(_flow._input_stream[input_index]);
1225
1226- // create the font_instance
1227- font_instance *font = text_source->styleGetFontInstance();
1228- if (font == NULL)
1229- continue; // bad news: we'll have to ignore all this text because we know of no font to render it
1230+ // create the font_instance
1231+ font_instance *font = text_source->styleGetFontInstance();
1232+ if (font == NULL)
1233+ continue; // bad news: we'll have to ignore all this text because we know of no font to render it
1234
1235 PangoAttribute *attribute_font_description = pango_attr_font_desc_new(font->descr);
1236 attribute_font_description->start_index = para_text.bytes();
1237@@ -1028,25 +1069,22 @@
1238
1239 TRACE(("whole para: \"%s\"\n", para_text.data()));
1240 TRACE(("%d input sources used\n", input_index - para->first_input_index));
1241-
1242 // do the pango_itemize()
1243 GList *pango_items_glist = NULL;
1244+ para->direction = LEFT_TO_RIGHT; // CSS default
1245 if (_flow._input_stream[para->first_input_index]->Type() == TEXT_SOURCE) {
1246 Layout::InputStreamTextSource const *text_source = static_cast<Layout::InputStreamTextSource *>(_flow._input_stream[para->first_input_index]);
1247- if (text_source->style->direction.set) {
1248- PangoDirection pango_direction = (PangoDirection)_enum_converter(text_source->style->direction.computed, enum_convert_spstyle_direction_to_pango_direction, sizeof(enum_convert_spstyle_direction_to_pango_direction)/sizeof(enum_convert_spstyle_direction_to_pango_direction[0]));
1249- pango_items_glist = pango_itemize_with_base_dir(_pango_context, pango_direction, para_text.data(), 0, para_text.bytes(), attributes_list, NULL);
1250- para->direction = (Layout::Direction)_enum_converter(text_source->style->direction.computed, enum_convert_spstyle_direction_to_my_direction, sizeof(enum_convert_spstyle_direction_to_my_direction)/sizeof(enum_convert_spstyle_direction_to_my_direction[0]));
1251- }
1252+
1253+ para->direction = (text_source->style->direction.computed == SP_CSS_DIRECTION_LTR) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT;
1254+ PangoDirection pango_direction = (text_source->style->direction.computed == SP_CSS_DIRECTION_LTR) ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
1255+ pango_items_glist = pango_itemize_with_base_dir(_pango_context, pango_direction, para_text.data(), 0, para_text.bytes(), attributes_list, NULL);
1256 }
1257- if (pango_items_glist == NULL) { // no direction specified, guess it
1258+
1259+ if( pango_items_glist == NULL ) {
1260+ // Type wasn't TEXT_SOURCE or direction was not set.
1261 pango_items_glist = pango_itemize(_pango_context, para_text.data(), 0, para_text.bytes(), attributes_list, NULL);
1262+ }
1263
1264- // I think according to the css spec this is wrong and we're never allowed to guess the directionality
1265- // of a paragraph. Need to talk to an rtl speaker.
1266- if (pango_items_glist == NULL || pango_items_glist->data == NULL) para->direction = LEFT_TO_RIGHT;
1267- else para->direction = (((PangoItem*)pango_items_glist->data)->analysis.level & 1) ? RIGHT_TO_LEFT : LEFT_TO_RIGHT;
1268- }
1269 pango_attr_list_unref(attributes_list);
1270
1271 // convert the GList to our vector<> and make the font_instance for each PangoItem at the same time
1272@@ -1070,44 +1108,32 @@
1273 }
1274
1275 /**
1276- * Gets the ascent, descent and leading for a font and the alteration that has to be performed
1277- * according to the value specified by the line-height css property. The result of multiplying
1278- * \a line_height by \a line_height_multiplier is the inline box height as specified in css2
1279- * section 10.8.
1280+ * Finds the value of line_height_multiplier given the 'line-height' property. The result of
1281+ * multiplying \a l by \a line_height_multiplier is the inline box height as specified in css2
1282+ * section 10.8. http://www.w3.org/TR/CSS2/visudet.html#line-height
1283+ *
1284+ * The 'computed' value of 'line-height' does not have a consistent meaning. We need to find the
1285+ * 'used' value and divide that by the font size.
1286 */
1287-void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font_size,
1288- SPStyle const *style, LineHeight *line_height,
1289- double *line_height_multiplier)
1290+double Layout::Calculator::_computeFontLineHeight( SPStyle const *style )
1291 {
1292- if (font == NULL) {
1293- line_height->setZero();
1294- *line_height_multiplier = 1.0;
1295- }
1296- else {
1297- font->FontMetrics(line_height->ascent, line_height->descent, line_height->leading);
1298- }
1299- *line_height *= font_size;
1300-
1301 // yet another borked SPStyle member that we're going to have to fix ourselves
1302+ // We shouldn't need to climb the element tree...
1303 for ( ; ; ) {
1304 if (style->line_height.set && !style->line_height.inherit) {
1305 if (style->line_height.normal)
1306 break;
1307 switch (style->line_height.unit) {
1308 case SP_CSS_UNIT_NONE:
1309- *line_height_multiplier = style->line_height.computed * font_size / line_height->total();
1310- return;
1311+ return style->line_height.computed;
1312 case SP_CSS_UNIT_EX:
1313- *line_height_multiplier = style->line_height.value * 0.5 * font_size / line_height->total();
1314+ return style->line_height.value * 0.5;
1315 // 0.5 is an approximation of the x-height. Fixme.
1316- return;
1317 case SP_CSS_UNIT_EM:
1318 case SP_CSS_UNIT_PERCENT:
1319- *line_height_multiplier = style->line_height.value * font_size / line_height->total();
1320- return;
1321+ return style->line_height.value;
1322 default: // absolute values
1323- *line_height_multiplier = style->line_height.computed / line_height->total();
1324- return;
1325+ return style->line_height.computed / style->font_size.computed;
1326 }
1327 break;
1328 }
1329@@ -1115,7 +1141,7 @@
1330 style = style->object->parent->style;
1331 if (style == NULL) break;
1332 }
1333- *line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total();
1334+ return (LINE_HEIGHT_NORMAL);
1335 }
1336
1337 bool compareGlyphWidth(const PangoGlyphInfo &a, const PangoGlyphInfo &b)
1338@@ -1155,7 +1181,6 @@
1339 new_span.input_index = input_index;
1340 new_span.line_height.ascent = control_code->ascent * _flow.getTextLengthMultiplierDue();
1341 new_span.line_height.descent = control_code->descent * _flow.getTextLengthMultiplierDue();
1342- new_span.line_height.leading = 0.0;
1343 new_span.text_bytes = 0;
1344 new_span.char_index_in_para = char_index_in_para;
1345 para->unbroken_spans.push_back(new_span);
1346@@ -1194,11 +1219,13 @@
1347 new_span.dy._set = false;
1348 new_span.rotate._set = false;
1349 if (_block_progression == TOP_TO_BOTTOM || _block_progression == BOTTOM_TO_TOP) {
1350+ // Horizontal text
1351 if (text_source->x.size() > char_index_in_source) new_span.x = text_source->x[char_index_in_source];
1352 if (text_source->y.size() > char_index_in_source) new_span.y = text_source->y[char_index_in_source];
1353 if (text_source->dx.size() > char_index_in_source) new_span.dx = text_source->dx[char_index_in_source].computed * _flow.getTextLengthMultiplierDue();
1354 if (text_source->dy.size() > char_index_in_source) new_span.dy = text_source->dy[char_index_in_source].computed * _flow.getTextLengthMultiplierDue();
1355 } else {
1356+ // Vertical text
1357 if (text_source->x.size() > char_index_in_source) new_span.y = text_source->x[char_index_in_source];
1358 if (text_source->y.size() > char_index_in_source) new_span.x = text_source->y[char_index_in_source];
1359 if (text_source->dx.size() > char_index_in_source) new_span.dy = text_source->dx[char_index_in_source].computed * _flow.getTextLengthMultiplierDue();
1360@@ -1231,7 +1258,7 @@
1361 }
1362
1363 // now we know the length, do some final calculations and add the UnbrokenSpan to the list
1364- new_span.font_size = text_source->styleComputeFontSize() * _flow.getTextLengthMultiplierDue();
1365+ new_span.font_size = text_source->style->font_size.computed * _flow.getTextLengthMultiplierDue();
1366 if (new_span.text_bytes) {
1367 new_span.glyph_string = pango_glyph_string_new();
1368 /* Some assertions intended to help diagnose bug #1277746. */
1369@@ -1240,6 +1267,7 @@
1370 g_assert( span_start_byte_in_source + new_span.text_bytes <= text_source->text->bytes() );
1371 g_assert( memchr(text_source->text->data() + span_start_byte_in_source, '\0', static_cast<size_t>(new_span.text_bytes))
1372 == NULL );
1373+
1374 /* Notes as of 4/29/13. Pango_shape is not generating English language ligatures, but it is generating
1375 them for Hebrew (and probably other similar languages). In the case observed 3 unicode characters (a base
1376 and 2 Mark, nonspacings) are merged into two glyphs (the base + first Mn, the 2nd Mn). All of these map
1377@@ -1247,6 +1275,8 @@
1378 characters and glyphs. A big chunk of the conditional code which immediately follows this call
1379 is there to clean up the resulting mess.
1380 */
1381+
1382+ // Convert characters to glyphs
1383 pango_shape(text_source->text->data() + span_start_byte_in_source,
1384 new_span.text_bytes,
1385 &para->pango_items[pango_item_index].item->analysis,
1386@@ -1324,11 +1354,14 @@
1387 /* glyphs[].x_offset values may be out of order within any log_clusters, apparently harmless */
1388 }
1389 new_span.pango_item_index = pango_item_index;
1390- _computeFontLineHeight(para->pango_items[pango_item_index].font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier);
1391+ new_span.line_height_multiplier = _computeFontLineHeight( text_source->style );
1392+ new_span.line_height.set( para->pango_items[pango_item_index].font );
1393+ new_span.line_height *= new_span.font_size;
1394
1395 // At some point we may want to calculate baseline_shift here (to take advantage
1396 // of otm features like superscript baseline), but for now we use style baseline_shift.
1397 new_span.baseline_shift = text_source->style->baseline_shift.computed;
1398+ new_span.text_orientation = (SPCSSTextOrientation)text_source->style->text_orientation.computed;
1399
1400 // TODO: metrics for vertical text
1401 TRACE(("add text span %lu \"%s\"\n", para->unbroken_spans.size(), text_source->text->raw().substr(span_start_byte_in_source, new_span.text_bytes).c_str()));
1402@@ -1338,11 +1371,13 @@
1403 new_span.pango_item_index = -1;
1404 font_instance *font = text_source->styleGetFontInstance();
1405 if (font) {
1406- _computeFontLineHeight(font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier);
1407+ new_span.line_height_multiplier = _computeFontLineHeight( text_source->style );
1408+ new_span.line_height.set( font );
1409+ new_span.line_height *= new_span.font_size;
1410 font->Unref();
1411 } else {
1412- new_span.line_height.setZero();
1413- new_span.line_height_multiplier = 1.0;
1414+ new_span.line_height *= 0.0; // Set all to zero
1415+ new_span.line_height_multiplier = LINE_HEIGHT_NORMAL;
1416 }
1417 TRACE(("add style init span %lu\n", para->unbroken_spans.size()));
1418 }
1419@@ -1399,7 +1434,7 @@
1420 bool Layout::Calculator::_findChunksForLine(ParagraphInfo const &para,
1421 UnbrokenSpanPosition *start_span_pos,
1422 std::vector<ChunkInfo> *chunk_info,
1423- LineHeight *line_height)
1424+ FontMetrics *line_height)
1425 {
1426 // init the initial line_height
1427 if (start_span_pos->iter_span == para.unbroken_spans.end()) {
1428@@ -1408,9 +1443,9 @@
1429 InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream.front());
1430 font_instance *font = text_source->styleGetFontInstance();
1431 if (font) {
1432- double font_size = text_source->styleComputeFontSize();
1433- double multiplier;
1434- _computeFontLineHeight(font, font_size, text_source->style, line_height, &multiplier);
1435+ double multiplier = _computeFontLineHeight(text_source->style);
1436+ line_height->set( font );
1437+ *line_height *= text_source->style->font_size.computed;
1438 font->Unref();
1439 *line_height *= multiplier;
1440 _scanline_maker->setNewYCoordinate(_scanline_maker->yCoordinate() - line_height->ascent);
1441@@ -1422,7 +1457,6 @@
1442 // if we're not wrapping set the line_height big and negative so we can use negative line height
1443 line_height->ascent = -1.0e10;
1444 line_height->descent = -1.0e10;
1445- line_height->leading = -1.0e10;
1446 }
1447 else
1448 line_height->setZero();
1449@@ -1474,7 +1508,7 @@
1450 UnbrokenSpanPosition const &start_span_pos,
1451 ScanlineMaker::ScanRun const &scan_run,
1452 std::vector<ChunkInfo> *chunk_info,
1453- LineHeight *line_height) const
1454+ FontMetrics *line_height) const
1455 {
1456 ChunkInfo new_chunk;
1457 new_chunk.text_width = 0.0;
1458@@ -1511,16 +1545,17 @@
1459 }
1460
1461 // see if this span is too tall to fit on the current line
1462- LineHeight total_height = new_span.start.iter_span->line_height;
1463- total_height *= new_span.start.iter_span->line_height_multiplier;
1464+ FontMetrics new_span_height = new_span.start.iter_span->line_height;
1465+ new_span_height.computeEffective( new_span.start.iter_span->line_height_multiplier );
1466 /* floating point 80-bit/64-bit rounding problems require epsilon. See
1467 discussion http://inkscape.gristle.org/2005-03-16.txt around 22:00 */
1468- if ( total_height.ascent > line_height->ascent + FLT_EPSILON
1469- || total_height.descent > line_height->descent + FLT_EPSILON
1470- || total_height.leading > line_height->leading + FLT_EPSILON) {
1471- line_height->max(total_height);
1472- if (!_scanline_maker->canExtendCurrentScanline(*line_height))
1473+ if ( new_span_height.ascent > line_height->ascent + FLT_EPSILON ||
1474+ new_span_height.descent > line_height->descent + FLT_EPSILON) {
1475+ // Take larger of each of the two ascents and two descents per CSS
1476+ line_height->max(new_span_height);
1477+ if (!_scanline_maker->canExtendCurrentScanline(*line_height)) {
1478 return false;
1479+ }
1480 }
1481
1482 bool span_fitted = _measureUnbrokenSpan(para, &new_span, &last_span_at_break, &last_span_at_emergency_break, new_chunk.scanrun_width - new_chunk.text_width);
1483@@ -1540,7 +1575,7 @@
1484 TRACE(("chunk complete, used %f width (%d whitespaces, %lu brokenspans)\n", new_chunk.text_width, new_chunk.whitespace_count, new_chunk.broken_spans.size()));
1485 chunk_info->push_back(new_chunk);
1486
1487- if (scan_run.width() >= 4.0 * line_height->total() && last_span_at_break.end == start_span_pos) {
1488+ if (scan_run.width() >= 4.0 * line_height->emSize() && last_span_at_break.end == start_span_pos) {
1489 /* **non-SVG spec bit**: See bug #1191102
1490 If the user types a very long line with no spaces, the way the spec
1491 is written at the moment means that when the length of the text
1492@@ -1619,14 +1654,27 @@
1493 _flow._clearOutputObjects();
1494
1495 _pango_context = (font_factory::Default())->fontContext;
1496+
1497 _font_factory_size_multiplier = (font_factory::Default())->fontSize;
1498
1499+ // Reset gravity hint in case it was changed via previous use of 'text-orientation'
1500+ // (scripts take their natural gravity given base gravity).
1501+ pango_context_set_gravity_hint(_pango_context, PANGO_GRAVITY_HINT_NATURAL);
1502+
1503 _block_progression = _flow._blockProgression();
1504+ if( _block_progression == RIGHT_TO_LEFT || _block_progression == LEFT_TO_RIGHT ) {
1505+ // Vertical text, CJK
1506+ pango_context_set_base_gravity(_pango_context, PANGO_GRAVITY_EAST);
1507+ } else {
1508+ // Horizontal text
1509+ pango_context_set_base_gravity(_pango_context, PANGO_GRAVITY_AUTO);
1510+ }
1511+
1512 _y_offset = 0.0;
1513 _createFirstScanlineMaker();
1514
1515 ParagraphInfo para;
1516- LineHeight line_height; // needs to be maintained across paragraphs to be able to deal with blank paras
1517+ FontMetrics line_height; // needs to be maintained across paragraphs to be able to deal with blank paras
1518 for(para.first_input_index = 0 ; para.first_input_index < _flow._input_stream.size() ; ) {
1519 // jump to the next wrap shape if this is a SHAPE_BREAK control code
1520 if (_flow._input_stream[para.first_input_index]->Type() == CONTROL_CODE) {
1521@@ -1640,7 +1688,10 @@
1522 if (_scanline_maker == NULL)
1523 break; // we're trying to flow past the last wrap shape
1524
1525- _buildPangoItemizationForPara(&para);
1526+ // Break things up into little pango units with unique direction, gravity, etc.
1527+ _buildPangoItemizationForPara(&para);
1528+
1529+ // Do shaping (convert characters to glyphs)
1530 unsigned para_end_input_index = _buildSpansForPara(&para);
1531
1532 if (_flow._input_stream[para.first_input_index]->Type() == TEXT_SOURCE)
1533@@ -1679,7 +1730,7 @@
1534 Layout::Span new_span;
1535 if (_flow._spans.empty()) {
1536 new_span.font = NULL;
1537- new_span.font_size = line_height.ascent + line_height.descent;
1538+ new_span.font_size = line_height.emSize();
1539 new_span.line_height = line_height;
1540 new_span.x_end = 0.0;
1541 } else {
1542@@ -1755,19 +1806,16 @@
1543 InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream.front());
1544
1545 font_instance *font = text_source->styleGetFontInstance();
1546- double font_size = text_source->styleComputeFontSize();
1547+ double font_size = text_source->style->font_size.computed;
1548 double caret_slope_run = 0.0, caret_slope_rise = 1.0;
1549- LineHeight line_height;
1550+ FontMetrics line_height;
1551 if (font) {
1552 const_cast<font_instance*>(font)->FontSlope(caret_slope_run, caret_slope_rise);
1553- font->FontMetrics(line_height.ascent, line_height.descent, line_height.leading);
1554+ font->FontMetrics(line_height.ascent, line_height.descent, line_height.xheight);
1555 line_height *= font_size;
1556 font->Unref();
1557- } else {
1558- line_height.ascent = font_size * 0.85; // random guesses
1559- line_height.descent = font_size * 0.15;
1560- line_height.leading = 0.0;
1561 }
1562+
1563 double caret_slope = atan2(caret_slope_run, caret_slope_rise);
1564 _empty_cursor_shape.height = font_size / cos(caret_slope);
1565 _empty_cursor_shape.rotation = caret_slope;
1566@@ -1780,10 +1828,13 @@
1567 ShapeScanlineMaker scanline_maker(_input_wrap_shapes.front().shape, block_progression);
1568 std::vector<ScanlineMaker::ScanRun> scan_runs = scanline_maker.makeScanline(line_height);
1569 if (!scan_runs.empty()) {
1570- if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT)
1571+ if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) {
1572+ // Vertical text
1573 _empty_cursor_shape.position = Geom::Point(scan_runs.front().y + font_size, scan_runs.front().x_start);
1574- else
1575+ } else {
1576+ // Horizontal text
1577 _empty_cursor_shape.position = Geom::Point(scan_runs.front().x_start, scan_runs.front().y + font_size);
1578+ }
1579 }
1580 }
1581 }
1582
1583=== modified file 'src/libnrtype/Layout-TNG-Input.cpp'
1584--- src/libnrtype/Layout-TNG-Input.cpp 2015-10-15 12:14:01 +0000
1585+++ src/libnrtype/Layout-TNG-Input.cpp 2015-11-24 09:40:36 +0000
1586@@ -126,52 +126,6 @@
1587 return conversion_table[0].output;
1588 }
1589
1590-// ***** the style format interface
1591-// this doesn't include all accesses to SPStyle, only the ones that are non-trivial
1592-
1593-static const float medium_font_size = 12.0; // more of a default if all else fails than anything else
1594-float Layout::InputStreamTextSource::styleComputeFontSize() const
1595-{
1596- return style->font_size.computed;
1597-
1598- // in case the computed value's not good enough, here's some manual code held in reserve:
1599- SPStyle const *this_style = style;
1600- float inherit_multiplier = 1.0;
1601-
1602- for ( ; ; ) {
1603- if (this_style->font_size.set && !this_style->font_size.inherit) {
1604- switch (this_style->font_size.type) {
1605- case SP_FONT_SIZE_LITERAL: {
1606- switch(this_style->font_size.literal) { // these multipliers are straight out of the CSS spec
1607- case SP_CSS_FONT_SIZE_XX_SMALL: return medium_font_size * inherit_multiplier * (3.0/5.0);
1608- case SP_CSS_FONT_SIZE_X_SMALL: return medium_font_size * inherit_multiplier * (3.0/4.0);
1609- case SP_CSS_FONT_SIZE_SMALL: return medium_font_size * inherit_multiplier * (8.0/9.0);
1610- default:
1611- case SP_CSS_FONT_SIZE_MEDIUM: return medium_font_size * inherit_multiplier;
1612- case SP_CSS_FONT_SIZE_LARGE: return medium_font_size * inherit_multiplier * (6.0/5.0);
1613- case SP_CSS_FONT_SIZE_X_LARGE: return medium_font_size * inherit_multiplier * (3.0/2.0);
1614- case SP_CSS_FONT_SIZE_XX_LARGE: return medium_font_size * inherit_multiplier * 2.0;
1615- case SP_CSS_FONT_SIZE_SMALLER: inherit_multiplier *= 0.84; break; //not exactly according to spec
1616- case SP_CSS_FONT_SIZE_LARGER: inherit_multiplier *= 1.26; break; //not exactly according to spec
1617- }
1618- break;
1619- }
1620- case SP_FONT_SIZE_PERCENTAGE: { // 'em' units should be in here, but aren't. Fix in style.cpp.
1621- inherit_multiplier *= this_style->font_size.value;
1622- break;
1623- }
1624- case SP_FONT_SIZE_LENGTH: {
1625- return this_style->font_size.value * inherit_multiplier;
1626- }
1627- }
1628- }
1629- if (this_style->object == NULL || this_style->object->parent == NULL) break;
1630- this_style = this_style->object->parent->style;
1631- if (this_style == NULL) break;
1632- }
1633- return medium_font_size * inherit_multiplier;
1634-}
1635-
1636 Layout::Direction Layout::InputStreamTextSource::styleGetBlockProgression() const
1637 {
1638 switch( style->writing_mode.computed ) {
1639@@ -192,6 +146,16 @@
1640 return TOP_TO_BOTTOM;
1641 }
1642
1643+SPCSSTextOrientation Layout::InputStreamTextSource::styleGetTextOrientation() const
1644+{
1645+ return ((SPCSSTextOrientation)style->text_orientation.computed);
1646+}
1647+
1648+SPCSSBaseline Layout::InputStreamTextSource::styleGetDominantBaseline() const
1649+{
1650+ return ((SPCSSBaseline)style->dominant_baseline.computed);
1651+}
1652+
1653 static Layout::Alignment text_anchor_to_alignment(unsigned anchor, Layout::Direction para_direction)
1654 {
1655 switch (anchor) {
1656
1657=== modified file 'src/libnrtype/Layout-TNG-OutIter.cpp'
1658--- src/libnrtype/Layout-TNG-OutIter.cpp 2015-08-17 01:16:35 +0000
1659+++ src/libnrtype/Layout-TNG-OutIter.cpp 2015-11-24 09:40:36 +0000
1660@@ -120,7 +120,8 @@
1661 double best_y_range = DBL_MAX;
1662 double best_x_range = DBL_MAX;
1663 for (chunk_index = 0 ; chunk_index < _chunks.size() ; chunk_index++) {
1664- LineHeight line_height = {0.0, 0.0, 0.0};
1665+ FontMetrics line_height;
1666+ line_height *= 0.0; // Set all metrics to zero.
1667 double chunk_width = 0.0;
1668 for ( ; span_index < _spans.size() && _spans[span_index].in_chunk == chunk_index ; span_index++) {
1669 line_height.max(_spans[span_index].line_height);
1670@@ -340,8 +341,8 @@
1671
1672 double baseline_y = _characters[char_index].line(this).baseline_y + _characters[char_index].span(this).baseline_shift;
1673 if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
1674- double span_height = _spans[_characters[char_index].in_span].line_height.ascent + _spans[_characters[char_index].in_span].line_height.descent;
1675- top_left[Geom::Y] = top_left[Geom::X];
1676+ double span_height = _spans[_characters[char_index].in_span].line_height.emSize();
1677+ top_left[Geom::Y] = top_left[Geom::X];
1678 top_left[Geom::X] = baseline_y - span_height * 0.5;
1679 bottom_right[Geom::Y] = bottom_right[Geom::X];
1680 bottom_right[Geom::X] = baseline_y + span_height * 0.5;
1681@@ -404,7 +405,7 @@
1682 double vertical_scale = _glyphs.back().vertical_scale;
1683
1684 if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
1685- double span_height = vertical_scale * (_spans[span_index].line_height.ascent + _spans[span_index].line_height.descent);
1686+ double span_height = vertical_scale * _spans[span_index].line_height.emSize();
1687 top_left[Geom::Y] = top_left[Geom::X];
1688 top_left[Geom::X] = baseline_y - span_height * 0.5;
1689 bottom_right[Geom::Y] = bottom_right[Geom::X];
1690@@ -510,17 +511,19 @@
1691 double vertical_scale = _glyphs.empty() ? 1.0 : _glyphs.back().vertical_scale;
1692
1693 if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
1694- height = vertical_scale * span->line_height.ascent + span->line_height.descent;
1695+ // Vertical text
1696+ height = vertical_scale * span->line_height.emSize();
1697 rotation += M_PI / 2;
1698 std::swap(position[Geom::X], position[Geom::Y]);
1699 position[Geom::X] -= vertical_scale * sin(rotation) * height * 0.5;
1700 position[Geom::Y] += vertical_scale * cos(rotation) * height * 0.5;
1701 } else {
1702+ // Horizontal text
1703 double caret_slope_run = 0.0, caret_slope_rise = 1.0;
1704 if (span->font)
1705 const_cast<font_instance*>(span->font)->FontSlope(caret_slope_run, caret_slope_rise);
1706 double caret_slope = atan2(caret_slope_run, caret_slope_rise);
1707- height = vertical_scale * (span->line_height.ascent + span->line_height.descent) / cos(caret_slope);
1708+ height = vertical_scale * (span->line_height.emSize()) / cos(caret_slope);
1709 rotation += caret_slope;
1710 position[Geom::X] -= sin(rotation) * vertical_scale * span->line_height.descent;
1711 position[Geom::Y] += cos(rotation) * vertical_scale * span->line_height.descent;
1712
1713=== modified file 'src/libnrtype/Layout-TNG-Output.cpp'
1714--- src/libnrtype/Layout-TNG-Output.cpp 2015-01-05 10:17:51 +0000
1715+++ src/libnrtype/Layout-TNG-Output.cpp 2015-11-24 09:40:36 +0000
1716@@ -93,26 +93,55 @@
1717 _path_fitted = NULL;
1718 }
1719
1720-void Layout::LineHeight::max(LineHeight const &other)
1721-{
1722- if (other.ascent > ascent) ascent = other.ascent;
1723- if (other.descent > descent) descent = other.descent;
1724- if (other.leading > leading) leading = other.leading;
1725+void Layout::FontMetrics::set(font_instance *font)
1726+{
1727+ if( font != NULL ) {
1728+ ascent = font->GetTypoAscent();
1729+ descent = font->GetTypoDescent();
1730+ xheight = font->GetXHeight();
1731+ ascent_max = font->GetMaxAscent();
1732+ descent_max = font->GetMaxDescent();
1733+ }
1734+}
1735+
1736+void Layout::FontMetrics::max(FontMetrics const &other)
1737+{
1738+ if (other.ascent > ascent ) ascent = other.ascent;
1739+ if (other.descent > descent ) descent = other.descent;
1740+ if( other.xheight > xheight ) xheight = other.xheight;
1741+ if( other.ascent_max > ascent_max ) ascent_max = other.ascent_max;
1742+ if( other.descent_max > descent_max ) descent_max = other.descent_max;
1743+}
1744+
1745+void Layout::FontMetrics::computeEffective( const double &line_height_multiplier ) {
1746+ double half_leading = 0.5 * (line_height_multiplier - 1.0) * emSize();
1747+ ascent += half_leading;
1748+ descent += half_leading;
1749 }
1750
1751 void Layout::_getGlyphTransformMatrix(int glyph_index, Geom::Affine *matrix) const
1752 {
1753 Span const &span = _glyphs[glyph_index].span(this);
1754- double sin_rotation = sin(_glyphs[glyph_index].rotation);
1755- double cos_rotation = cos(_glyphs[glyph_index].rotation);
1756+ double rotation = _glyphs[glyph_index].rotation;
1757+ if ( (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) &&
1758+ _glyphs[glyph_index].orientation == ORIENTATION_SIDEWAYS ) {
1759+ // Vertical sideways text
1760+ rotation += M_PI/2.0;
1761+ }
1762+ double sin_rotation = sin(rotation);
1763+ double cos_rotation = cos(rotation);
1764 (*matrix)[0] = span.font_size * cos_rotation;
1765 (*matrix)[1] = span.font_size * sin_rotation;
1766 (*matrix)[2] = span.font_size * sin_rotation;
1767 (*matrix)[3] = -span.font_size * cos_rotation * (_glyphs[glyph_index].vertical_scale); // unscale vertically so the specified text height is preserved if lengthAdjust=spacingAndGlyphs
1768 if (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) {
1769+ // Vertical text
1770+ // This effectively swaps x for y which changes handedness of coordinate system. This is a bit strange
1771+ // and not what one would expect but the compute code already reverses y so OK.
1772 (*matrix)[4] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
1773 (*matrix)[5] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
1774 } else {
1775+ // Horizontal text
1776 (*matrix)[4] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
1777 (*matrix)[5] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
1778 }
1779@@ -127,9 +156,9 @@
1780 InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]);
1781
1782 text_source->style->text_decoration_data.tspan_width = _spans[span_index].width();
1783- text_source->style->text_decoration_data.ascender = _spans[span_index].line_height.getAscent();
1784- text_source->style->text_decoration_data.descender = _spans[span_index].line_height.getDescent();
1785- text_source->style->text_decoration_data.line_gap = _spans[span_index].line_height.getLeading();
1786+ text_source->style->text_decoration_data.ascender = _spans[span_index].line_height.getTypoAscent();
1787+ text_source->style->text_decoration_data.descender = _spans[span_index].line_height.getTypoDescent();
1788+
1789 if(!span_index ||
1790 (_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index-1].in_chunk].in_line)){
1791 text_source->style->text_decoration_data.tspan_line_start = true;
1792@@ -170,11 +199,14 @@
1793 first_line_glyph = false;
1794 phase0 = glyph_matrix.translation()[Geom::X];
1795 }
1796- // save the starting coordinates for the line - these are needed for figuring out dot/dash/wave phase
1797+ // Save the starting coordinates for the line - these are needed for figuring out
1798+ // dot/dash/wave phase.
1799+ // Use maximum ascent and descent to ensure glpyhs that extend outside the embox
1800+ // are fully drawn.
1801 (void) nr_text->addComponent(_spans[span_index].font, _glyphs[glyph_index].glyph, glyph_matrix,
1802 _glyphs[glyph_index].width,
1803- _spans[span_index].line_height.getAscent(),
1804- _spans[span_index].line_height.getDescent(),
1805+ _spans[span_index].line_height.getMaxAscent(),
1806+ _spans[span_index].line_height.getMaxDescent(),
1807 glyph_matrix.translation()[Geom::X] - phase0
1808 );
1809 }
1810@@ -580,7 +612,7 @@
1811 result += Glib::ustring::compose(" font '%1' %2 %3 %4\n", sp_font_description_get_family(_spans[span_index].font->descr), _spans[span_index].font_size, style_to_text(pango_font_description_get_style(_spans[span_index].font->descr)), weight_to_text(pango_font_description_get_weight(_spans[span_index].font->descr)));
1812 }
1813 result += Glib::ustring::compose(" x_start = %1, x_end = %2\n", _spans[span_index].x_start, _spans[span_index].x_end)
1814- + Glib::ustring::compose(" line height: ascent %1, descent %2 leading %3\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent, _spans[span_index].line_height.leading)
1815+ + Glib::ustring::compose(" line height: ascent %1, descent %2\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent)
1816 + Glib::ustring::compose(" direction %1, block-progression %2\n", direction_to_text(_spans[span_index].direction), direction_to_text(_spans[span_index].block_progression))
1817 + " ** characters:\n";
1818 Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character;
1819
1820=== modified file 'src/libnrtype/Layout-TNG-Scanline-Maker.h'
1821--- src/libnrtype/Layout-TNG-Scanline-Maker.h 2010-11-17 02:12:56 +0000
1822+++ src/libnrtype/Layout-TNG-Scanline-Maker.h 2015-11-24 09:40:36 +0000
1823@@ -47,7 +47,7 @@
1824 between calls if the new height too big to fit in the space remaining in
1825 this shape. Returns an empty vector if there is no space left in the
1826 current shape. */
1827- virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height) =0;
1828+ virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height) =0;
1829
1830 /** Indicates that the caller has successfully filled the current line
1831 and hence that the next call to makeScanline() should return lines on
1832@@ -71,7 +71,7 @@
1833 The metrics given here are considered to be the ones that are being
1834 used now, and hence is the line advance height used by completeLine().
1835 */
1836- virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height) =0;
1837+ virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height) =0;
1838 };
1839
1840 /** \brief private to Layout. Generates infinite scanlines for when you don't want wrapping
1841@@ -90,7 +90,7 @@
1842 virtual ~InfiniteScanlineMaker();
1843
1844 /** Returns a single infinite run at the current location */
1845- virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height);
1846+ virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height);
1847
1848 /** Increments the current y by the current line height */
1849 virtual void completeLine();
1850@@ -102,11 +102,11 @@
1851 virtual void setNewYCoordinate(double new_y);
1852
1853 /** Always true, but has to save the new height */
1854- virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height);
1855+ virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height);
1856
1857 private:
1858 double _x, _y;
1859- Layout::LineHeight _current_line_height;
1860+ Layout::FontMetrics _current_line_height;
1861 bool _negative_block_progression; /// if true, indicates that completeLine() should decrement rather than increment, ie block-progression is either rl or bt
1862 };
1863
1864@@ -122,7 +122,7 @@
1865 ShapeScanlineMaker(Shape const *shape, Layout::Direction block_progression);
1866 virtual ~ShapeScanlineMaker();
1867
1868- virtual std::vector<ScanRun> makeScanline(Layout::LineHeight const &line_height);
1869+ virtual std::vector<ScanRun> makeScanline(Layout::FontMetrics const &line_height);
1870
1871 virtual void completeLine();
1872
1873@@ -131,7 +131,7 @@
1874 virtual void setNewYCoordinate(double new_y);
1875
1876 /** never true */
1877- virtual bool canExtendCurrentScanline(Layout::LineHeight const &line_height);
1878+ virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height);
1879 private:
1880 /** To generate scanlines for top-to-bottom text it is easiest if we
1881 simply rotate the given shape by a multiple of 90 degrees. This stores
1882
1883=== modified file 'src/libnrtype/Layout-TNG-Scanline-Makers.cpp'
1884--- src/libnrtype/Layout-TNG-Scanline-Makers.cpp 2011-02-02 21:24:36 +0000
1885+++ src/libnrtype/Layout-TNG-Scanline-Makers.cpp 2015-11-24 09:40:36 +0000
1886@@ -19,9 +19,7 @@
1887
1888 Layout::InfiniteScanlineMaker::InfiniteScanlineMaker(double initial_x, double initial_y, Layout::Direction block_progression)
1889 {
1890- _current_line_height.ascent = 0.0;
1891- _current_line_height.descent = 0.0;
1892- _current_line_height.leading = 0.0;
1893+ _current_line_height.setZero();
1894 switch (block_progression) {
1895 case LEFT_TO_RIGHT:
1896 case RIGHT_TO_LEFT:
1897@@ -41,7 +39,7 @@
1898 {
1899 }
1900
1901-std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeScanline(Layout::LineHeight const &line_height)
1902+std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeScanline(Layout::FontMetrics const &line_height)
1903 {
1904 std::vector<ScanRun> runs(1);
1905 runs[0].x_start = _x;
1906@@ -54,12 +52,10 @@
1907 void Layout::InfiniteScanlineMaker::completeLine()
1908 {
1909 if (_negative_block_progression)
1910- _y -= _current_line_height.total();
1911+ _y -= _current_line_height.emSize();
1912 else
1913- _y += _current_line_height.total();
1914- _current_line_height.ascent = 0.0;
1915- _current_line_height.descent = 0.0;
1916- _current_line_height.leading = 0.0;
1917+ _y += _current_line_height.emSize();
1918+ _current_line_height.setZero();
1919 }
1920
1921 void Layout::InfiniteScanlineMaker::setNewYCoordinate(double new_y)
1922@@ -67,7 +63,7 @@
1923 _y = new_y;
1924 }
1925
1926-bool Layout::InfiniteScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &line_height)
1927+bool Layout::InfiniteScanlineMaker::canExtendCurrentScanline(Layout::FontMetrics const &line_height)
1928 {
1929 _current_line_height = line_height;
1930 return true;
1931@@ -111,29 +107,29 @@
1932 delete _rotated_shape;
1933 }
1934
1935-std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScanline(Layout::LineHeight const &line_height)
1936+std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScanline(Layout::FontMetrics const &line_height)
1937 {
1938- FloatLigne line_rasterization;
1939- FloatLigne line_decent_length_runs;
1940- float line_text_height = (float)(line_height.ascent + line_height.descent);
1941-
1942 if (_y > _bounding_box_bottom)
1943 return std::vector<ScanRun>();
1944
1945 if (_y < _bounding_box_top)
1946 _y = _bounding_box_top;
1947
1948+ FloatLigne line_rasterization;
1949+ FloatLigne line_decent_length_runs;
1950+ float line_text_height = (float)(line_height.emSize());
1951 if (line_text_height == 0.0)
1952 line_text_height = 0.001; // Scan() doesn't work for zero height so this will have to do
1953
1954- _current_line_height = (float)line_height.total();
1955+ _current_line_height = (float)line_height.emSize();
1956
1957 // I think what's going on here is that we're moving the top of the scanline to the given position...
1958 _rotated_shape->Scan(_rasterizer_y, _current_rasterization_point, _y, line_text_height);
1959 // ...then actually retreiving the scanline (which alters the first two parameters)
1960 _rotated_shape->Scan(_rasterizer_y, _current_rasterization_point, _y + line_text_height , &line_rasterization, true, line_text_height);
1961- // sanitise the raw rasterisation, which could have weird overlaps
1962+ // sanitise the raw rasterisation, which could have weird overlaps
1963 line_rasterization.Flatten();
1964+ // line_rasterization.Affiche();
1965 // cut out runs that cover less than 90% of the line
1966 line_decent_length_runs.Over(&line_rasterization, 0.9 * line_text_height);
1967
1968@@ -179,7 +175,7 @@
1969 // it's not an important question because <flowSpan> doesn't have a y attribute
1970 }
1971
1972-bool Layout::ShapeScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &/*line_height*/)
1973+bool Layout::ShapeScanlineMaker::canExtendCurrentScanline(Layout::FontMetrics const &/*line_height*/)
1974 {
1975 //we actually could return true if only the leading changed, but that's too much effort for something that rarely happens
1976 return false;
1977
1978=== modified file 'src/libnrtype/Layout-TNG.h'
1979--- src/libnrtype/Layout-TNG.h 2015-05-08 15:19:23 +0000
1980+++ src/libnrtype/Layout-TNG.h 2015-11-24 09:40:36 +0000
1981@@ -22,6 +22,7 @@
1982 #include <vector>
1983 #include <boost/optional.hpp>
1984 #include <svg/svg-length.h>
1985+#include "style-enums.h"
1986
1987 #ifdef HAVE_CAIRO_PDF
1988 namespace Inkscape {
1989@@ -157,6 +158,9 @@
1990 both the 'direction' and 'block-progression' CSS attributes. */
1991 enum Direction {LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP};
1992
1993+ /** Used to specify orientation of glyphs in vertical text. */
1994+ enum Orientation {ORIENTATION_UPRIGHT, ORIENTATION_SIDEWAYS};
1995+
1996 /** Display alignment for shapes. See appendWrapShape(). */
1997 enum DisplayAlign {DISPLAY_ALIGN_BEFORE, DISPLAY_ALIGN_CENTER, DISPLAY_ALIGN_AFTER};
1998
1999@@ -603,19 +607,69 @@
2000
2001 //@}
2002
2003- /// it's useful for this to be public so that ScanlineMaker can use it
2004- struct LineHeight {
2005- double ascent;
2006- double descent;
2007- double leading;
2008- inline double total() const {return ascent + descent + leading;}
2009- inline void setZero() {ascent = descent = leading = 0.0;}
2010- inline LineHeight& operator*=(double x) {ascent *= x; descent *= x; leading *= x; return *this;}
2011- void max(LineHeight const &other); /// makes this object contain the largest of all three members between this object and other
2012- inline double getAscent() const {return ascent; }
2013- inline double getDescent() const {return descent; }
2014- inline double getLeading() const {return leading; }
2015- };
2016+
2017+ /**
2018+ * Keep track of font metrics. Two use cases:
2019+ * 1. Keep track of ascent, descent, and x-height of an individual font.
2020+ * 2. Keep track of effective ascent and descent that includes half-leading.
2021+ *
2022+ * Note: Leading refers to the "external" leading which is added (subtracted) due to
2023+ * a computed value of 'line-height' that differs from 'font-size'. "Internal" leading
2024+ * which is specified inside a font is not used in CSS. The 'font-size' is based on
2025+ * the font's em size which is 'ascent' + 'descent'.
2026+ *
2027+ * This structure was renamed (and modified) from "LineHeight".
2028+ *
2029+ * It's useful for this to be public so that ScanlineMaker can use it.
2030+ */
2031+ class FontMetrics {
2032+
2033+ public:
2034+ FontMetrics() { reset(); }
2035+
2036+ void reset() {
2037+ ascent = 0.8;
2038+ descent = -0.2;
2039+ xheight = 0.5;
2040+ ascent_max = 0.8;
2041+ descent_max = 0.2;
2042+ }
2043+
2044+ void set( font_instance *font );
2045+
2046+ // CSS 2.1 dictates that font-size is based on em-size which is defined as ascent + descent
2047+ inline double emSize() const {return ascent + descent;}
2048+ // Alternatively name function for use 2.
2049+ inline double lineSize() const { return ascent + descent; }
2050+ inline void setZero() {ascent = descent = xheight = ascent_max = descent_max = 0.0;}
2051+
2052+ // For scaling for 'font-size'.
2053+ inline FontMetrics& operator*=(double x) {
2054+ ascent *= x; descent *= x; xheight *= x; ascent_max *= x; descent_max *= x;
2055+ return *this;
2056+ }
2057+
2058+ /// Save the larger values of ascent and descent between this and other. Needed for laying
2059+ /// out a line with mixed font-sizes, fonts, or line spacings.
2060+ void max(FontMetrics const &other);
2061+
2062+ /// Calculate the effective ascent and descent including half "leading".
2063+ void computeEffective( const double &line_height );
2064+
2065+ inline double getTypoAscent() const {return ascent; }
2066+ inline double getTypoDescent() const {return descent; }
2067+ inline double getXHeight() const {return xheight; }
2068+ inline double getMaxAscent() const {return ascent_max; }
2069+ inline double getMaxDescent() const {return descent_max; }
2070+
2071+ // private:
2072+ double ascent; // Typographic ascent.
2073+ double descent; // Typographic descent.
2074+ double xheight; // Height of 'x' measured from alphabetic baseline.
2075+ double ascent_max; // Maximum ascent of all glyphs in font.
2076+ double descent_max; // Maximum descent of all glyphs in font.
2077+
2078+ }; // End FontMetrics
2079
2080 /// see _enum_converter()
2081 struct EnumConversionItem {
2082@@ -664,11 +718,12 @@
2083 LengthAdjust lengthAdjust;
2084
2085 // a few functions for some of the more complicated style accesses
2086- float styleComputeFontSize() const;
2087 /// The return value must be freed with pango_font_description_free()
2088 PangoFontDescription *styleGetFontDescription() const;
2089 font_instance *styleGetFontInstance() const;
2090 Direction styleGetBlockProgression() const;
2091+ SPCSSTextOrientation styleGetTextOrientation() const;
2092+ SPCSSBaseline styleGetDominantBaseline() const;
2093 Alignment styleGetAlignment(Direction para_direction, bool try_text_align) const;
2094 };
2095
2096@@ -707,6 +762,22 @@
2097 return TOP_TO_BOTTOM;
2098 }
2099
2100+ /** The overall text-orientation of the whole flow. */
2101+ inline SPCSSTextOrientation _blockTextOrientation() const
2102+ {
2103+ if(!_input_stream.empty())
2104+ return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetTextOrientation();
2105+ return SP_CSS_TEXT_ORIENTATION_MIXED;
2106+ }
2107+
2108+ /** The overall text-orientation of the whole flow. */
2109+ inline SPCSSBaseline _blockBaseline() const
2110+ {
2111+ if(!_input_stream.empty())
2112+ return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetDominantBaseline();
2113+ return SP_CSS_BASELINE_AUTO;
2114+ }
2115+
2116 /** so that LEFT_TO_RIGHT == RIGHT_TO_LEFT but != TOP_TO_BOTTOM */
2117 static bool _directions_are_orthogonal(Direction d1, Direction d2);
2118
2119@@ -748,6 +819,7 @@
2120 float x; /// relative to the start of the chunk
2121 float y; /// relative to the current line's baseline
2122 float rotation; /// absolute, modulo any object transforms, which we don't know about
2123+ Orientation orientation; /// Orientation of glyph in vertical text
2124 float width;
2125 float vertical_scale; /// to implement lengthAdjust="spacingAndGlyphs" that must scale glyphs only horizontally; instead we change font size and then undo that change vertically only
2126 inline Span const & span(Layout const *l) const {return l->_spans[l->_characters[in_character].in_span];}
2127@@ -772,8 +844,9 @@
2128 float x_start; /// relative to the start of the chunk
2129 float x_end; /// relative to the start of the chunk
2130 inline float width() const {return std::abs(x_start - x_end);}
2131- LineHeight line_height;
2132+ FontMetrics line_height;
2133 double baseline_shift; /// relative to the line's baseline
2134+ SPCSSTextOrientation text_orientation;
2135 Direction direction; /// See CSS3 section 3.2. Either rtl or ltr
2136 Direction block_progression; /// See CSS3 section 3.2. The direction in which lines go.
2137 unsigned in_input_stream_item;
2138
2139=== modified file 'src/libnrtype/TextWrapper.cpp'
2140--- src/libnrtype/TextWrapper.cpp 2014-03-27 01:33:44 +0000
2141+++ src/libnrtype/TextWrapper.cpp 2015-11-24 09:40:36 +0000
2142@@ -862,7 +862,7 @@
2143 for (int i = 0; i < nbBox; i++) {
2144 boxes[i].ascent = 0;
2145 boxes[i].descent = 0;
2146- boxes[i].leading = 0;
2147+ boxes[i].xheight = 0;
2148 boxes[i].width = 0;
2149
2150 PangoFont *curPF = glyph_text[boxes[i].g_st].font;
2151@@ -870,7 +870,7 @@
2152 PangoFontDescription *pfd = pango_font_describe(curPF);
2153 font_instance *curF = f_src->Face(pfd);
2154 if ( curF ) {
2155- curF->FontMetrics(boxes[i].ascent, boxes[i].descent, boxes[i].leading);
2156+ curF->FontMetrics(boxes[i].ascent, boxes[i].descent, boxes[i].xheight);
2157 curF->Unref();
2158 }
2159 pango_font_description_free(pfd);
2160
2161=== modified file 'src/libnrtype/font-instance.h'
2162--- src/libnrtype/font-instance.h 2015-06-25 11:46:43 +0000
2163+++ src/libnrtype/font-instance.h 2015-11-24 09:40:36 +0000
2164@@ -57,19 +57,31 @@
2165
2166 // nota: all coordinates returned by these functions are on a [0..1] scale; you need to multiply
2167 // by the fontsize to get the real sizes
2168+
2169+ // Return 2geom pathvector for glyph. Deallocated when font instance dies.
2170 Geom::PathVector* PathVector(int glyph_id);
2171- // returns the 2geom-type pathvector for this glyph. no refcounting needed, it's deallocated when the font_instance dies
2172+
2173+ // Horizontal advance if 'vertical' is false, vertical advance if true.
2174 double Advance(int glyph_id, bool vertical);
2175- // nominal advance of the font.
2176+
2177+ double GetTypoAscent() { return _ascent; }
2178+ double GetTypoDescent() { return _descent; }
2179+ double GetXHeight() { return _xheight; }
2180+ double GetMaxAscent() { return _ascent_max; }
2181+ double GetMaxDescent() { return _descent_max; }
2182+ const double* GetBaselines() { return _baselines; }
2183+
2184 bool FontMetrics(double &ascent, double &descent, double &leading);
2185- bool FontDecoration(double &underline_position, double &underline_thickness,
2186- double &linethrough_position, double &linethrough_thickness);
2187+ bool FontDecoration(double &underline_position, double &underline_thickness,
2188+ double &linethrough_position, double &linethrough_thickness);
2189 bool FontSlope(double &run, double &rise);
2190 // for generating slanted cursors for oblique fonts
2191- Geom::OptRect BBox(int glyph_id);
2192+ Geom::OptRect BBox(int glyph_id);
2193
2194 private:
2195 void FreeTheFace();
2196+ // Find ascent, descent, x-height, and baselines.
2197+ void FindFontMetrics();
2198
2199 // Temp: make public
2200 public:
2201@@ -81,6 +93,17 @@
2202 // as long as pFont is valid, theFace is too
2203 #endif
2204
2205+private:
2206+
2207+ // Font metrics in em-box units
2208+ double _ascent; // Typographic ascent.
2209+ double _descent; // Typographic descent.
2210+ double _xheight; // x-height of font.
2211+ double _ascent_max; // Maxiumum ascent of all glyphs in font.
2212+ double _descent_max; // Maxiumum descent of all glyphs in font.
2213+
2214+ // Baselines
2215+ double _baselines[SP_CSS_BASELINE_SIZE];
2216 };
2217
2218
2219
2220=== modified file 'src/libnrtype/one-box.h'
2221--- src/libnrtype/one-box.h 2010-11-17 02:12:56 +0000
2222+++ src/libnrtype/one-box.h 2015-11-24 09:40:36 +0000
2223@@ -9,7 +9,7 @@
2224 // this time for sp-typeset
2225 struct one_box {
2226 int g_st, g_en; ///< First and last glyph of this word.
2227- double ascent, descent, leading;
2228+ double ascent, descent, xheight;
2229 double width;
2230 bool word_start, word_end;
2231 };
2232
2233=== modified file 'src/style-enums.h'
2234--- src/style-enums.h 2015-10-15 11:52:47 +0000
2235+++ src/style-enums.h 2015-11-24 09:40:36 +0000
2236@@ -174,10 +174,7 @@
2237 enum SPCSSTextOrientation {
2238 SP_CSS_TEXT_ORIENTATION_MIXED,
2239 SP_CSS_TEXT_ORIENTATION_UPRIGHT,
2240- SP_CSS_TEXT_ORIENTATION_SIDEWAYS_RIGHT,
2241- SP_CSS_TEXT_ORIENTATION_SIDEWAYS_LEFT,
2242- SP_CSS_TEXT_ORIENTATION_SIDEWAYS,
2243- SP_CSS_TEXT_ORIENTATION_USE_GLYPH_ORIENTATION
2244+ SP_CSS_TEXT_ORIENTATION_SIDEWAYS
2245 };
2246
2247 enum SPTextAnchor {
2248@@ -194,10 +191,24 @@
2249 SP_CSS_WHITE_SPACE_PRELINE
2250 };
2251
2252+// Not complete list
2253+enum SPCSSBaseline {
2254+ SP_CSS_BASELINE_AUTO,
2255+ SP_CSS_BASELINE_ALPHABETIC,
2256+ SP_CSS_BASELINE_IDEOGRAPHIC,
2257+ SP_CSS_BASELINE_HANGING,
2258+ SP_CSS_BASELINE_MATHEMATICAL,
2259+ SP_CSS_BASELINE_CENTRAL,
2260+ SP_CSS_BASELINE_MIDDLE,
2261+ SP_CSS_BASELINE_TEXT_BEFORE_EDGE,
2262+ SP_CSS_BASELINE_TEXT_AFTER_EDGE,
2263+ SP_CSS_BASELINE_SIZE // Size of enum, keep last.
2264+};
2265+
2266 enum SPCSSBaselineShift {
2267- SP_CSS_BASELINE_SHIFT_BASELINE,
2268- SP_CSS_BASELINE_SHIFT_SUB,
2269- SP_CSS_BASELINE_SHIFT_SUPER
2270+ SP_CSS_BASELINE_SHIFT_BASELINE,
2271+ SP_CSS_BASELINE_SHIFT_SUB,
2272+ SP_CSS_BASELINE_SHIFT_SUPER
2273 };
2274
2275 enum SPVisibility {
2276@@ -519,10 +530,20 @@
2277 static SPStyleEnum const enum_text_orientation[] = {
2278 {"mixed", SP_CSS_TEXT_ORIENTATION_MIXED}, // Default
2279 {"upright", SP_CSS_TEXT_ORIENTATION_UPRIGHT},
2280- {"sideways-right", SP_CSS_TEXT_ORIENTATION_SIDEWAYS_RIGHT},
2281- {"sideways-left", SP_CSS_TEXT_ORIENTATION_SIDEWAYS_LEFT},
2282 {"sideways", SP_CSS_TEXT_ORIENTATION_SIDEWAYS},
2283- {"use-glyph-orientation", SP_CSS_TEXT_ORIENTATION_USE_GLYPH_ORIENTATION},
2284+ {NULL, -1}
2285+};
2286+
2287+static SPStyleEnum const enum_baseline[] = {
2288+ {"auto", SP_CSS_BASELINE_AUTO}, // Default
2289+ {"alphabetic", SP_CSS_BASELINE_ALPHABETIC},
2290+ {"ideographic", SP_CSS_BASELINE_IDEOGRAPHIC},
2291+ {"hanging", SP_CSS_BASELINE_HANGING},
2292+ {"mathematical", SP_CSS_BASELINE_MATHEMATICAL},
2293+ {"central", SP_CSS_BASELINE_CENTRAL},
2294+ {"middle", SP_CSS_BASELINE_MIDDLE},
2295+ {"text-before-edge", SP_CSS_BASELINE_TEXT_BEFORE_EDGE},
2296+ {"text-after-edge", SP_CSS_BASELINE_TEXT_AFTER_EDGE},
2297 {NULL, -1}
2298 };
2299
2300
2301=== modified file 'src/style-internal.h'
2302--- src/style-internal.h 2015-06-22 10:10:30 +0000
2303+++ src/style-internal.h 2015-11-24 09:40:36 +0000
2304@@ -1260,7 +1260,6 @@
2305 float tspan_width; // from libnrtype, when it calculates spans
2306 float ascender; // the rest from tspan's font
2307 float descender;
2308- float line_gap;
2309 float underline_thickness;
2310 float underline_position;
2311 float line_through_thickness;
2312
2313=== modified file 'src/style.cpp'
2314--- src/style.cpp 2015-10-15 11:52:47 +0000
2315+++ src/style.cpp 2015-11-24 09:40:36 +0000
2316@@ -113,7 +113,7 @@
2317 font_weight( "font-weight", enum_font_weight, SP_CSS_FONT_WEIGHT_NORMAL, SP_CSS_FONT_WEIGHT_400 ),
2318 font_stretch( "font-stretch", enum_font_stretch, SP_CSS_FONT_STRETCH_NORMAL ),
2319 font_size(),
2320- line_height( "line-height", 1.0 ), // SPILengthOrNormal
2321+ line_height( "line-height", 1.25 ), // SPILengthOrNormal
2322 font_family( "font-family", "sans-serif" ), // SPIString w/default
2323 font(), // SPIFont
2324 font_specification( "-inkscape-font-specification" ), // SPIString
2325@@ -138,6 +138,7 @@
2326 direction( "direction", enum_direction, SP_CSS_DIRECTION_LTR ),
2327 writing_mode( "writing-mode", enum_writing_mode, SP_CSS_WRITING_MODE_LR_TB ),
2328 text_orientation( "text-orientation",enum_text_orientation,SP_CSS_TEXT_ORIENTATION_MIXED ),
2329+ dominant_baseline("dominant-baseline",enum_baseline, SP_CSS_BASELINE_AUTO ),
2330 baseline_shift(),
2331 text_anchor( "text-anchor", enum_text_anchor, SP_CSS_TEXT_ANCHOR_START ),
2332 white_space( "white-space", enum_white_space, SP_CSS_WHITE_SPACE_NORMAL ),
2333@@ -321,6 +322,7 @@
2334 _properties.push_back( &writing_mode );
2335 _properties.push_back( &direction );
2336 _properties.push_back( &text_orientation );
2337+ _properties.push_back( &dominant_baseline );
2338 _properties.push_back( &baseline_shift );
2339 _properties.push_back( &text_anchor );
2340 _properties.push_back( &white_space );
2341@@ -415,6 +417,7 @@
2342 // _propmap.insert( std::make_pair( direction.name, reinterpret_cast<SPIBasePtr>(&SPStyle::direction ) ) );
2343 // _propmap.insert( std::make_pair( writing_mode.name, reinterpret_cast<SPIBasePtr>(&SPStyle::writing_mode ) ) );
2344 // _propmap.insert( std::make_pair( text_orientation.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_orientation ) ) );
2345+ // _propmap.insert( std::make_pair( dominant_baseline.name, reinterpret_cast<SPIBasePtr>(&SPStyle::dominant_baseline ) ) );
2346 // _propmap.insert( std::make_pair( baseline_shift.name, reinterpret_cast<SPIBasePtr>(&SPStyle::baseline_shift ) ) );
2347 // _propmap.insert( std::make_pair( text_anchor.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_anchor ) ) );
2348 // _propmap.insert( std::make_pair( white_space.name, reinterpret_cast<SPIBasePtr>(&SPStyle::white_space ) ) );
2349@@ -796,6 +799,9 @@
2350 case SP_PROP_SHAPE_PADDING:
2351 shape_padding.readIfUnset( val );
2352 break;
2353+ case SP_PROP_DOMINANT_BASELINE:
2354+ dominant_baseline.readIfUnset( val );
2355+ break;
2356 case SP_PROP_BASELINE_SHIFT:
2357 baseline_shift.readIfUnset( val );
2358 break;
2359@@ -805,9 +811,6 @@
2360 case SP_PROP_ALIGNMENT_BASELINE:
2361 g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val);
2362 break;
2363- case SP_PROP_DOMINANT_BASELINE:
2364- g_warning("Unimplemented style property SP_PROP_DOMINANT_BASELINE: value: %s", val);
2365- break;
2366 case SP_PROP_GLYPH_ORIENTATION_HORIZONTAL:
2367 g_warning("Unimplemented style property SP_PROP_ORIENTATION_HORIZONTAL: value: %s", val);
2368 break;
2369
2370=== modified file 'src/style.h'
2371--- src/style.h 2015-10-15 11:52:47 +0000
2372+++ src/style.h 2015-11-24 09:40:36 +0000
2373@@ -148,6 +148,8 @@
2374 SPIEnum writing_mode;
2375 /** Text orientation (CSS Writing Modes 3) */
2376 SPIEnum text_orientation;
2377+ /** Dominant baseline (svg1.1) */
2378+ SPIEnum dominant_baseline;
2379 /** Baseline shift (svg1.1 10.9.2) */
2380 SPIBaselineShift baseline_shift;
2381
2382
2383=== modified file 'src/widgets/text-toolbar.cpp'
2384--- src/widgets/text-toolbar.cpp 2015-11-13 17:54:23 +0000
2385+++ src/widgets/text-toolbar.cpp 2015-11-24 09:40:36 +0000
2386@@ -737,28 +737,88 @@
2387 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2388 }
2389
2390-static void sp_text_orientation_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2391-{
2392- // quit if run by the _changed callbacks
2393- if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2394- return;
2395- }
2396- g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2397-
2398- int mode = ege_select_one_action_get_active( act );
2399-
2400- SPCSSAttr *css = sp_repr_css_attr_new ();
2401- switch (mode)
2402- {
2403- case 0:
2404- {
2405- sp_repr_css_set_property (css, "writing-mode", "lr");
2406- break;
2407- }
2408-
2409- case 1:
2410- {
2411- sp_repr_css_set_property (css, "writing-mode", "tb");
2412+static void sp_writing_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2413+{
2414+ // quit if run by the _changed callbacks
2415+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2416+ return;
2417+ }
2418+ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2419+
2420+ int mode = ege_select_one_action_get_active( act );
2421+
2422+ SPCSSAttr *css = sp_repr_css_attr_new ();
2423+ switch (mode)
2424+ {
2425+ case 0:
2426+ {
2427+ sp_repr_css_set_property (css, "writing-mode", "lr-tb");
2428+ break;
2429+ }
2430+
2431+ case 1:
2432+ {
2433+ sp_repr_css_set_property (css, "writing-mode", "tb-rl");
2434+ break;
2435+ }
2436+
2437+ case 2:
2438+ {
2439+ sp_repr_css_set_property (css, "writing-mode", "vertical-lr");
2440+ break;
2441+ }
2442+}
2443+
2444+ SPStyle query(SP_ACTIVE_DOCUMENT);
2445+ int result_numbers =
2446+ sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
2447+
2448+ // If querying returned nothing, update default style.
2449+ if (result_numbers == QUERY_STYLE_NOTHING)
2450+ {
2451+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2452+ prefs->mergeStyle("/tools/text/style", css);
2453+ }
2454+
2455+ sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true);
2456+ if(result_numbers != QUERY_STYLE_NOTHING)
2457+ {
2458+ DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_TEXT,
2459+ _("Text: Change writing mode"));
2460+ }
2461+ sp_repr_css_attr_unref (css);
2462+
2463+ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2464+}
2465+
2466+static void sp_text_orientation_changed( EgeSelectOneAction *act, GObject *tbl )
2467+{
2468+ // quit if run by the _changed callbacks
2469+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2470+ return;
2471+ }
2472+ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2473+
2474+ int mode = ege_select_one_action_get_active( act );
2475+
2476+ SPCSSAttr *css = sp_repr_css_attr_new ();
2477+ switch (mode)
2478+ {
2479+ case 0:
2480+ {
2481+ sp_repr_css_set_property (css, "text-orientation", "auto");
2482+ break;
2483+ }
2484+
2485+ case 1:
2486+ {
2487+ sp_repr_css_set_property (css, "text-orientation", "upright");
2488+ break;
2489+ }
2490+
2491+ case 2:
2492+ {
2493+ sp_repr_css_set_property (css, "text-orientation", "sideways");
2494 break;
2495 }
2496 }
2497@@ -893,13 +953,17 @@
2498 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTSTYLE);
2499 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
2500 int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_BASELINES);
2501+ int result_wmode = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_WRITINGMODES);
2502
2503 /*
2504 * If no text in selection (querying returned nothing), read the style from
2505 * the /tools/text preferencess (default style for new texts). Return if
2506 * tool bar already set to these preferences.
2507 */
2508- if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
2509+ if (result_family == QUERY_STYLE_NOTHING ||
2510+ result_style == QUERY_STYLE_NOTHING ||
2511+ result_numbers == QUERY_STYLE_NOTHING ||
2512+ result_wmode == QUERY_STYLE_NOTHING ) {
2513 // There are no texts in selection, read from preferences.
2514 query.readFromPrefs("/tools/text");
2515 #ifdef DEBUG_TEXT
2516@@ -1047,13 +1111,42 @@
2517 gtk_adjustment_set_value( letterSpacingAdjustment, letterSpacing );
2518
2519
2520+ // Writing mode
2521+ int activeButton2 = 0;
2522+ if (query.writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) activeButton2 = 0;
2523+ if (query.writing_mode.computed == SP_CSS_WRITING_MODE_TB_RL) activeButton2 = 1;
2524+ if (query.writing_mode.computed == SP_CSS_WRITING_MODE_TB_LR) activeButton2 = 2;
2525+
2526+ EgeSelectOneAction* writingModeAction =
2527+ EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextWritingModeAction" ) );
2528+ ege_select_one_action_set_active( writingModeAction, activeButton2 );
2529+
2530 // Orientation
2531- int activeButton2 = (query.writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB ? 0 : 1);
2532+ int activeButton3 = 0;
2533+ if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED ) activeButton3 = 0;
2534+ if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_UPRIGHT ) activeButton3 = 1;
2535+ if (query.text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS) activeButton3 = 2;
2536
2537 EgeSelectOneAction* textOrientationAction =
2538 EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextOrientationAction" ) );
2539- ege_select_one_action_set_active( textOrientationAction, activeButton2 );
2540-
2541+ ege_select_one_action_set_active( textOrientationAction, activeButton3 );
2542+
2543+ // Disable text orientation for horizontal text.. See above for why this nonsense
2544+ model = GTK_LIST_STORE( ege_select_one_action_get_model( textOrientationAction ) );
2545+
2546+ path = gtk_tree_path_new_from_string("0");
2547+ gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path );
2548+ gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 );
2549+
2550+ path = gtk_tree_path_new_from_string("1");
2551+ gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path );
2552+ gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 );
2553+
2554+ path = gtk_tree_path_new_from_string("2");
2555+ gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path );
2556+ gtk_list_store_set( model, &iter, /* column */ 3, activeButton2 != 0, -1 );
2557+
2558+ ege_select_one_action_update_sensitive( textOrientationAction );
2559
2560 }
2561
2562@@ -1387,7 +1480,7 @@
2563 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_align_mode_changed), holder );
2564 }
2565
2566- /* Orientation (Left to Right, Top to Bottom */
2567+ /* Writing mode (Horizontal, Vertical-LR, Vertical-RL) */
2568 {
2569 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2570
2571@@ -1402,14 +1495,73 @@
2572
2573 gtk_list_store_append( model, &iter );
2574 gtk_list_store_set( model, &iter,
2575- 0, _("Vertical"),
2576- 1, _("Vertical text"),
2577+ 0, _("Vertical — RL"),
2578+ 1, _("Vertical text — lines: right to left"),
2579 2, INKSCAPE_ICON("format-text-direction-vertical"),
2580 -1 );
2581
2582+ gtk_list_store_append( model, &iter );
2583+ gtk_list_store_set( model, &iter,
2584+ 0, _("Vertical — LR"),
2585+ 1, _("Vertical text — lines: left to right"), // Mongolian!
2586+ 2, INKSCAPE_ICON("format-text-direction-vertical-lr"),
2587+ -1 );
2588+
2589+ EgeSelectOneAction* act = ege_select_one_action_new( "TextWritingModeAction", // Name
2590+ _("Writing mode"), // Label
2591+ _("Block progression"), // Tooltip
2592+ NULL, // Icon name
2593+ GTK_TREE_MODEL(model) ); // Model
2594+
2595+ g_object_set( act, "short_label", "NotUsed", NULL );
2596+ gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2597+ g_object_set_data( holder, "TextWritingModeAction", act );
2598+
2599+ ege_select_one_action_set_appearance( act, "full" );
2600+ ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2601+ g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2602+ ege_select_one_action_set_icon_column( act, 2 );
2603+ ege_select_one_action_set_icon_size( act, secondarySize );
2604+ ege_select_one_action_set_tooltip_column( act, 1 );
2605+
2606+ gint mode = prefs->getInt("/tools/text/writing_mode", 0);
2607+ ege_select_one_action_set_active( act, mode );
2608+ g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_writing_mode_changed), holder );
2609+ }
2610+
2611+ /* Text (glyph) orientation (Auto (mixed), Upright, Sideways) */
2612+ {
2613+ GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
2614+
2615+ GtkTreeIter iter;
2616+
2617+ gtk_list_store_append( model, &iter );
2618+ gtk_list_store_set( model, &iter,
2619+ 0, _("Auto"),
2620+ 1, _("Auto glyph orientation"),
2621+ 2, INKSCAPE_ICON("text-orientation-auto"),
2622+ 3, true,
2623+ -1 );
2624+
2625+ gtk_list_store_append( model, &iter );
2626+ gtk_list_store_set( model, &iter,
2627+ 0, _("Upright"),
2628+ 1, _("Upright glyph orientation"),
2629+ 2, INKSCAPE_ICON("text-orientation-upright"),
2630+ 3, true,
2631+ -1 );
2632+
2633+ gtk_list_store_append( model, &iter );
2634+ gtk_list_store_set( model, &iter,
2635+ 0, _("Sideways"),
2636+ 1, _("Sideways glyph orientation"),
2637+ 2, INKSCAPE_ICON("text-orientation-sideways"),
2638+ 3, true,
2639+ -1 );
2640+
2641 EgeSelectOneAction* act = ege_select_one_action_new( "TextOrientationAction", // Name
2642- _("Orientation"), // Label
2643- _("Text orientation"), // Tooltip
2644+ _("Text orientation"), // Label
2645+ _("Text (glyph) orientation in vertical text."), // Tooltip
2646 NULL, // Icon name
2647 GTK_TREE_MODEL(model) ); // Model
2648
2649@@ -1423,10 +1575,11 @@
2650 ege_select_one_action_set_icon_column( act, 2 );
2651 ege_select_one_action_set_icon_size( act, secondarySize );
2652 ege_select_one_action_set_tooltip_column( act, 1 );
2653+ ege_select_one_action_set_sensitive_column( act, 3 );
2654
2655- gint mode = prefs->getInt("/tools/text/orientation", 0);
2656+ gint mode = prefs->getInt("/tools/text/text_orientation", 0);
2657 ege_select_one_action_set_active( act, mode );
2658- g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_mode_changed), holder );
2659+ g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_changed), holder );
2660 }
2661
2662 /* Line height */
2663
2664=== modified file 'src/widgets/toolbox.cpp'
2665--- src/widgets/toolbox.cpp 2015-11-21 12:48:59 +0000
2666+++ src/widgets/toolbox.cpp 2015-11-24 09:40:36 +0000
2667@@ -517,6 +517,8 @@
2668 " <toolitem action='TextDyAction' />"
2669 " <toolitem action='TextRotationAction' />"
2670 " <separator />"
2671+ " <toolitem action='TextWritingModeAction' />"
2672+ " <separator />"
2673 " <toolitem action='TextOrientationAction' />"
2674 " </toolbar>"
2675