PageRenderTime 85ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/pango/pango-layout.c

https://gitlab.com/ImageMagick/pango
C | 6471 lines | 3927 code | 970 blank | 1574 comment | 791 complexity | 5344e44b5d1f4418be39a12b6661480d MD5 | raw file
Possible License(s): LGPL-2.0
  1. /* Pango
  2. * pango-layout.c: High-level layout driver
  3. *
  4. * Copyright (C) 2000, 2001, 2006 Red Hat Software
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Library General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Library General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Library General Public
  17. * License along with this library; if not, write to the
  18. * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  19. * Boston, MA 02111-1307, USA.
  20. */
  21. /**
  22. * SECTION:layout
  23. * @short_description:High-level layout driver objects
  24. * @title:Layout Objects
  25. *
  26. * While complete access to the layout capabilities of Pango is provided
  27. * using the detailed interfaces for itemization and shaping, using
  28. * that functionality directly involves writing a fairly large amount
  29. * of code. The objects and functions in this section provide a
  30. * high-level driver for formatting entire paragraphs of text
  31. * at once.
  32. */
  33. /**
  34. * PangoLayout:
  35. *
  36. * The #PangoLayout structure represents an entire paragraph
  37. * of text. It is initialized with a #PangoContext, UTF-8 string
  38. * and set of attributes for that string. Once that is done, the
  39. * set of formatted lines can be extracted from the object,
  40. * the layout can be rendered, and conversion between logical
  41. * character positions within the layout's text, and the physical
  42. * position of the resulting glyphs can be made.
  43. *
  44. * There are also a number of parameters to adjust the formatting
  45. * of a #PangoLayout, which are illustrated in <xref linkend="parameters"/>.
  46. * It is possible, as well, to ignore the 2-D setup, and simply
  47. * treat the results of a #PangoLayout as a list of lines.
  48. *
  49. * <figure id="parameters">
  50. * <title>Adjustable parameters for a PangoLayout</title>
  51. * <graphic fileref="layout.gif" format="GIF"></graphic>
  52. * </figure>
  53. *
  54. * The #PangoLayout structure is opaque, and has no user-visible
  55. * fields.
  56. */
  57. /**
  58. * PangoLayoutIter:
  59. *
  60. * A #PangoLayoutIter structure can be used to
  61. * iterate over the visual extents of a #PangoLayout.
  62. *
  63. * The #PangoLayoutIter structure is opaque, and
  64. * has no user-visible fields.
  65. */
  66. #include "config.h"
  67. #include "pango-glyph.h" /* For pango_shape() */
  68. #include "pango-break.h"
  69. #include "pango-item.h"
  70. #include "pango-engine.h"
  71. #include "pango-impl-utils.h"
  72. #include "pango-glyph-item.h"
  73. #include <string.h>
  74. #include "pango-layout-private.h"
  75. typedef struct _ItemProperties ItemProperties;
  76. typedef struct _ParaBreakState ParaBreakState;
  77. struct _ItemProperties
  78. {
  79. PangoUnderline uline;
  80. gboolean strikethrough;
  81. gint rise;
  82. gint letter_spacing;
  83. gboolean shape_set;
  84. PangoRectangle *shape_ink_rect;
  85. PangoRectangle *shape_logical_rect;
  86. };
  87. typedef struct _PangoLayoutLinePrivate PangoLayoutLinePrivate;
  88. struct _PangoLayoutLinePrivate
  89. {
  90. PangoLayoutLine line;
  91. guint ref_count;
  92. /* Extents cache status:
  93. *
  94. * LEAKED means that the user has access to this line structure or a
  95. * run included in this line, and so can change the glyphs/glyph-widths.
  96. * If this is true, extents caching will be disabled.
  97. */
  98. enum {
  99. NOT_CACHED,
  100. CACHED,
  101. LEAKED
  102. } cache_status;
  103. PangoRectangle ink_rect;
  104. PangoRectangle logical_rect;
  105. };
  106. struct _PangoLayoutClass
  107. {
  108. GObjectClass parent_class;
  109. };
  110. #define LINE_IS_VALID(line) ((line) && (line)->layout != NULL)
  111. #ifdef G_DISABLE_CHECKS
  112. #define ITER_IS_INVALID(iter) FALSE
  113. #else
  114. #define ITER_IS_INVALID(iter) G_UNLIKELY (check_invalid ((iter), G_STRLOC))
  115. static gboolean
  116. check_invalid (PangoLayoutIter *iter,
  117. const char *loc)
  118. {
  119. if (iter->line->layout == NULL)
  120. {
  121. g_warning ("%s: PangoLayout changed since PangoLayoutIter was created, iterator invalid", loc);
  122. return TRUE;
  123. }
  124. else
  125. {
  126. return FALSE;
  127. }
  128. }
  129. #endif
  130. static void check_context_changed (PangoLayout *layout);
  131. static void layout_changed (PangoLayout *layout);
  132. static void pango_layout_clear_lines (PangoLayout *layout);
  133. static void pango_layout_check_lines (PangoLayout *layout);
  134. static PangoAttrList *pango_layout_get_effective_attributes (PangoLayout *layout);
  135. static PangoLayoutLine * pango_layout_line_new (PangoLayout *layout);
  136. static void pango_layout_line_postprocess (PangoLayoutLine *line,
  137. ParaBreakState *state,
  138. gboolean wrapped);
  139. static int *pango_layout_line_get_log2vis_map (PangoLayoutLine *line,
  140. gboolean strong);
  141. static int *pango_layout_line_get_vis2log_map (PangoLayoutLine *line,
  142. gboolean strong);
  143. static void pango_layout_line_leaked (PangoLayoutLine *line);
  144. /* doesn't leak line */
  145. static PangoLayoutLine* _pango_layout_iter_get_line (PangoLayoutIter *iter);
  146. static void pango_layout_get_item_properties (PangoItem *item,
  147. ItemProperties *properties);
  148. static void pango_layout_get_empty_extents_at_index (PangoLayout *layout,
  149. int index,
  150. PangoRectangle *logical_rect);
  151. static void pango_layout_finalize (GObject *object);
  152. G_DEFINE_TYPE (PangoLayout, pango_layout, G_TYPE_OBJECT)
  153. static void
  154. pango_layout_init (PangoLayout *layout)
  155. {
  156. layout->serial = 1;
  157. layout->attrs = NULL;
  158. layout->font_desc = NULL;
  159. layout->text = NULL;
  160. layout->length = 0;
  161. layout->width = -1;
  162. layout->height = -1;
  163. layout->indent = 0;
  164. layout->spacing = 0;
  165. layout->alignment = PANGO_ALIGN_LEFT;
  166. layout->justify = FALSE;
  167. layout->auto_dir = TRUE;
  168. layout->log_attrs = NULL;
  169. layout->lines = NULL;
  170. layout->line_count = 0;
  171. layout->tab_width = -1;
  172. layout->unknown_glyphs_count = -1;
  173. layout->wrap = PANGO_WRAP_WORD;
  174. layout->is_wrapped = FALSE;
  175. layout->ellipsize = PANGO_ELLIPSIZE_NONE;
  176. layout->is_ellipsized = FALSE;
  177. }
  178. static void
  179. pango_layout_class_init (PangoLayoutClass *klass)
  180. {
  181. GObjectClass *object_class = G_OBJECT_CLASS (klass);
  182. object_class->finalize = pango_layout_finalize;
  183. }
  184. static void
  185. pango_layout_finalize (GObject *object)
  186. {
  187. PangoLayout *layout;
  188. layout = PANGO_LAYOUT (object);
  189. pango_layout_clear_lines (layout);
  190. if (layout->context)
  191. g_object_unref (layout->context);
  192. if (layout->attrs)
  193. pango_attr_list_unref (layout->attrs);
  194. g_free (layout->text);
  195. if (layout->font_desc)
  196. pango_font_description_free (layout->font_desc);
  197. if (layout->tabs)
  198. pango_tab_array_free (layout->tabs);
  199. G_OBJECT_CLASS (pango_layout_parent_class)->finalize (object);
  200. }
  201. /**
  202. * pango_layout_new:
  203. * @context: a #PangoContext
  204. *
  205. * Create a new #PangoLayout object with attributes initialized to
  206. * default values for a particular #PangoContext.
  207. *
  208. * Return value: the newly allocated #PangoLayout, with a reference
  209. * count of one, which should be freed with
  210. * g_object_unref().
  211. **/
  212. PangoLayout *
  213. pango_layout_new (PangoContext *context)
  214. {
  215. PangoLayout *layout;
  216. g_return_val_if_fail (context != NULL, NULL);
  217. layout = g_object_new (PANGO_TYPE_LAYOUT, NULL);
  218. layout->context = context;
  219. layout->context_serial = pango_context_get_serial (context);
  220. g_object_ref (context);
  221. return layout;
  222. }
  223. /**
  224. * pango_layout_copy:
  225. * @src: a #PangoLayout
  226. *
  227. * Does a deep copy-by-value of the @src layout. The attribute list,
  228. * tab array, and text from the original layout are all copied by
  229. * value.
  230. *
  231. * Return value: (transfer full): the newly allocated #PangoLayout,
  232. * with a reference count of one, which should be freed
  233. * with g_object_unref().
  234. **/
  235. PangoLayout*
  236. pango_layout_copy (PangoLayout *src)
  237. {
  238. PangoLayout *layout;
  239. g_return_val_if_fail (PANGO_IS_LAYOUT (src), NULL);
  240. /* Copy referenced members */
  241. layout = pango_layout_new (src->context);
  242. if (src->attrs)
  243. layout->attrs = pango_attr_list_copy (src->attrs);
  244. if (src->font_desc)
  245. layout->font_desc = pango_font_description_copy (src->font_desc);
  246. if (src->tabs)
  247. layout->tabs = pango_tab_array_copy (src->tabs);
  248. /* Dupped */
  249. layout->text = g_strdup (src->text);
  250. /* Value fields */
  251. memcpy (&layout->copy_begin, &src->copy_begin,
  252. G_STRUCT_OFFSET (PangoLayout, copy_end) - G_STRUCT_OFFSET (PangoLayout, copy_begin));
  253. return layout;
  254. }
  255. /**
  256. * pango_layout_get_context:
  257. * @layout: a #PangoLayout
  258. *
  259. * Retrieves the #PangoContext used for this layout.
  260. *
  261. * Return value: (transfer none): the #PangoContext for the layout.
  262. * This does not have an additional refcount added, so if you want to
  263. * keep a copy of this around, you must reference it yourself.
  264. **/
  265. PangoContext *
  266. pango_layout_get_context (PangoLayout *layout)
  267. {
  268. g_return_val_if_fail (layout != NULL, NULL);
  269. return layout->context;
  270. }
  271. /**
  272. * pango_layout_set_width:
  273. * @layout: a #PangoLayout.
  274. * @width: the desired width in Pango units, or -1 to indicate that no
  275. * wrapping or ellipsization should be performed.
  276. *
  277. * Sets the width to which the lines of the #PangoLayout should wrap or
  278. * ellipsized. The default value is -1: no width set.
  279. **/
  280. void
  281. pango_layout_set_width (PangoLayout *layout,
  282. int width)
  283. {
  284. g_return_if_fail (layout != NULL);
  285. if (width < 0)
  286. width = -1;
  287. if (width != layout->width)
  288. {
  289. layout->width = width;
  290. layout_changed (layout);
  291. }
  292. }
  293. /**
  294. * pango_layout_get_width:
  295. * @layout: a #PangoLayout
  296. *
  297. * Gets the width to which the lines of the #PangoLayout should wrap.
  298. *
  299. * Return value: the width in Pango units, or -1 if no width set.
  300. **/
  301. int
  302. pango_layout_get_width (PangoLayout *layout)
  303. {
  304. g_return_val_if_fail (layout != NULL, 0);
  305. return layout->width;
  306. }
  307. /**
  308. * pango_layout_set_height:
  309. * @layout: a #PangoLayout.
  310. * @height: the desired height of the layout in Pango units if positive,
  311. * or desired number of lines if negative.
  312. *
  313. * Sets the height to which the #PangoLayout should be ellipsized at. There
  314. * are two different behaviors, based on whether @height is positive or
  315. * negative.
  316. *
  317. * If @height is positive, it will be the maximum height of the layout. Only
  318. * lines would be shown that would fit, and if there is any text omitted,
  319. * an ellipsis added. At least one line is included in each paragraph regardless
  320. * of how small the height value is. A value of zero will render exactly one
  321. * line for the entire layout.
  322. *
  323. * If @height is negative, it will be the (negative of) maximum number of lines per
  324. * paragraph. That is, the total number of lines shown may well be more than
  325. * this value if the layout contains multiple paragraphs of text.
  326. * The default value of -1 means that first line of each paragraph is ellipsized.
  327. * This behvaior may be changed in the future to act per layout instead of per
  328. * paragraph. File a bug against pango at <ulink
  329. * url="http://bugzilla.gnome.org/">http://bugzilla.gnome.org/</ulink> if your
  330. * code relies on this behavior.
  331. *
  332. * Height setting only has effect if a positive width is set on
  333. * @layout and ellipsization mode of @layout is not %PANGO_ELLIPSIZE_NONE.
  334. * The behavior is undefined if a height other than -1 is set and
  335. * ellipsization mode is set to %PANGO_ELLIPSIZE_NONE, and may change in the
  336. * future.
  337. *
  338. * Since: 1.20
  339. **/
  340. void
  341. pango_layout_set_height (PangoLayout *layout,
  342. int height)
  343. {
  344. g_return_if_fail (layout != NULL);
  345. if (height != layout->height)
  346. {
  347. layout->height = height;
  348. /* Do not invalidate if the number of lines requested is
  349. * larger than the total number of lines in layout.
  350. * Bug 549003
  351. */
  352. if (layout->ellipsize != PANGO_ELLIPSIZE_NONE &&
  353. !(layout->lines && layout->is_ellipsized == FALSE &&
  354. height < 0 && layout->line_count <= (guint) -height))
  355. layout_changed (layout);
  356. }
  357. }
  358. /**
  359. * pango_layout_get_height:
  360. * @layout: a #PangoLayout
  361. *
  362. * Gets the height of layout used for ellipsization. See
  363. * pango_layout_set_height() for details.
  364. *
  365. * Return value: the height, in Pango units if positive, or
  366. * number of lines if negative.
  367. *
  368. * Since: 1.20
  369. **/
  370. int
  371. pango_layout_get_height (PangoLayout *layout)
  372. {
  373. g_return_val_if_fail (layout != NULL, 0);
  374. return layout->height;
  375. }
  376. /**
  377. * pango_layout_set_wrap:
  378. * @layout: a #PangoLayout
  379. * @wrap: the wrap mode
  380. *
  381. * Sets the wrap mode; the wrap mode only has effect if a width
  382. * is set on the layout with pango_layout_set_width().
  383. * To turn off wrapping, set the width to -1.
  384. **/
  385. void
  386. pango_layout_set_wrap (PangoLayout *layout,
  387. PangoWrapMode wrap)
  388. {
  389. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  390. if (layout->wrap != wrap)
  391. {
  392. layout->wrap = wrap;
  393. if (layout->width != -1)
  394. layout_changed (layout);
  395. }
  396. }
  397. /**
  398. * pango_layout_get_wrap:
  399. * @layout: a #PangoLayout
  400. *
  401. * Gets the wrap mode for the layout.
  402. *
  403. * Use pango_layout_is_wrapped() to query whether any paragraphs
  404. * were actually wrapped.
  405. *
  406. * Return value: active wrap mode.
  407. **/
  408. PangoWrapMode
  409. pango_layout_get_wrap (PangoLayout *layout)
  410. {
  411. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), 0);
  412. return layout->wrap;
  413. }
  414. /**
  415. * pango_layout_is_wrapped:
  416. * @layout: a #PangoLayout
  417. *
  418. * Queries whether the layout had to wrap any paragraphs.
  419. *
  420. * This returns %TRUE if a positive width is set on @layout,
  421. * ellipsization mode of @layout is set to %PANGO_ELLIPSIZE_NONE,
  422. * and there are paragraphs exceeding the layout width that have
  423. * to be wrapped.
  424. *
  425. * Return value: %TRUE if any paragraphs had to be wrapped, %FALSE
  426. * otherwise.
  427. *
  428. * Since: 1.16
  429. */
  430. gboolean
  431. pango_layout_is_wrapped (PangoLayout *layout)
  432. {
  433. g_return_val_if_fail (layout != NULL, FALSE);
  434. pango_layout_check_lines (layout);
  435. return layout->is_wrapped;
  436. }
  437. /**
  438. * pango_layout_set_indent:
  439. * @layout: a #PangoLayout.
  440. * @indent: the amount by which to indent.
  441. *
  442. * Sets the width in Pango units to indent each paragraph. A negative value
  443. * of @indent will produce a hanging indentation. That is, the first line will
  444. * have the full width, and subsequent lines will be indented by the
  445. * absolute value of @indent.
  446. *
  447. * The indent setting is ignored if layout alignment is set to
  448. * %PANGO_ALIGN_CENTER.
  449. **/
  450. void
  451. pango_layout_set_indent (PangoLayout *layout,
  452. int indent)
  453. {
  454. g_return_if_fail (layout != NULL);
  455. if (indent != layout->indent)
  456. {
  457. layout->indent = indent;
  458. layout_changed (layout);
  459. }
  460. }
  461. /**
  462. * pango_layout_get_indent:
  463. * @layout: a #PangoLayout
  464. *
  465. * Gets the paragraph indent width in Pango units. A negative value
  466. * indicates a hanging indentation.
  467. *
  468. * Return value: the indent in Pango units.
  469. **/
  470. int
  471. pango_layout_get_indent (PangoLayout *layout)
  472. {
  473. g_return_val_if_fail (layout != NULL, 0);
  474. return layout->indent;
  475. }
  476. /**
  477. * pango_layout_set_spacing:
  478. * @layout: a #PangoLayout.
  479. * @spacing: the amount of spacing
  480. *
  481. * Sets the amount of spacing in Pango unit between the lines of the
  482. * layout.
  483. **/
  484. void
  485. pango_layout_set_spacing (PangoLayout *layout,
  486. int spacing)
  487. {
  488. g_return_if_fail (layout != NULL);
  489. if (spacing != layout->spacing)
  490. {
  491. layout->spacing = spacing;
  492. layout_changed (layout);
  493. }
  494. }
  495. /**
  496. * pango_layout_get_spacing:
  497. * @layout: a #PangoLayout
  498. *
  499. * Gets the amount of spacing between the lines of the layout.
  500. *
  501. * Return value: the spacing in Pango units.
  502. **/
  503. int
  504. pango_layout_get_spacing (PangoLayout *layout)
  505. {
  506. g_return_val_if_fail (layout != NULL, 0);
  507. return layout->spacing;
  508. }
  509. /**
  510. * pango_layout_set_attributes:
  511. * @layout: a #PangoLayout
  512. * @attrs: (allow-none) (transfer none): a #PangoAttrList, can be %NULL
  513. *
  514. * Sets the text attributes for a layout object.
  515. * References @attrs, so the caller can unref its reference.
  516. **/
  517. void
  518. pango_layout_set_attributes (PangoLayout *layout,
  519. PangoAttrList *attrs)
  520. {
  521. PangoAttrList *old_attrs;
  522. g_return_if_fail (layout != NULL);
  523. old_attrs = layout->attrs;
  524. /* We always clear lines such that this function can be called
  525. * whenever attrs changes.
  526. */
  527. layout->attrs = attrs;
  528. if (layout->attrs)
  529. pango_attr_list_ref (layout->attrs);
  530. layout_changed (layout);
  531. if (old_attrs)
  532. pango_attr_list_unref (old_attrs);
  533. layout->tab_width = -1;
  534. }
  535. /**
  536. * pango_layout_get_attributes:
  537. * @layout: a #PangoLayout
  538. *
  539. * Gets the attribute list for the layout, if any.
  540. *
  541. * Return value: (transfer none): a #PangoAttrList.
  542. **/
  543. PangoAttrList*
  544. pango_layout_get_attributes (PangoLayout *layout)
  545. {
  546. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
  547. return layout->attrs;
  548. }
  549. /**
  550. * pango_layout_set_font_description:
  551. * @layout: a #PangoLayout
  552. * @desc: (allow-none): the new #PangoFontDescription, or %NULL to unset the
  553. * current font description
  554. *
  555. * Sets the default font description for the layout. If no font
  556. * description is set on the layout, the font description from
  557. * the layout's context is used.
  558. **/
  559. void
  560. pango_layout_set_font_description (PangoLayout *layout,
  561. const PangoFontDescription *desc)
  562. {
  563. g_return_if_fail (layout != NULL);
  564. if (desc != layout->font_desc &&
  565. (!desc || !layout->font_desc || !pango_font_description_equal(desc, layout->font_desc)))
  566. {
  567. if (layout->font_desc)
  568. pango_font_description_free (layout->font_desc);
  569. layout->font_desc = desc ? pango_font_description_copy (desc) : NULL;
  570. layout_changed (layout);
  571. layout->tab_width = -1;
  572. }
  573. }
  574. /**
  575. * pango_layout_get_font_description:
  576. * @layout: a #PangoLayout
  577. *
  578. * Gets the font description for the layout, if any.
  579. *
  580. * Return value: (nullable): a pointer to the layout's font
  581. * description, or %NULL if the font description from the layout's
  582. * context is inherited. This value is owned by the layout and must
  583. * not be modified or freed.
  584. *
  585. * Since: 1.8
  586. **/
  587. const PangoFontDescription *
  588. pango_layout_get_font_description (PangoLayout *layout)
  589. {
  590. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
  591. return layout->font_desc;
  592. }
  593. /**
  594. * pango_layout_set_justify:
  595. * @layout: a #PangoLayout
  596. * @justify: whether the lines in the layout should be justified.
  597. *
  598. * Sets whether each complete line should be stretched to
  599. * fill the entire width of the layout. This stretching is typically
  600. * done by adding whitespace, but for some scripts (such as Arabic),
  601. * the justification may be done in more complex ways, like extending
  602. * the characters.
  603. *
  604. * Note that this setting is not implemented and so is ignored in Pango
  605. * older than 1.18.
  606. **/
  607. void
  608. pango_layout_set_justify (PangoLayout *layout,
  609. gboolean justify)
  610. {
  611. g_return_if_fail (layout != NULL);
  612. if (justify != layout->justify)
  613. {
  614. layout->justify = justify;
  615. if (layout->is_ellipsized || layout->is_wrapped)
  616. layout_changed (layout);
  617. }
  618. }
  619. /**
  620. * pango_layout_get_justify:
  621. * @layout: a #PangoLayout
  622. *
  623. * Gets whether each complete line should be stretched to fill the entire
  624. * width of the layout.
  625. *
  626. * Return value: the justify.
  627. **/
  628. gboolean
  629. pango_layout_get_justify (PangoLayout *layout)
  630. {
  631. g_return_val_if_fail (layout != NULL, FALSE);
  632. return layout->justify;
  633. }
  634. /**
  635. * pango_layout_set_auto_dir:
  636. * @layout: a #PangoLayout
  637. * @auto_dir: if %TRUE, compute the bidirectional base direction
  638. * from the layout's contents.
  639. *
  640. * Sets whether to calculate the bidirectional base direction
  641. * for the layout according to the contents of the layout;
  642. * when this flag is on (the default), then paragraphs in
  643. @layout that begin with strong right-to-left characters
  644. * (Arabic and Hebrew principally), will have right-to-left
  645. * layout, paragraphs with letters from other scripts will
  646. * have left-to-right layout. Paragraphs with only neutral
  647. * characters get their direction from the surrounding paragraphs.
  648. *
  649. * When %FALSE, the choice between left-to-right and
  650. * right-to-left layout is done according to the base direction
  651. * of the layout's #PangoContext. (See pango_context_set_base_dir()).
  652. *
  653. * When the auto-computed direction of a paragraph differs from the
  654. * base direction of the context, the interpretation of
  655. * %PANGO_ALIGN_LEFT and %PANGO_ALIGN_RIGHT are swapped.
  656. *
  657. * Since: 1.4
  658. **/
  659. void
  660. pango_layout_set_auto_dir (PangoLayout *layout,
  661. gboolean auto_dir)
  662. {
  663. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  664. auto_dir = auto_dir != FALSE;
  665. if (auto_dir != layout->auto_dir)
  666. {
  667. layout->auto_dir = auto_dir;
  668. layout_changed (layout);
  669. }
  670. }
  671. /**
  672. * pango_layout_get_auto_dir:
  673. * @layout: a #PangoLayout
  674. *
  675. * Gets whether to calculate the bidirectional base direction
  676. * for the layout according to the contents of the layout.
  677. * See pango_layout_set_auto_dir().
  678. *
  679. * Return value: %TRUE if the bidirectional base direction
  680. * is computed from the layout's contents, %FALSE otherwise.
  681. *
  682. * Since: 1.4
  683. **/
  684. gboolean
  685. pango_layout_get_auto_dir (PangoLayout *layout)
  686. {
  687. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
  688. return layout->auto_dir;
  689. }
  690. /**
  691. * pango_layout_set_alignment:
  692. * @layout: a #PangoLayout
  693. * @alignment: the alignment
  694. *
  695. * Sets the alignment for the layout: how partial lines are
  696. * positioned within the horizontal space available.
  697. **/
  698. void
  699. pango_layout_set_alignment (PangoLayout *layout,
  700. PangoAlignment alignment)
  701. {
  702. g_return_if_fail (layout != NULL);
  703. if (alignment != layout->alignment)
  704. {
  705. layout->alignment = alignment;
  706. layout_changed (layout);
  707. }
  708. }
  709. /**
  710. * pango_layout_get_alignment:
  711. * @layout: a #PangoLayout
  712. *
  713. * Gets the alignment for the layout: how partial lines are
  714. * positioned within the horizontal space available.
  715. *
  716. * Return value: the alignment.
  717. **/
  718. PangoAlignment
  719. pango_layout_get_alignment (PangoLayout *layout)
  720. {
  721. g_return_val_if_fail (layout != NULL, PANGO_ALIGN_LEFT);
  722. return layout->alignment;
  723. }
  724. /**
  725. * pango_layout_set_tabs:
  726. * @layout: a #PangoLayout
  727. * @tabs: (allow-none): a #PangoTabArray, or %NULL
  728. *
  729. * Sets the tabs to use for @layout, overriding the default tabs
  730. * (by default, tabs are every 8 spaces). If @tabs is %NULL, the default
  731. * tabs are reinstated. @tabs is copied into the layout; you must
  732. * free your copy of @tabs yourself.
  733. **/
  734. void
  735. pango_layout_set_tabs (PangoLayout *layout,
  736. PangoTabArray *tabs)
  737. {
  738. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  739. if (tabs != layout->tabs)
  740. {
  741. if (layout->tabs)
  742. pango_tab_array_free (layout->tabs);
  743. layout->tabs = tabs ? pango_tab_array_copy (tabs) : NULL;
  744. layout_changed (layout);
  745. }
  746. }
  747. /**
  748. * pango_layout_get_tabs:
  749. * @layout: a #PangoLayout
  750. *
  751. * Gets the current #PangoTabArray used by this layout. If no
  752. * #PangoTabArray has been set, then the default tabs are in use
  753. * and %NULL is returned. Default tabs are every 8 spaces.
  754. * The return value should be freed with pango_tab_array_free().
  755. *
  756. * Return value: (nullable): a copy of the tabs for this layout, or
  757. * %NULL.
  758. **/
  759. PangoTabArray*
  760. pango_layout_get_tabs (PangoLayout *layout)
  761. {
  762. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
  763. if (layout->tabs)
  764. return pango_tab_array_copy (layout->tabs);
  765. else
  766. return NULL;
  767. }
  768. /**
  769. * pango_layout_set_single_paragraph_mode:
  770. * @layout: a #PangoLayout
  771. * @setting: new setting
  772. *
  773. * If @setting is %TRUE, do not treat newlines and similar characters
  774. * as paragraph separators; instead, keep all text in a single paragraph,
  775. * and display a glyph for paragraph separator characters. Used when
  776. * you want to allow editing of newlines on a single text line.
  777. **/
  778. void
  779. pango_layout_set_single_paragraph_mode (PangoLayout *layout,
  780. gboolean setting)
  781. {
  782. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  783. setting = setting != FALSE;
  784. if (layout->single_paragraph != setting)
  785. {
  786. layout->single_paragraph = setting;
  787. layout_changed (layout);
  788. }
  789. }
  790. /**
  791. * pango_layout_get_single_paragraph_mode:
  792. * @layout: a #PangoLayout
  793. *
  794. * Obtains the value set by pango_layout_set_single_paragraph_mode().
  795. *
  796. * Return value: %TRUE if the layout does not break paragraphs at
  797. * paragraph separator characters, %FALSE otherwise.
  798. **/
  799. gboolean
  800. pango_layout_get_single_paragraph_mode (PangoLayout *layout)
  801. {
  802. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
  803. return layout->single_paragraph;
  804. }
  805. /**
  806. * pango_layout_set_ellipsize:
  807. * @layout: a #PangoLayout
  808. * @ellipsize: the new ellipsization mode for @layout
  809. *
  810. * Sets the type of ellipsization being performed for @layout.
  811. * Depending on the ellipsization mode @ellipsize text is
  812. * removed from the start, middle, or end of text so they
  813. * fit within the width and height of layout set with
  814. * pango_layout_set_width() and pango_layout_set_height().
  815. *
  816. * If the layout contains characters such as newlines that
  817. * force it to be layed out in multiple paragraphs, then whether
  818. * each paragraph is ellipsized separately or the entire layout
  819. * is ellipsized as a whole depends on the set height of the layout.
  820. * See pango_layout_set_height() for details.
  821. *
  822. * Since: 1.6
  823. **/
  824. void
  825. pango_layout_set_ellipsize (PangoLayout *layout,
  826. PangoEllipsizeMode ellipsize)
  827. {
  828. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  829. if (ellipsize != layout->ellipsize)
  830. {
  831. layout->ellipsize = ellipsize;
  832. if (layout->is_ellipsized || layout->is_wrapped)
  833. layout_changed (layout);
  834. }
  835. }
  836. /**
  837. * pango_layout_get_ellipsize:
  838. * @layout: a #PangoLayout
  839. *
  840. * Gets the type of ellipsization being performed for @layout.
  841. * See pango_layout_set_ellipsize()
  842. *
  843. * Return value: the current ellipsization mode for @layout.
  844. *
  845. * Use pango_layout_is_ellipsized() to query whether any paragraphs
  846. * were actually ellipsized.
  847. *
  848. * Since: 1.6
  849. **/
  850. PangoEllipsizeMode
  851. pango_layout_get_ellipsize (PangoLayout *layout)
  852. {
  853. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), PANGO_ELLIPSIZE_NONE);
  854. return layout->ellipsize;
  855. }
  856. /**
  857. * pango_layout_is_ellipsized:
  858. * @layout: a #PangoLayout
  859. *
  860. * Queries whether the layout had to ellipsize any paragraphs.
  861. *
  862. * This returns %TRUE if the ellipsization mode for @layout
  863. * is not %PANGO_ELLIPSIZE_NONE, a positive width is set on @layout,
  864. * and there are paragraphs exceeding that width that have to be
  865. * ellipsized.
  866. *
  867. * Return value: %TRUE if any paragraphs had to be ellipsized, %FALSE
  868. * otherwise.
  869. *
  870. * Since: 1.16
  871. */
  872. gboolean
  873. pango_layout_is_ellipsized (PangoLayout *layout)
  874. {
  875. g_return_val_if_fail (layout != NULL, FALSE);
  876. pango_layout_check_lines (layout);
  877. return layout->is_ellipsized;
  878. }
  879. /**
  880. * pango_layout_set_text:
  881. * @layout: a #PangoLayout
  882. * @text: a valid UTF-8 string
  883. * @length: maximum length of @text, in bytes. -1 indicates that
  884. * the string is nul-terminated and the length should be
  885. * calculated. The text will also be truncated on
  886. * encountering a nul-termination even when @length is
  887. * positive.
  888. *
  889. * Sets the text of the layout.
  890. *
  891. * Note that if you have used
  892. * pango_layout_set_markup() or pango_layout_set_markup_with_accel() on
  893. * @layout before, you may want to call pango_layout_set_attributes() to clear
  894. * the attributes set on the layout from the markup as this function does not
  895. * clear attributes.
  896. **/
  897. void
  898. pango_layout_set_text (PangoLayout *layout,
  899. const char *text,
  900. int length)
  901. {
  902. char *old_text, *start, *end;
  903. g_return_if_fail (layout != NULL);
  904. g_return_if_fail (length == 0 || text != NULL);
  905. old_text = layout->text;
  906. if (length < 0)
  907. layout->text = g_strdup (text);
  908. else if (length > 0)
  909. /* This is not exactly what we want. We don't need the padding...
  910. */
  911. layout->text = g_strndup (text, length);
  912. else
  913. layout->text = g_malloc0 (1);
  914. layout->length = strlen (layout->text);
  915. /* validate it, and replace invalid bytes with '?'
  916. */
  917. start = layout->text;
  918. for (;;) {
  919. gboolean valid;
  920. valid = g_utf8_validate (start, -1, (const char **)&end);
  921. if (!*end)
  922. break;
  923. /* Replace invalid bytes with -1. The -1 will be converted to
  924. * ((gunichar) -1) by glib, and that in turn yields a glyph value of
  925. * ((PangoGlyph) -1) by PANGO_GET_UNKNOWN_GLYPH(-1),
  926. * and that's PANGO_GLYPH_INVALID_INPUT.
  927. */
  928. if (!valid)
  929. *end++ = -1;
  930. start = end;
  931. }
  932. if (start != layout->text)
  933. /* TODO: Write out the beginning excerpt of text? */
  934. g_warning ("Invalid UTF-8 string passed to pango_layout_set_text()");
  935. layout->n_chars = pango_utf8_strlen (layout->text, -1);
  936. layout_changed (layout);
  937. g_free (old_text);
  938. }
  939. /**
  940. * pango_layout_get_text:
  941. * @layout: a #PangoLayout
  942. *
  943. * Gets the text in the layout. The returned text should not
  944. * be freed or modified.
  945. *
  946. * Return value: the text in the @layout.
  947. **/
  948. const char*
  949. pango_layout_get_text (PangoLayout *layout)
  950. {
  951. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
  952. /* We don't ever want to return NULL as the text.
  953. */
  954. if (G_UNLIKELY (!layout->text))
  955. return "";
  956. return layout->text;
  957. }
  958. /**
  959. * pango_layout_get_character_count:
  960. * @layout: a #PangoLayout
  961. *
  962. * Returns the number of Unicode characters in the
  963. * the text of @layout.
  964. *
  965. * Return value: the number of Unicode characters
  966. * in the text of @layout
  967. *
  968. * Since: 1.30
  969. */
  970. gint
  971. pango_layout_get_character_count (PangoLayout *layout)
  972. {
  973. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), 0);
  974. return layout->n_chars;
  975. }
  976. /**
  977. * pango_layout_set_markup:
  978. * @layout: a #PangoLayout
  979. * @markup: marked-up text
  980. * @length: length of marked-up text in bytes, or -1 if @markup is
  981. * null-terminated
  982. *
  983. * Same as pango_layout_set_markup_with_accel(), but
  984. * the markup text isn't scanned for accelerators.
  985. *
  986. **/
  987. void
  988. pango_layout_set_markup (PangoLayout *layout,
  989. const char *markup,
  990. int length)
  991. {
  992. pango_layout_set_markup_with_accel (layout, markup, length, 0, NULL);
  993. }
  994. /**
  995. * pango_layout_set_markup_with_accel:
  996. * @layout: a #PangoLayout
  997. * @markup: marked-up text
  998. * (see <link linkend="PangoMarkupFormat">markup format</link>)
  999. * @length: length of marked-up text in bytes, or -1 if @markup is
  1000. * null-terminated
  1001. * @accel_marker: marker for accelerators in the text
  1002. * @accel_char: (out caller-allocates) (allow-none): return location
  1003. * for first located accelerator, or %NULL
  1004. *
  1005. * Sets the layout text and attribute list from marked-up text (see
  1006. * <link linkend="PangoMarkupFormat">markup format</link>). Replaces
  1007. * the current text and attribute list.
  1008. *
  1009. * If @accel_marker is nonzero, the given character will mark the
  1010. * character following it as an accelerator. For example, @accel_marker
  1011. * might be an ampersand or underscore. All characters marked
  1012. * as an accelerator will receive a %PANGO_UNDERLINE_LOW attribute,
  1013. * and the first character so marked will be returned in @accel_char.
  1014. * Two @accel_marker characters following each other produce a single
  1015. * literal @accel_marker character.
  1016. **/
  1017. void
  1018. pango_layout_set_markup_with_accel (PangoLayout *layout,
  1019. const char *markup,
  1020. int length,
  1021. gunichar accel_marker,
  1022. gunichar *accel_char)
  1023. {
  1024. PangoAttrList *list = NULL;
  1025. char *text = NULL;
  1026. GError *error;
  1027. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  1028. g_return_if_fail (markup != NULL);
  1029. error = NULL;
  1030. if (!pango_parse_markup (markup, length,
  1031. accel_marker,
  1032. &list, &text,
  1033. accel_char,
  1034. &error))
  1035. {
  1036. g_warning ("pango_layout_set_markup_with_accel: %s", error->message);
  1037. g_error_free (error);
  1038. return;
  1039. }
  1040. pango_layout_set_text (layout, text, -1);
  1041. pango_layout_set_attributes (layout, list);
  1042. pango_attr_list_unref (list);
  1043. g_free (text);
  1044. }
  1045. /**
  1046. * pango_layout_get_unknown_glyphs_count:
  1047. * @layout: a #PangoLayout
  1048. *
  1049. * Counts the number unknown glyphs in @layout. That is, zero if
  1050. * glyphs for all characters in the layout text were found, or more
  1051. * than zero otherwise.
  1052. *
  1053. * This function can be used to determine if there are any fonts
  1054. * available to render all characters in a certain string, or when
  1055. * used in combination with %PANGO_ATTR_FALLBACK, to check if a
  1056. * certain font supports all the characters in the string.
  1057. *
  1058. * Return value: The number of unknown glyphs in @layout.
  1059. *
  1060. * Since: 1.16
  1061. */
  1062. int
  1063. pango_layout_get_unknown_glyphs_count (PangoLayout *layout)
  1064. {
  1065. PangoLayoutLine *line;
  1066. PangoLayoutRun *run;
  1067. GSList *lines_list;
  1068. GSList *runs_list;
  1069. int i, count = 0;
  1070. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), 0);
  1071. pango_layout_check_lines (layout);
  1072. if (layout->unknown_glyphs_count >= 0)
  1073. return layout->unknown_glyphs_count;
  1074. lines_list = layout->lines;
  1075. while (lines_list)
  1076. {
  1077. line = lines_list->data;
  1078. runs_list = line->runs;
  1079. while (runs_list)
  1080. {
  1081. run = runs_list->data;
  1082. for (i = 0; i < run->glyphs->num_glyphs; i++)
  1083. {
  1084. if (run->glyphs->glyphs[i].glyph & PANGO_GLYPH_UNKNOWN_FLAG)
  1085. count++;
  1086. }
  1087. runs_list = runs_list->next;
  1088. }
  1089. lines_list = lines_list->next;
  1090. }
  1091. layout->unknown_glyphs_count = count;
  1092. return count;
  1093. }
  1094. static void
  1095. check_context_changed (PangoLayout *layout)
  1096. {
  1097. guint old_serial = layout->context_serial;
  1098. layout->context_serial = pango_context_get_serial (layout->context);
  1099. if (old_serial != layout->context_serial)
  1100. pango_layout_context_changed (layout);
  1101. }
  1102. static void
  1103. layout_changed (PangoLayout *layout)
  1104. {
  1105. layout->serial++;
  1106. if (layout->serial == 0)
  1107. layout->serial++;
  1108. pango_layout_clear_lines (layout);
  1109. }
  1110. /**
  1111. * pango_layout_context_changed:
  1112. * @layout: a #PangoLayout
  1113. *
  1114. * Forces recomputation of any state in the #PangoLayout that
  1115. * might depend on the layout's context. This function should
  1116. * be called if you make changes to the context subsequent
  1117. * to creating the layout.
  1118. **/
  1119. void
  1120. pango_layout_context_changed (PangoLayout *layout)
  1121. {
  1122. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  1123. layout_changed (layout);
  1124. layout->tab_width = -1;
  1125. }
  1126. /**
  1127. * pango_layout_get_serial:
  1128. * @layout: a #PangoLayout
  1129. *
  1130. * Returns the current serial number of @layout. The serial number is
  1131. * initialized to an small number larger than zero when a new layout
  1132. * is created and is increased whenever the layout is changed using any
  1133. * of the setter functions, or the #PangoContext it uses has changed.
  1134. * The serial may wrap, but will never have the value 0. Since it
  1135. * can wrap, never compare it with "less than", always use "not equals".
  1136. *
  1137. * This can be used to automatically detect changes to a #PangoLayout, and
  1138. * is useful for example to decide whether a layout needs redrawing.
  1139. * To force the serial to be increased, use pango_layout_context_changed().
  1140. *
  1141. * Return value: The current serial number of @layout.
  1142. *
  1143. * Since: 1.32.4
  1144. **/
  1145. guint
  1146. pango_layout_get_serial (PangoLayout *layout)
  1147. {
  1148. check_context_changed (layout);
  1149. return layout->serial;
  1150. }
  1151. /**
  1152. * pango_layout_get_log_attrs:
  1153. * @layout: a #PangoLayout
  1154. * @attrs: (out)(array length=n_attrs)(transfer container):
  1155. * location to store a pointer to an array of logical attributes
  1156. * This value must be freed with g_free().
  1157. * @n_attrs: (out): location to store the number of the attributes in the
  1158. * array. (The stored value will be one more than the total number
  1159. * of characters in the layout, since there need to be attributes
  1160. * corresponding to both the position before the first character
  1161. * and the position after the last character.)
  1162. *
  1163. * Retrieves an array of logical attributes for each character in
  1164. * the @layout.
  1165. **/
  1166. void
  1167. pango_layout_get_log_attrs (PangoLayout *layout,
  1168. PangoLogAttr **attrs,
  1169. gint *n_attrs)
  1170. {
  1171. g_return_if_fail (layout != NULL);
  1172. pango_layout_check_lines (layout);
  1173. if (attrs)
  1174. {
  1175. *attrs = g_new (PangoLogAttr, layout->n_chars + 1);
  1176. memcpy (*attrs, layout->log_attrs, sizeof(PangoLogAttr) * (layout->n_chars + 1));
  1177. }
  1178. if (n_attrs)
  1179. *n_attrs = layout->n_chars + 1;
  1180. }
  1181. /**
  1182. * pango_layout_get_log_attrs_readonly:
  1183. * @layout: a #PangoLayout
  1184. * @n_attrs: (out): location to store the number of the attributes in
  1185. * the array
  1186. *
  1187. * Retrieves an array of logical attributes for each character in
  1188. * the @layout.
  1189. *
  1190. * This is a faster alternative to pango_layout_get_log_attrs().
  1191. * The returned array is part of @layout and must not be modified.
  1192. * Modifying the layout will invalidate the returned array.
  1193. *
  1194. * The number of attributes returned in @n_attrs will be one more
  1195. * than the total number of characters in the layout, since there
  1196. * need to be attributes corresponding to both the position before
  1197. * the first character and the position after the last character.
  1198. *
  1199. * Returns: (array length=n_attrs): an array of logical attributes
  1200. *
  1201. * Since: 1.30
  1202. */
  1203. const PangoLogAttr *
  1204. pango_layout_get_log_attrs_readonly (PangoLayout *layout,
  1205. gint *n_attrs)
  1206. {
  1207. if (n_attrs)
  1208. *n_attrs = 0;
  1209. g_return_val_if_fail (layout != NULL, NULL);
  1210. pango_layout_check_lines (layout);
  1211. if (n_attrs)
  1212. *n_attrs = layout->n_chars + 1;
  1213. return layout->log_attrs;
  1214. }
  1215. /**
  1216. * pango_layout_get_line_count:
  1217. * @layout: #PangoLayout
  1218. *
  1219. * Retrieves the count of lines for the @layout.
  1220. *
  1221. * Return value: the line count.
  1222. **/
  1223. int
  1224. pango_layout_get_line_count (PangoLayout *layout)
  1225. {
  1226. g_return_val_if_fail (layout != NULL, 0);
  1227. pango_layout_check_lines (layout);
  1228. return layout->line_count;
  1229. }
  1230. /**
  1231. * pango_layout_get_lines:
  1232. * @layout: a #PangoLayout
  1233. *
  1234. * Returns the lines of the @layout as a list.
  1235. *
  1236. * Use the faster pango_layout_get_lines_readonly() if you do not plan
  1237. * to modify the contents of the lines (glyphs, glyph widths, etc.).
  1238. *
  1239. * Return value: (element-type Pango.LayoutLine) (transfer none): a #GSList containing
  1240. * the lines in the layout. This points to internal data of the #PangoLayout
  1241. * and must be used with care. It will become invalid on any change to the layout's
  1242. * text or properties.
  1243. **/
  1244. GSList *
  1245. pango_layout_get_lines (PangoLayout *layout)
  1246. {
  1247. pango_layout_check_lines (layout);
  1248. if (layout->lines)
  1249. {
  1250. GSList *tmp_list = layout->lines;
  1251. while (tmp_list)
  1252. {
  1253. PangoLayoutLine *line = tmp_list->data;
  1254. tmp_list = tmp_list->next;
  1255. pango_layout_line_leaked (line);
  1256. }
  1257. }
  1258. return layout->lines;
  1259. }
  1260. /**
  1261. * pango_layout_get_lines_readonly:
  1262. * @layout: a #PangoLayout
  1263. *
  1264. * Returns the lines of the @layout as a list.
  1265. *
  1266. * This is a faster alternative to pango_layout_get_lines(),
  1267. * but the user is not expected
  1268. * to modify the contents of the lines (glyphs, glyph widths, etc.).
  1269. *
  1270. * Return value: (element-type Pango.LayoutLine) (transfer none): a #GSList containing
  1271. * the lines in the layout. This points to internal data of the #PangoLayout and
  1272. * must be used with care. It will become invalid on any change to the layout's
  1273. * text or properties. No changes should be made to the lines.
  1274. *
  1275. * Since: 1.16
  1276. **/
  1277. GSList *
  1278. pango_layout_get_lines_readonly (PangoLayout *layout)
  1279. {
  1280. pango_layout_check_lines (layout);
  1281. return layout->lines;
  1282. }
  1283. /**
  1284. * pango_layout_get_line:
  1285. * @layout: a #PangoLayout
  1286. * @line: the index of a line, which must be between 0 and
  1287. * <literal>pango_layout_get_line_count(layout) - 1</literal>, inclusive.
  1288. *
  1289. * Retrieves a particular line from a #PangoLayout.
  1290. *
  1291. * Use the faster pango_layout_get_line_readonly() if you do not plan
  1292. * to modify the contents of the line (glyphs, glyph widths, etc.).
  1293. *
  1294. * Return value: (transfer none) (nullable): the requested
  1295. * #PangoLayoutLine, or %NULL if the index is out of
  1296. * range. This layout line can be ref'ed and retained,
  1297. * but will become invalid if changes are made to the
  1298. * #PangoLayout.
  1299. **/
  1300. PangoLayoutLine *
  1301. pango_layout_get_line (PangoLayout *layout,
  1302. int line)
  1303. {
  1304. GSList *list_item;
  1305. g_return_val_if_fail (layout != NULL, NULL);
  1306. if (line < 0)
  1307. return NULL;
  1308. pango_layout_check_lines (layout);
  1309. list_item = g_slist_nth (layout->lines, line);
  1310. if (list_item)
  1311. {
  1312. PangoLayoutLine *line = list_item->data;
  1313. pango_layout_line_leaked (line);
  1314. return line;
  1315. }
  1316. return NULL;
  1317. }
  1318. /**
  1319. * pango_layout_get_line_readonly:
  1320. * @layout: a #PangoLayout
  1321. * @line: the index of a line, which must be between 0 and
  1322. * <literal>pango_layout_get_line_count(layout) - 1</literal>, inclusive.
  1323. *
  1324. * Retrieves a particular line from a #PangoLayout.
  1325. *
  1326. * This is a faster alternative to pango_layout_get_line(),
  1327. * but the user is not expected
  1328. * to modify the contents of the line (glyphs, glyph widths, etc.).
  1329. *
  1330. * Return value: (transfer none) (nullable): the requested
  1331. * #PangoLayoutLine, or %NULL if the index is out of
  1332. * range. This layout line can be ref'ed and retained,
  1333. * but will become invalid if changes are made to the
  1334. * #PangoLayout. No changes should be made to the line.
  1335. *
  1336. * Since: 1.16
  1337. **/
  1338. PangoLayoutLine *
  1339. pango_layout_get_line_readonly (PangoLayout *layout,
  1340. int line)
  1341. {
  1342. GSList *list_item;
  1343. g_return_val_if_fail (layout != NULL, NULL);
  1344. if (line < 0)
  1345. return NULL;
  1346. pango_layout_check_lines (layout);
  1347. list_item = g_slist_nth (layout->lines, line);
  1348. if (list_item)
  1349. {
  1350. PangoLayoutLine *line = list_item->data;
  1351. return line;
  1352. }
  1353. return NULL;
  1354. }
  1355. /**
  1356. * pango_layout_line_index_to_x:
  1357. * @line: a #PangoLayoutLine
  1358. * @index_: byte offset of a grapheme within the layout
  1359. * @trailing: an integer indicating the edge of the grapheme to retrieve
  1360. * the position of. If > 0, the trailing edge of the grapheme,
  1361. * if 0, the leading of the grapheme.
  1362. * @x_pos: (out): location to store the x_offset (in Pango unit)
  1363. *
  1364. * Converts an index within a line to a X position.
  1365. *
  1366. **/
  1367. void
  1368. pango_layout_line_index_to_x (PangoLayoutLine *line,
  1369. int index,
  1370. int trailing,
  1371. int *x_pos)
  1372. {
  1373. PangoLayout *layout = line->layout;
  1374. GSList *run_list = line->runs;
  1375. int width = 0;
  1376. while (run_list)
  1377. {
  1378. PangoLayoutRun *run = run_list->data;
  1379. ItemProperties properties;
  1380. pango_layout_get_item_properties (run->item, &properties);
  1381. if (run->item->offset <= index && run->item->offset + run->item->length > index)
  1382. {
  1383. int offset = g_utf8_pointer_to_offset (layout->text, layout->text + index);
  1384. if (trailing)
  1385. {
  1386. while (index < line->start_index + line->length &&
  1387. offset + 1 < layout->n_chars &&
  1388. !layout->log_attrs[offset + 1].is_cursor_position)
  1389. {
  1390. offset++;
  1391. index = g_utf8_next_char (layout->text + index) - layout->text;
  1392. }
  1393. }
  1394. else
  1395. {
  1396. while (index > line->start_index &&
  1397. !layout->log_attrs[offset].is_cursor_position)
  1398. {
  1399. offset--;
  1400. index = g_utf8_prev_char (layout->text + index) - layout->text;
  1401. }
  1402. }
  1403. pango_glyph_string_index_to_x (run->glyphs,
  1404. layout->text + run->item->offset,
  1405. run->item->length,
  1406. &run->item->analysis,
  1407. index - run->item->offset, trailing, x_pos);
  1408. if (x_pos)
  1409. *x_pos += width;
  1410. return;
  1411. }
  1412. width += pango_glyph_string_get_width (run->glyphs);
  1413. run_list = run_list->next;
  1414. }
  1415. if (x_pos)
  1416. *x_pos = width;
  1417. }
  1418. static PangoLayoutLine *
  1419. pango_layout_index_to_line (PangoLayout *layout,
  1420. int index,
  1421. int *line_nr,
  1422. PangoLayoutLine **line_before,
  1423. PangoLayoutLine **line_after)
  1424. {
  1425. GSList *tmp_list;
  1426. GSList *line_list;
  1427. PangoLayoutLine *line = NULL;
  1428. PangoLayoutLine *prev_line = NULL;
  1429. int i = -1;
  1430. line_list = tmp_list = layout->lines;
  1431. while (tmp_list)
  1432. {
  1433. PangoLayoutLine *tmp_line = tmp_list->data;
  1434. if (tmp_line->start_index > index)
  1435. break; /* index was in paragraph delimiters */
  1436. prev_line = line;
  1437. line = tmp_line;
  1438. line_list = tmp_list;
  1439. i++;
  1440. if (line->start_index + line->length > index)
  1441. break;
  1442. tmp_list = tmp_list->next;
  1443. }
  1444. if (line_nr)
  1445. *line_nr = i;
  1446. if (line_before)
  1447. *line_before = prev_line;
  1448. if (line_after)
  1449. *line_after = (line_list && line_list->next) ? line_list->next->data : NULL;
  1450. return line;
  1451. }
  1452. static PangoLayoutLine *
  1453. pango_layout_index_to_line_and_extents (PangoLayout *layout,
  1454. int index,
  1455. PangoRectangle *line_rect)
  1456. {
  1457. PangoLayoutIter iter;
  1458. PangoLayoutLine *line = NULL;
  1459. _pango_layout_get_iter (layout, &iter);
  1460. if (!ITER_IS_INVALID (&iter))
  1461. while (TRUE)
  1462. {
  1463. PangoLayoutLine *tmp_line = _pango_layout_iter_get_line (&iter);
  1464. if (tmp_line->start_index > index)
  1465. break; /* index was in paragraph delimiters */
  1466. line = tmp_line;
  1467. pango_layout_iter_get_line_extents (&iter, NULL, line_rect);
  1468. if (line->start_index + line->length > index)
  1469. break;
  1470. if (!pango_layout_iter_next_line (&iter))
  1471. break; /* Use end of last line */
  1472. }
  1473. _pango_layout_iter_destroy (&iter);
  1474. return line;
  1475. }
  1476. /**
  1477. * pango_layout_index_to_line_x:
  1478. * @layout: a #PangoLayout
  1479. * @index_: the byte index of a grapheme within the layout.
  1480. * @trailing: an integer indicating the edge of the grapheme to retrieve the
  1481. * position of. If > 0, the trailing edge of the grapheme, if 0,
  1482. * the leading of the grapheme.
  1483. * @line: (out) (allow-none): location to store resulting line index. (which will
  1484. * between 0 and pango_layout_get_line_count(layout) - 1), or %NULL
  1485. * @x_pos: (out) (allow-none): location to store resulting position within line
  1486. * (%PANGO_SCALE units per device unit), or %NULL
  1487. *
  1488. * Converts from byte @index_ within the @layout to line and X position.
  1489. * (X position is measured from the left edge of the line)
  1490. */
  1491. void
  1492. pango_layout_index_to_line_x (PangoLayout *layout,
  1493. int index,
  1494. gboolean trailing,
  1495. int *line,
  1496. int *x_pos)
  1497. {
  1498. int line_num;
  1499. PangoLayoutLine *layout_line = NULL;
  1500. g_return_if_fail (layout != NULL);
  1501. g_return_if_fail (index >= 0);
  1502. g_return_if_fail (index <= layout->length);
  1503. pango_layout_check_lines (layout);
  1504. layout_line = pango_layout_index_to_line (layout, index,
  1505. &line_num, NULL, NULL);
  1506. if (layout_line)
  1507. {
  1508. /* use end of line if index was in the paragraph delimiters */
  1509. if (index > layout_line->start_index + layout_line->length)
  1510. index = layout_line->start_index + layout_line->length;
  1511. if (line)
  1512. *line = line_num;
  1513. pango_layout_line_index_to_x (layout_line, index, trailing, x_pos);
  1514. }
  1515. else
  1516. {
  1517. if (line)
  1518. *line = -1;
  1519. if (x_pos)
  1520. *x_pos = -1;
  1521. }
  1522. }
  1523. /**
  1524. * pango_layout_move_cursor_visually:
  1525. * @layout: a #PangoLayout.
  1526. * @strong: whether the moving cursor is the strong cursor or the
  1527. * weak cursor. The strong cursor is the cursor corresponding
  1528. * to text insertion in the base direction for the layout.
  1529. * @old_index: the byte index of the grapheme for the old index
  1530. * @old_trailing: if 0, the cursor was at the leading edge of the
  1531. * grapheme indicated by @old_index, if > 0, the cursor
  1532. * was at the trailing edge.
  1533. * @direction: direction to move cursor. A negative
  1534. * value indicates motion to the left.
  1535. * @new_index: (out): location to store the new cursor byte index. A value of -1
  1536. * indicates that the cursor has been moved off the beginning
  1537. * of the layout. A value of %G_MAXINT indicates that
  1538. * the cursor has been moved off the end of the layout.
  1539. * @new_trailing: (out): number of characters to move forward from the
  1540. * location returned for @new_index to get the position
  1541. * where the cursor should be displayed. This allows
  1542. * distinguishing the position at the beginning of one
  1543. * line from the position at the end of the preceding
  1544. * line. @new_index is always on the line where the
  1545. * cursor should be displayed.
  1546. *
  1547. * Computes a new cursor position from an old position and
  1548. * a count of positions to move visually. If @direction is positive,
  1549. * then the new strong cursor position will be one position
  1550. * to the right of the old cursor position. If @direction is negative,
  1551. * then the new strong cursor position will be one position
  1552. * to the left of the old cursor position.
  1553. *
  1554. * In the presence of bidirectional text, the correspondence
  1555. * between logical and visual order will depend on the direction
  1556. * of the current run, and there may be jumps when the cursor
  1557. * is moved off of the end of a run.
  1558. *
  1559. * Motion here is in cursor positions, not in characters, so a
  1560. * single call to pango_layout_move_cursor_visually() may move the
  1561. * cursor over multiple characters when multiple characters combine
  1562. * to form a single grapheme.
  1563. **/
  1564. void
  1565. pango_layout_move_cursor_visually (PangoLayout *layout,
  1566. gboolean strong,
  1567. int old_index,
  1568. int old_trailing,
  1569. int direction,
  1570. int *new_index,
  1571. int *new_trailing)
  1572. {
  1573. PangoLayoutLine *line = NULL;
  1574. PangoLayoutLine *prev_line;
  1575. PangoLayoutLine *next_line;
  1576. int *log2vis_map;
  1577. int *vis2log_map;
  1578. int n_vis;
  1579. int vis_pos, vis_pos_old, log_pos;
  1580. int start_offset;
  1581. gboolean off_start = FALSE;
  1582. gboolean off_end = FALSE;
  1583. g_return_if_fail (layout != NULL);
  1584. g_return_if_fail (old_index >= 0 && old_index <= layout->length);
  1585. g_return_if_fail (old_index < layout->length || old_trailing == 0);
  1586. g_return_if_fail (new_index != NULL);
  1587. g_return_if_fail (new_trailing != NULL);
  1588. direction = (direction >= 0 ? 1 : -1);
  1589. pango_layout_check_lines (layout);
  1590. /* Find the line the old cursor is on */
  1591. line = pango_layout_index_to_line (layout, old_index,
  1592. NULL, &prev_line, &next_line);
  1593. start_offset = g_utf8_pointer_to_offset (layout->text, layout->text + line->start_index);
  1594. while (old_trailing--)
  1595. old_index = g_utf8_next_char (layout->text + old_index) - layout->text;
  1596. log2vis_map = pango_layout_line_get_log2vis_map (line, strong);
  1597. n_vis = pango_utf8_strlen (layout->text + line->start_index, line->length);
  1598. /* Clamp old_index to fit on the line */
  1599. if (old_index > (line->start_index + line->length))
  1600. old_index = line->start_index + line->length;
  1601. vis_pos = log2vis_map[old_index - line->start_index];
  1602. g_free (log2vis_map);
  1603. /* Handling movement between lines */
  1604. if (vis_pos == 0 && direction < 0)
  1605. {
  1606. if (line->resolved_dir == PANGO_DIRECTION_LTR)
  1607. off_start = TRUE;
  1608. else
  1609. off_end = TRUE;
  1610. }
  1611. else if (vis_pos == n_vis && direction > 0)
  1612. {
  1613. if (line->resolved_dir == PANGO_DIRECTION_LTR)
  1614. off_end = TRUE;
  1615. else
  1616. off_start = TRUE;
  1617. }
  1618. if (off_start || off_end)
  1619. {
  1620. /* If we move over a paragraph boundary, count that as
  1621. * an extra position in the motion
  1622. */
  1623. gboolean paragraph_boundary;
  1624. if (off_start)
  1625. {
  1626. if (!prev_line)
  1627. {
  1628. *new_index = -1;
  1629. *new_trailing = 0;
  1630. return;
  1631. }
  1632. line = prev_line;
  1633. paragraph_boundary = (line->start_index + line->length != old_index);
  1634. }
  1635. else
  1636. {
  1637. if (!next_line)
  1638. {
  1639. *new_index = G_MAXINT;
  1640. *new_trailing = 0;
  1641. return;
  1642. }
  1643. line = next_line;
  1644. paragraph_boundary = (line->start_index != old_index);
  1645. }
  1646. n_vis = pango_utf8_strlen (layout->text + line->start_index, line->length);
  1647. start_offset = g_utf8_pointer_to_offset (layout->text, layout->text + line->start_index);
  1648. if (vis_pos == 0 && direction < 0)
  1649. {
  1650. vis_pos = n_vis;
  1651. if (paragraph_boundary)
  1652. vis_pos++;
  1653. }
  1654. else /* (vis_pos == n_vis && direction > 0) */
  1655. {
  1656. vis_pos = 0;
  1657. if (paragraph_boundary)
  1658. vis_pos--;
  1659. }
  1660. }
  1661. vis2log_map = pango_layout_line_get_vis2log_map (line, strong);
  1662. vis_pos_old = vis_pos + direction;
  1663. log_pos = g_utf8_pointer_to_offset (layout->text + line->start_index,
  1664. layout->text + line->start_index + vis2log_map[vis_pos_old]);
  1665. do
  1666. {
  1667. vis_pos += direction;
  1668. log_pos += g_utf8_pointer_to_offset (layout->text + line->start_index + vis2log_map[vis_pos_old],
  1669. layout->text + line->start_index + vis2log_map[vis_pos]);
  1670. vis_pos_old = vis_pos;
  1671. }
  1672. while (vis_pos > 0 && vis_pos < n_vis &&
  1673. !layout->log_attrs[start_offset + log_pos].is_cursor_position);
  1674. *new_index = line->start_index + vis2log_map[vis_pos];
  1675. g_free (vis2log_map);
  1676. *new_trailing = 0;
  1677. if (*new_index == line->start_index + line->length && line->length > 0)
  1678. {
  1679. do
  1680. {
  1681. log_pos--;
  1682. *new_index = g_utf8_prev_char (layout->text + *new_index) - layout->text;
  1683. (*new_trailing)++;
  1684. }
  1685. while (log_pos > 0 && !layout->log_attrs[start_offset + log_pos].is_cursor_position);
  1686. }
  1687. }
  1688. /**
  1689. * pango_layout_xy_to_index:
  1690. * @layout: a #PangoLayout
  1691. * @x: the X offset (in Pango units)
  1692. * from the left edge of the layout.
  1693. * @y: the Y offset (in Pango units)
  1694. * from the top edge of the layout
  1695. * @index_: (out): location to store calculated byte index
  1696. * @trailing: (out): location to store a integer indicating where
  1697. * in the grapheme the user clicked. It will either
  1698. * be zero, or the number of characters in the
  1699. * grapheme. 0 represents the leading edge of the grapheme.
  1700. *
  1701. * Converts from X and Y position within a layout to the byte
  1702. * index to the character at that logical position. If the
  1703. * Y position is not inside the layout, the closest position is chosen
  1704. * (the position will be clamped inside the layout). If the
  1705. * X position is not within the layout, then the start or the
  1706. * end of the line is chosen as described for pango_layout_line_x_to_index().
  1707. * If either the X or Y positions were not inside the layout, then the
  1708. * function returns %FALSE; on an exact hit, it returns %TRUE.
  1709. *
  1710. * Return value: %TRUE if the coordinates were inside text, %FALSE otherwise.
  1711. **/
  1712. gboolean
  1713. pango_layout_xy_to_index (PangoLayout *layout,
  1714. int x,
  1715. int y,
  1716. int *index,
  1717. gint *trailing)
  1718. {
  1719. PangoLayoutIter iter;
  1720. PangoLayoutLine *prev_line = NULL;
  1721. PangoLayoutLine *found = NULL;
  1722. int found_line_x = 0;
  1723. int prev_last = 0;
  1724. int prev_line_x = 0;
  1725. gboolean retval = FALSE;
  1726. gboolean outside = FALSE;
  1727. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
  1728. _pango_layout_get_iter (layout, &iter);
  1729. do
  1730. {
  1731. PangoRectangle line_logical;
  1732. int first_y, last_y;
  1733. pango_layout_iter_get_line_extents (&iter, NULL, &line_logical);
  1734. pango_layout_iter_get_line_yrange (&iter, &first_y, &last_y);
  1735. if (y < first_y)
  1736. {
  1737. if (prev_line && y < (prev_last + (first_y - prev_last) / 2))
  1738. {
  1739. found = prev_line;
  1740. found_line_x = prev_line_x;
  1741. }
  1742. else
  1743. {
  1744. if (prev_line == NULL)
  1745. outside = TRUE; /* off the top */
  1746. found = _pango_layout_iter_get_line (&iter);
  1747. found_line_x = x - line_logical.x;
  1748. }
  1749. }
  1750. else if (y >= first_y &&
  1751. y < last_y)
  1752. {
  1753. found = _pango_layout_iter_get_line (&iter);
  1754. found_line_x = x - line_logical.x;
  1755. }
  1756. prev_line = _pango_layout_iter_get_line (&iter);
  1757. prev_last = last_y;
  1758. prev_line_x = x - line_logical.x;
  1759. if (found != NULL)
  1760. break;
  1761. }
  1762. while (pango_layout_iter_next_line (&iter));
  1763. _pango_layout_iter_destroy (&iter);
  1764. if (found == NULL)
  1765. {
  1766. /* Off the bottom of the layout */
  1767. outside = TRUE;
  1768. found = prev_line;
  1769. found_line_x = prev_line_x;
  1770. }
  1771. retval = pango_layout_line_x_to_index (found,
  1772. found_line_x,
  1773. index, trailing);
  1774. if (outside)
  1775. retval = FALSE;
  1776. return retval;
  1777. }
  1778. /**
  1779. * pango_layout_index_to_pos:
  1780. * @layout: a #PangoLayout
  1781. * @index_: byte index within @layout
  1782. * @pos: (out): rectangle in which to store the position of the grapheme
  1783. *
  1784. * Converts from an index within a #PangoLayout to the onscreen position
  1785. * corresponding to the grapheme at that index, which is represented
  1786. * as rectangle. Note that <literal>pos->x</literal> is always the leading
  1787. * edge of the grapheme and <literal>pos->x + pos->width</literal> the trailing
  1788. * edge of the grapheme. If the directionality of the grapheme is right-to-left,
  1789. * then <literal>pos->width</literal> will be negative.
  1790. **/
  1791. void
  1792. pango_layout_index_to_pos (PangoLayout *layout,
  1793. int index,
  1794. PangoRectangle *pos)
  1795. {
  1796. PangoRectangle logical_rect;
  1797. PangoLayoutIter iter;
  1798. PangoLayoutLine *layout_line = NULL;
  1799. int x_pos;
  1800. g_return_if_fail (layout != NULL);
  1801. g_return_if_fail (index >= 0);
  1802. g_return_if_fail (pos != NULL);
  1803. _pango_layout_get_iter (layout, &iter);
  1804. if (!ITER_IS_INVALID (&iter))
  1805. {
  1806. while (TRUE)
  1807. {
  1808. PangoLayoutLine *tmp_line = _pango_layout_iter_get_line (&iter);
  1809. if (tmp_line->start_index > index)
  1810. {
  1811. /* index is in the paragraph delim&iters, move to
  1812. * end of previous line
  1813. *
  1814. * This shouldn’t occur in the first loop &iteration as the first
  1815. * line’s start_index should always be 0.
  1816. */
  1817. g_assert (layout_line != NULL);
  1818. index = layout_line->start_index + layout_line->length;
  1819. break;
  1820. }
  1821. layout_line = tmp_line;
  1822. pango_layout_iter_get_line_extents (&iter, NULL, &logical_rect);
  1823. if (layout_line->start_index + layout_line->length > index)
  1824. break;
  1825. if (!pango_layout_iter_next_line (&iter))
  1826. {
  1827. index = layout_line->start_index + layout_line->length;
  1828. break;
  1829. }
  1830. }
  1831. pos->y = logical_rect.y;
  1832. pos->height = logical_rect.height;
  1833. pango_layout_line_index_to_x (layout_line, index, 0, &x_pos);
  1834. pos->x = logical_rect.x + x_pos;
  1835. if (index < layout_line->start_index + layout_line->length)
  1836. {
  1837. pango_layout_line_index_to_x (layout_line, index, 1, &x_pos);
  1838. pos->width = (logical_rect.x + x_pos) - pos->x;
  1839. }
  1840. else
  1841. pos->width = 0;
  1842. }
  1843. _pango_layout_iter_destroy (&iter);
  1844. }
  1845. static void
  1846. pango_layout_line_get_range (PangoLayoutLine *line,
  1847. char **start,
  1848. char **end)
  1849. {
  1850. char *p;
  1851. p = line->layout->text + line->start_index;
  1852. if (start)
  1853. *start = p;
  1854. if (end)
  1855. *end = p + line->length;
  1856. }
  1857. static int *
  1858. pango_layout_line_get_vis2log_map (PangoLayoutLine *line,
  1859. gboolean strong)
  1860. {
  1861. PangoLayout *layout = line->layout;
  1862. PangoDirection prev_dir;
  1863. PangoDirection cursor_dir;
  1864. GSList *tmp_list;
  1865. gchar *start, *end;
  1866. int *result;
  1867. int pos;
  1868. int n_chars;
  1869. pango_layout_line_get_range (line, &start, &end);
  1870. n_chars = pango_utf8_strlen (start, end - start);
  1871. result = g_new (int, n_chars + 1);
  1872. if (strong)
  1873. cursor_dir = line->resolved_dir;
  1874. else
  1875. cursor_dir = (line->resolved_dir == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
  1876. /* Handle the first visual position
  1877. */
  1878. if (line->resolved_dir == cursor_dir)
  1879. result[0] = line->resolved_dir == PANGO_DIRECTION_LTR ? 0 : end - start;
  1880. prev_dir = line->resolved_dir;
  1881. pos = 0;
  1882. tmp_list = line->runs;
  1883. while (tmp_list)
  1884. {
  1885. PangoLayoutRun *run = tmp_list->data;
  1886. int run_n_chars = run->item->num_chars;
  1887. PangoDirection run_dir = (run->item->analysis.level % 2) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
  1888. char *p = layout->text + run->item->offset;
  1889. int i;
  1890. /* pos is the visual position at the start of the run */
  1891. /* p is the logical byte index at the start of the run */
  1892. if (run_dir == PANGO_DIRECTION_LTR)
  1893. {
  1894. if ((cursor_dir == PANGO_DIRECTION_LTR) ||
  1895. (prev_dir == run_dir))
  1896. result[pos] = p - start;
  1897. p = g_utf8_next_char (p);
  1898. for (i = 1; i < run_n_chars; i++)
  1899. {
  1900. result[pos + i] = p - start;
  1901. p = g_utf8_next_char (p);
  1902. }
  1903. if (cursor_dir == PANGO_DIRECTION_LTR)
  1904. result[pos + run_n_chars] = p - start;
  1905. }
  1906. else
  1907. {
  1908. if (cursor_dir == PANGO_DIRECTION_RTL)
  1909. result[pos + run_n_chars] = p - start;
  1910. p = g_utf8_next_char (p);
  1911. for (i = 1; i < run_n_chars; i++)
  1912. {
  1913. result[pos + run_n_chars - i] = p - start;
  1914. p = g_utf8_next_char (p);
  1915. }
  1916. if ((cursor_dir == PANGO_DIRECTION_RTL) ||
  1917. (prev_dir == run_dir))
  1918. result[pos] = p - start;
  1919. }
  1920. pos += run_n_chars;
  1921. prev_dir = run_dir;
  1922. tmp_list = tmp_list->next;
  1923. }
  1924. /* And the last visual position
  1925. */
  1926. if ((cursor_dir == line->resolved_dir) || (prev_dir == line->resolved_dir))
  1927. result[pos] = line->resolved_dir == PANGO_DIRECTION_LTR ? end - start : 0;
  1928. return result;
  1929. }
  1930. static int *
  1931. pango_layout_line_get_log2vis_map (PangoLayoutLine *line,
  1932. gboolean strong)
  1933. {
  1934. gchar *start, *end;
  1935. int *reverse_map;
  1936. int *result;
  1937. int i;
  1938. int n_chars;
  1939. pango_layout_line_get_range (line, &start, &end);
  1940. n_chars = pango_utf8_strlen (start, end - start);
  1941. result = g_new0 (int, end - start + 1);
  1942. reverse_map = pango_layout_line_get_vis2log_map (line, strong);
  1943. for (i=0; i <= n_chars; i++)
  1944. result[reverse_map[i]] = i;
  1945. g_free (reverse_map);
  1946. return result;
  1947. }
  1948. static PangoDirection
  1949. pango_layout_line_get_char_direction (PangoLayoutLine *layout_line,
  1950. int index)
  1951. {
  1952. GSList *run_list;
  1953. run_list = layout_line->runs;
  1954. while (run_list)
  1955. {
  1956. PangoLayoutRun *run = run_list->data;
  1957. if (run->item->offset <= index && run->item->offset + run->item->length > index)
  1958. return run->item->analysis.level % 2 ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
  1959. run_list = run_list->next;
  1960. }
  1961. g_assert_not_reached ();
  1962. return PANGO_DIRECTION_LTR;
  1963. }
  1964. /**
  1965. * pango_layout_get_cursor_pos:
  1966. * @layout: a #PangoLayout
  1967. * @index_: the byte index of the cursor
  1968. * @strong_pos: (out) (allow-none): location to store the strong cursor position
  1969. * (may be %NULL)
  1970. * @weak_pos: (out) (allow-none): location to store the weak cursor position (may be %NULL)
  1971. *
  1972. * Given an index within a layout, determines the positions that of the
  1973. * strong and weak cursors if the insertion point is at that
  1974. * index. The position of each cursor is stored as a zero-width
  1975. * rectangle. The strong cursor location is the location where
  1976. * characters of the directionality equal to the base direction of the
  1977. * layout are inserted. The weak cursor location is the location
  1978. * where characters of the directionality opposite to the base
  1979. * direction of the layout are inserted.
  1980. **/
  1981. void
  1982. pango_layout_get_cursor_pos (PangoLayout *layout,
  1983. int index,
  1984. PangoRectangle *strong_pos,
  1985. PangoRectangle *weak_pos)
  1986. {
  1987. PangoDirection dir1;
  1988. PangoRectangle line_rect;
  1989. PangoLayoutLine *layout_line = NULL; /* Quiet GCC */
  1990. int x1_trailing;
  1991. int x2;
  1992. g_return_if_fail (layout != NULL);
  1993. g_return_if_fail (index >= 0 && index <= layout->length);
  1994. layout_line = pango_layout_index_to_line_and_extents (layout, index,
  1995. &line_rect);
  1996. g_assert (index >= layout_line->start_index);
  1997. /* Examine the trailing edge of the character before the cursor */
  1998. if (index == layout_line->start_index)
  1999. {
  2000. dir1 = layout_line->resolved_dir;
  2001. if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
  2002. x1_trailing = 0;
  2003. else
  2004. x1_trailing = line_rect.width;
  2005. }
  2006. else if (index >= layout_line->start_index + layout_line->length)
  2007. {
  2008. dir1 = layout_line->resolved_dir;
  2009. if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
  2010. x1_trailing = line_rect.width;
  2011. else
  2012. x1_trailing = 0;
  2013. }
  2014. else
  2015. {
  2016. gint prev_index = g_utf8_prev_char (layout->text + index) - layout->text;
  2017. dir1 = pango_layout_line_get_char_direction (layout_line, prev_index);
  2018. pango_layout_line_index_to_x (layout_line, prev_index, TRUE, &x1_trailing);
  2019. }
  2020. /* Examine the leading edge of the character after the cursor */
  2021. if (index >= layout_line->start_index + layout_line->length)
  2022. {
  2023. if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
  2024. x2 = line_rect.width;
  2025. else
  2026. x2 = 0;
  2027. }
  2028. else
  2029. {
  2030. pango_layout_line_index_to_x (layout_line, index, FALSE, &x2);
  2031. }
  2032. if (strong_pos)
  2033. {
  2034. strong_pos->x = line_rect.x;
  2035. if (dir1 == layout_line->resolved_dir)
  2036. strong_pos->x += x1_trailing;
  2037. else
  2038. strong_pos->x += x2;
  2039. strong_pos->y = line_rect.y;
  2040. strong_pos->width = 0;
  2041. strong_pos->height = line_rect.height;
  2042. }
  2043. if (weak_pos)
  2044. {
  2045. weak_pos->x = line_rect.x;
  2046. if (dir1 == layout_line->resolved_dir)
  2047. weak_pos->x += x2;
  2048. else
  2049. weak_pos->x += x1_trailing;
  2050. weak_pos->y = line_rect.y;
  2051. weak_pos->width = 0;
  2052. weak_pos->height = line_rect.height;
  2053. }
  2054. }
  2055. static inline int
  2056. direction_simple (PangoDirection d)
  2057. {
  2058. switch (d)
  2059. {
  2060. case PANGO_DIRECTION_LTR :
  2061. case PANGO_DIRECTION_WEAK_LTR :
  2062. case PANGO_DIRECTION_TTB_RTL :
  2063. return 1;
  2064. case PANGO_DIRECTION_RTL :
  2065. case PANGO_DIRECTION_WEAK_RTL :
  2066. case PANGO_DIRECTION_TTB_LTR :
  2067. return -1;
  2068. case PANGO_DIRECTION_NEUTRAL :
  2069. return 0;
  2070. /* no default, compiler should complain if a new values is added */
  2071. }
  2072. /* not reached */
  2073. return 0;
  2074. }
  2075. static PangoAlignment
  2076. get_alignment (PangoLayout *layout,
  2077. PangoLayoutLine *line)
  2078. {
  2079. PangoAlignment alignment = layout->alignment;
  2080. if (alignment != PANGO_ALIGN_CENTER && line->layout->auto_dir &&
  2081. direction_simple (line->resolved_dir) ==
  2082. -direction_simple (pango_context_get_base_dir (layout->context)))
  2083. {
  2084. if (alignment == PANGO_ALIGN_LEFT)
  2085. alignment = PANGO_ALIGN_RIGHT;
  2086. else if (alignment == PANGO_ALIGN_RIGHT)
  2087. alignment = PANGO_ALIGN_LEFT;
  2088. }
  2089. return alignment;
  2090. }
  2091. static void
  2092. get_x_offset (PangoLayout *layout,
  2093. PangoLayoutLine *line,
  2094. int layout_width,
  2095. int line_width,
  2096. int *x_offset)
  2097. {
  2098. PangoAlignment alignment = get_alignment (layout, line);
  2099. /* Alignment */
  2100. if (layout_width == 0)
  2101. *x_offset = 0;
  2102. else if (alignment == PANGO_ALIGN_RIGHT)
  2103. *x_offset = layout_width - line_width;
  2104. else if (alignment == PANGO_ALIGN_CENTER) {
  2105. *x_offset = (layout_width - line_width) / 2;
  2106. /* hinting */
  2107. if (((layout_width | line_width) & (PANGO_SCALE - 1)) == 0)
  2108. {
  2109. *x_offset = PANGO_UNITS_ROUND (*x_offset);
  2110. }
  2111. } else
  2112. *x_offset = 0;
  2113. /* Indentation */
  2114. /* For center, we ignore indentation; I think I've seen word
  2115. * processors that still do the indentation here as if it were
  2116. * indented left/right, though we can't sensibly do that without
  2117. * knowing whether left/right is the "normal" thing for this text
  2118. */
  2119. if (alignment == PANGO_ALIGN_CENTER)
  2120. return;
  2121. if (line->is_paragraph_start)
  2122. {
  2123. if (layout->indent > 0)
  2124. {
  2125. if (alignment == PANGO_ALIGN_LEFT)
  2126. *x_offset += layout->indent;
  2127. else
  2128. *x_offset -= layout->indent;
  2129. }
  2130. }
  2131. else
  2132. {
  2133. if (layout->indent < 0)
  2134. {
  2135. if (alignment == PANGO_ALIGN_LEFT)
  2136. *x_offset -= layout->indent;
  2137. else
  2138. *x_offset += layout->indent;
  2139. }
  2140. }
  2141. }
  2142. static void
  2143. get_line_extents_layout_coords (PangoLayout *layout,
  2144. PangoLayoutLine *line,
  2145. int layout_width,
  2146. int y_offset,
  2147. int *baseline,
  2148. PangoRectangle *line_ink_layout,
  2149. PangoRectangle *line_logical_layout)
  2150. {
  2151. int x_offset;
  2152. /* Line extents in line coords (origin at line baseline) */
  2153. PangoRectangle line_ink;
  2154. PangoRectangle line_logical;
  2155. pango_layout_line_get_extents (line, line_ink_layout ? &line_ink : NULL,
  2156. &line_logical);
  2157. get_x_offset (layout, line, layout_width, line_logical.width, &x_offset);
  2158. /* Convert the line's extents into layout coordinates */
  2159. if (line_ink_layout)
  2160. {
  2161. *line_ink_layout = line_ink;
  2162. line_ink_layout->x = line_ink.x + x_offset;
  2163. line_ink_layout->y = y_offset - line_logical.y + line_ink.y;
  2164. }
  2165. if (line_logical_layout)
  2166. {
  2167. *line_logical_layout = line_logical;
  2168. line_logical_layout->x = line_logical.x + x_offset;
  2169. line_logical_layout->y = y_offset;
  2170. }
  2171. if (baseline)
  2172. *baseline = y_offset - line_logical.y;
  2173. }
  2174. /* if non-NULL line_extents returns a list of line extents
  2175. * in layout coordinates
  2176. */
  2177. static void
  2178. pango_layout_get_extents_internal (PangoLayout *layout,
  2179. PangoRectangle *ink_rect,
  2180. PangoRectangle *logical_rect,
  2181. Extents **line_extents)
  2182. {
  2183. GSList *line_list;
  2184. int y_offset = 0;
  2185. int width;
  2186. gboolean need_width = FALSE;
  2187. int line_index = 0;
  2188. g_return_if_fail (layout != NULL);
  2189. pango_layout_check_lines (layout);
  2190. if (ink_rect && layout->ink_rect_cached)
  2191. {
  2192. *ink_rect = layout->ink_rect;
  2193. ink_rect = NULL;
  2194. }
  2195. if (logical_rect && layout->logical_rect_cached)
  2196. {
  2197. *logical_rect = layout->logical_rect;
  2198. logical_rect = NULL;
  2199. }
  2200. if (!ink_rect && !logical_rect && !line_extents)
  2201. return;
  2202. /* When we are not wrapping, we need the overall width of the layout to
  2203. * figure out the x_offsets of each line. However, we only need the
  2204. * x_offsets if we are computing the ink_rect or individual line extents.
  2205. */
  2206. width = layout->width;
  2207. if (layout->auto_dir)
  2208. {
  2209. /* If one of the lines of the layout is not left aligned, then we need
  2210. * the width of the layout to calculate line x-offsets; this requires
  2211. * looping through the lines for layout->auto_dir.
  2212. */
  2213. line_list = layout->lines;
  2214. while (line_list && !need_width)
  2215. {
  2216. PangoLayoutLine *line = line_list->data;
  2217. if (get_alignment (layout, line) != PANGO_ALIGN_LEFT)
  2218. need_width = TRUE;
  2219. line_list = line_list->next;
  2220. }
  2221. }
  2222. else if (layout->alignment != PANGO_ALIGN_LEFT)
  2223. need_width = TRUE;
  2224. if (width == -1 && need_width && (ink_rect || line_extents))
  2225. {
  2226. PangoRectangle overall_logical;
  2227. pango_layout_get_extents_internal (layout, NULL, &overall_logical, NULL);
  2228. width = overall_logical.width;
  2229. }
  2230. if (logical_rect)
  2231. {
  2232. logical_rect->x = 0;
  2233. logical_rect->y = 0;
  2234. logical_rect->width = 0;
  2235. logical_rect->height = 0;
  2236. }
  2237. if (line_extents && layout->line_count > 0)
  2238. {
  2239. *line_extents = g_malloc (sizeof (Extents) * layout->line_count);
  2240. }
  2241. line_list = layout->lines;
  2242. while (line_list)
  2243. {
  2244. PangoLayoutLine *line = line_list->data;
  2245. /* Line extents in layout coords (origin at 0,0 of the layout) */
  2246. PangoRectangle line_ink_layout;
  2247. PangoRectangle line_logical_layout;
  2248. int new_pos;
  2249. /* This block gets the line extents in layout coords */
  2250. {
  2251. int baseline;
  2252. get_line_extents_layout_coords (layout, line,
  2253. width, y_offset,
  2254. &baseline,
  2255. ink_rect ? &line_ink_layout : NULL,
  2256. &line_logical_layout);
  2257. if (line_extents && layout->line_count > 0)
  2258. {
  2259. Extents *ext = &(*line_extents)[line_index];
  2260. ext->baseline = baseline;
  2261. ext->ink_rect = line_ink_layout;
  2262. ext->logical_rect = line_logical_layout;
  2263. }
  2264. }
  2265. if (ink_rect)
  2266. {
  2267. /* Compute the union of the current ink_rect with
  2268. * line_ink_layout
  2269. */
  2270. if (line_list == layout->lines)
  2271. {
  2272. *ink_rect = line_ink_layout;
  2273. }
  2274. else
  2275. {
  2276. new_pos = MIN (ink_rect->x, line_ink_layout.x);
  2277. ink_rect->width =
  2278. MAX (ink_rect->x + ink_rect->width,
  2279. line_ink_layout.x + line_ink_layout.width) - new_pos;
  2280. ink_rect->x = new_pos;
  2281. new_pos = MIN (ink_rect->y, line_ink_layout.y);
  2282. ink_rect->height =
  2283. MAX (ink_rect->y + ink_rect->height,
  2284. line_ink_layout.y + line_ink_layout.height) - new_pos;
  2285. ink_rect->y = new_pos;
  2286. }
  2287. }
  2288. if (logical_rect)
  2289. {
  2290. if (layout->width == -1)
  2291. {
  2292. /* When no width is set on layout, we can just compute the max of the
  2293. * line lengths to get the horizontal extents ... logical_rect.x = 0.
  2294. */
  2295. logical_rect->width = MAX (logical_rect->width, line_logical_layout.width);
  2296. }
  2297. else
  2298. {
  2299. /* When a width is set, we have to compute the union of the horizontal
  2300. * extents of all the lines.
  2301. */
  2302. if (line_list == layout->lines)
  2303. {
  2304. logical_rect->x = line_logical_layout.x;
  2305. logical_rect->width = line_logical_layout.width;
  2306. }
  2307. else
  2308. {
  2309. new_pos = MIN (logical_rect->x, line_logical_layout.x);
  2310. logical_rect->width =
  2311. MAX (logical_rect->x + logical_rect->width,
  2312. line_logical_layout.x + line_logical_layout.width) - new_pos;
  2313. logical_rect->x = new_pos;
  2314. }
  2315. }
  2316. logical_rect->height += line_logical_layout.height;
  2317. /* No space after the last line, of course. */
  2318. if (line_list->next != NULL)
  2319. logical_rect->height += layout->spacing;
  2320. }
  2321. y_offset += line_logical_layout.height + layout->spacing;
  2322. line_list = line_list->next;
  2323. line_index ++;
  2324. }
  2325. if (ink_rect)
  2326. {
  2327. layout->ink_rect = *ink_rect;
  2328. layout->ink_rect_cached = TRUE;
  2329. }
  2330. if (logical_rect)
  2331. {
  2332. layout->logical_rect = *logical_rect;
  2333. layout->logical_rect_cached = TRUE;
  2334. }
  2335. }
  2336. /**
  2337. * pango_layout_get_extents:
  2338. * @layout: a #PangoLayout
  2339. * @ink_rect: (out) (allow-none): rectangle used to store the extents of the
  2340. * layout as drawn or %NULL to indicate that the result is
  2341. * not needed.
  2342. * @logical_rect: (out) (allow-none):rectangle used to store the logical
  2343. * extents of the layout or %NULL to indicate that the
  2344. * result is not needed.
  2345. *
  2346. * Computes the logical and ink extents of @layout. Logical extents
  2347. * are usually what you want for positioning things. Note that both extents
  2348. * may have non-zero x and y. You may want to use those to offset where you
  2349. * render the layout. Not doing that is a very typical bug that shows up as
  2350. * right-to-left layouts not being correctly positioned in a layout with
  2351. * a set width.
  2352. *
  2353. * The extents are given in layout coordinates and in Pango units; layout
  2354. * coordinates begin at the top left corner of the layout.
  2355. */
  2356. void
  2357. pango_layout_get_extents (PangoLayout *layout,
  2358. PangoRectangle *ink_rect,
  2359. PangoRectangle *logical_rect)
  2360. {
  2361. g_return_if_fail (layout != NULL);
  2362. pango_layout_get_extents_internal (layout, ink_rect, logical_rect, NULL);
  2363. }
  2364. /**
  2365. * pango_layout_get_pixel_extents:
  2366. * @layout: a #PangoLayout
  2367. * @ink_rect: (out) (allow-none): rectangle used to store the extents of the
  2368. * layout as drawn or %NULL to indicate that the result is
  2369. * not needed.
  2370. * @logical_rect: (out) (allow-none): rectangle used to store the logical
  2371. * extents of the layout or %NULL to indicate that the
  2372. * result is not needed.
  2373. *
  2374. * Computes the logical and ink extents of @layout in device units.
  2375. * This function just calls pango_layout_get_extents() followed by
  2376. * two pango_extents_to_pixels() calls, rounding @ink_rect and @logical_rect
  2377. * such that the rounded rectangles fully contain the unrounded one (that is,
  2378. * passes them as first argument to pango_extents_to_pixels()).
  2379. **/
  2380. void
  2381. pango_layout_get_pixel_extents (PangoLayout *layout,
  2382. PangoRectangle *ink_rect,
  2383. PangoRectangle *logical_rect)
  2384. {
  2385. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  2386. pango_layout_get_extents (layout, ink_rect, logical_rect);
  2387. pango_extents_to_pixels (ink_rect, NULL);
  2388. pango_extents_to_pixels (logical_rect, NULL);
  2389. }
  2390. /**
  2391. * pango_layout_get_size:
  2392. * @layout: a #PangoLayout
  2393. * @width: (out) (allow-none): location to store the logical width, or %NULL
  2394. * @height: (out) (allow-none): location to store the logical height, or %NULL
  2395. *
  2396. * Determines the logical width and height of a #PangoLayout
  2397. * in Pango units (device units scaled by %PANGO_SCALE). This
  2398. * is simply a convenience function around pango_layout_get_extents().
  2399. **/
  2400. void
  2401. pango_layout_get_size (PangoLayout *layout,
  2402. int *width,
  2403. int *height)
  2404. {
  2405. PangoRectangle logical_rect;
  2406. pango_layout_get_extents (layout, NULL, &logical_rect);
  2407. if (width)
  2408. *width = logical_rect.width;
  2409. if (height)
  2410. *height = logical_rect.height;
  2411. }
  2412. /**
  2413. * pango_layout_get_pixel_size:
  2414. * @layout: a #PangoLayout
  2415. * @width: (out) (allow-none): location to store the logical width, or %NULL
  2416. * @height: (out) (allow-none): location to store the logical height, or %NULL
  2417. *
  2418. * Determines the logical width and height of a #PangoLayout
  2419. * in device units. (pango_layout_get_size() returns the width
  2420. * and height scaled by %PANGO_SCALE.) This
  2421. * is simply a convenience function around
  2422. * pango_layout_get_pixel_extents().
  2423. **/
  2424. void
  2425. pango_layout_get_pixel_size (PangoLayout *layout,
  2426. int *width,
  2427. int *height)
  2428. {
  2429. PangoRectangle logical_rect;
  2430. pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
  2431. if (width)
  2432. *width = logical_rect.width;
  2433. if (height)
  2434. *height = logical_rect.height;
  2435. }
  2436. /**
  2437. * pango_layout_get_baseline:
  2438. * @layout: a #PangoLayout
  2439. *
  2440. * Gets the Y position of baseline of the first line in @layout.
  2441. *
  2442. * Return value: baseline of first line, from top of @layout.
  2443. *
  2444. * Since: 1.22
  2445. **/
  2446. int
  2447. pango_layout_get_baseline (PangoLayout *layout)
  2448. {
  2449. int baseline;
  2450. Extents *extents = NULL;
  2451. /* XXX this is kinda inefficient */
  2452. pango_layout_get_extents_internal (layout, NULL, NULL, &extents);
  2453. baseline = extents ? extents[0].baseline : 0;
  2454. g_free (extents);
  2455. return baseline;
  2456. }
  2457. static void
  2458. pango_layout_clear_lines (PangoLayout *layout)
  2459. {
  2460. if (layout->lines)
  2461. {
  2462. GSList *tmp_list = layout->lines;
  2463. while (tmp_list)
  2464. {
  2465. PangoLayoutLine *line = tmp_list->data;
  2466. tmp_list = tmp_list->next;
  2467. line->layout = NULL;
  2468. pango_layout_line_unref (line);
  2469. }
  2470. g_slist_free (layout->lines);
  2471. layout->lines = NULL;
  2472. layout->line_count = 0;
  2473. /* This could be handled separately, since we don't need to
  2474. * recompute log_attrs on a width change, but this is easiest
  2475. */
  2476. g_free (layout->log_attrs);
  2477. layout->log_attrs = NULL;
  2478. }
  2479. layout->unknown_glyphs_count = -1;
  2480. layout->logical_rect_cached = FALSE;
  2481. layout->ink_rect_cached = FALSE;
  2482. layout->is_ellipsized = FALSE;
  2483. layout->is_wrapped = FALSE;
  2484. }
  2485. static void
  2486. pango_layout_line_leaked (PangoLayoutLine *line)
  2487. {
  2488. PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
  2489. private->cache_status = LEAKED;
  2490. if (line->layout)
  2491. {
  2492. line->layout->logical_rect_cached = FALSE;
  2493. line->layout->ink_rect_cached = FALSE;
  2494. }
  2495. }
  2496. /*****************
  2497. * Line Breaking *
  2498. *****************/
  2499. static void shape_tab (PangoLayoutLine *line,
  2500. PangoGlyphString *glyphs);
  2501. static void
  2502. free_run (PangoLayoutRun *run, gpointer data)
  2503. {
  2504. gboolean free_item = data != NULL;
  2505. if (free_item)
  2506. pango_item_free (run->item);
  2507. pango_glyph_string_free (run->glyphs);
  2508. g_slice_free (PangoLayoutRun, run);
  2509. }
  2510. static PangoItem *
  2511. uninsert_run (PangoLayoutLine *line)
  2512. {
  2513. PangoLayoutRun *run;
  2514. PangoItem *item;
  2515. GSList *tmp_node = line->runs;
  2516. run = tmp_node->data;
  2517. item = run->item;
  2518. line->runs = tmp_node->next;
  2519. line->length -= item->length;
  2520. g_slist_free_1 (tmp_node);
  2521. free_run (run, (gpointer)FALSE);
  2522. return item;
  2523. }
  2524. static void
  2525. ensure_tab_width (PangoLayout *layout)
  2526. {
  2527. if (layout->tab_width == -1)
  2528. {
  2529. /* Find out how wide 8 spaces are in the context's default
  2530. * font. Utter performance killer. :-(
  2531. */
  2532. PangoGlyphString *glyphs = pango_glyph_string_new ();
  2533. PangoItem *item;
  2534. GList *items;
  2535. PangoAttribute *attr;
  2536. PangoAttrList *layout_attrs;
  2537. PangoAttrList *tmp_attrs;
  2538. PangoAttrIterator *iter;
  2539. PangoFontDescription *font_desc = pango_font_description_copy_static (pango_context_get_font_description (layout->context));
  2540. PangoLanguage *language;
  2541. layout_attrs = pango_layout_get_effective_attributes (layout);
  2542. iter = pango_attr_list_get_iterator (layout_attrs);
  2543. pango_attr_iterator_get_font (iter, font_desc, &language, NULL);
  2544. tmp_attrs = pango_attr_list_new ();
  2545. attr = pango_attr_font_desc_new (font_desc);
  2546. pango_font_description_free (font_desc);
  2547. pango_attr_list_insert_before (tmp_attrs, attr);
  2548. if (language)
  2549. {
  2550. attr = pango_attr_language_new (language);
  2551. pango_attr_list_insert_before (tmp_attrs, attr);
  2552. }
  2553. items = pango_itemize (layout->context, " ", 0, 1, tmp_attrs, NULL);
  2554. pango_attr_iterator_destroy (iter);
  2555. if (layout_attrs != layout->attrs)
  2556. {
  2557. pango_attr_list_unref (layout_attrs);
  2558. layout_attrs = NULL;
  2559. }
  2560. pango_attr_list_unref (tmp_attrs);
  2561. item = items->data;
  2562. pango_shape (" ", 8, &item->analysis, glyphs);
  2563. pango_item_free (item);
  2564. g_list_free (items);
  2565. layout->tab_width = pango_glyph_string_get_width (glyphs);
  2566. pango_glyph_string_free (glyphs);
  2567. /* We need to make sure the tab_width is > 0 so finding tab positions
  2568. * terminates. This check should be necessary only under extreme
  2569. * problems with the font.
  2570. */
  2571. if (layout->tab_width <= 0)
  2572. layout->tab_width = 50 * PANGO_SCALE; /* pretty much arbitrary */
  2573. }
  2574. }
  2575. /* For now we only need the tab position, we assume
  2576. * all tabs are left-aligned.
  2577. */
  2578. static int
  2579. get_tab_pos (PangoLayout *layout, int index, gboolean *is_default)
  2580. {
  2581. gint n_tabs;
  2582. gboolean in_pixels;
  2583. if (layout->tabs)
  2584. {
  2585. n_tabs = pango_tab_array_get_size (layout->tabs);
  2586. in_pixels = pango_tab_array_get_positions_in_pixels (layout->tabs);
  2587. if (is_default)
  2588. *is_default = FALSE;
  2589. }
  2590. else
  2591. {
  2592. n_tabs = 0;
  2593. in_pixels = FALSE;
  2594. if (is_default)
  2595. *is_default = TRUE;
  2596. }
  2597. if (index < n_tabs)
  2598. {
  2599. gint pos = 0;
  2600. pango_tab_array_get_tab (layout->tabs, index, NULL, &pos);
  2601. if (in_pixels)
  2602. return pos * PANGO_SCALE;
  2603. else
  2604. return pos;
  2605. }
  2606. if (n_tabs > 0)
  2607. {
  2608. /* Extrapolate tab position, repeating the last tab gap to
  2609. * infinity.
  2610. */
  2611. int last_pos = 0;
  2612. int next_to_last_pos = 0;
  2613. int tab_width;
  2614. pango_tab_array_get_tab (layout->tabs, n_tabs - 1, NULL, &last_pos);
  2615. if (n_tabs > 1)
  2616. pango_tab_array_get_tab (layout->tabs, n_tabs - 2, NULL, &next_to_last_pos);
  2617. else
  2618. next_to_last_pos = 0;
  2619. if (in_pixels)
  2620. {
  2621. next_to_last_pos *= PANGO_SCALE;
  2622. last_pos *= PANGO_SCALE;
  2623. }
  2624. if (last_pos > next_to_last_pos)
  2625. {
  2626. tab_width = last_pos - next_to_last_pos;
  2627. }
  2628. else
  2629. {
  2630. tab_width = layout->tab_width;
  2631. }
  2632. return last_pos + tab_width * (index - n_tabs + 1);
  2633. }
  2634. else
  2635. {
  2636. /* No tab array set, so use default tab width
  2637. */
  2638. return layout->tab_width * index;
  2639. }
  2640. }
  2641. static int
  2642. line_width (PangoLayoutLine *line)
  2643. {
  2644. GSList *l;
  2645. int i;
  2646. int width = 0;
  2647. /* Compute the width of the line currently - inefficient, but easier
  2648. * than keeping the current width of the line up to date everywhere
  2649. */
  2650. for (l = line->runs; l; l = l->next)
  2651. {
  2652. PangoLayoutRun *run = l->data;
  2653. for (i=0; i < run->glyphs->num_glyphs; i++)
  2654. width += run->glyphs->glyphs[i].geometry.width;
  2655. }
  2656. return width;
  2657. }
  2658. static void
  2659. shape_tab (PangoLayoutLine *line,
  2660. PangoGlyphString *glyphs)
  2661. {
  2662. int i, space_width;
  2663. int current_width = line_width (line);
  2664. pango_glyph_string_set_size (glyphs, 1);
  2665. glyphs->glyphs[0].glyph = PANGO_GLYPH_EMPTY;
  2666. glyphs->glyphs[0].geometry.x_offset = 0;
  2667. glyphs->glyphs[0].geometry.y_offset = 0;
  2668. glyphs->glyphs[0].attr.is_cluster_start = 1;
  2669. glyphs->log_clusters[0] = 0;
  2670. ensure_tab_width (line->layout);
  2671. space_width = line->layout->tab_width / 8;
  2672. for (i=0;;i++)
  2673. {
  2674. gboolean is_default;
  2675. int tab_pos = get_tab_pos (line->layout, i, &is_default);
  2676. /* Make sure there is at least a space-width of space between
  2677. * tab-aligned text and the text before it. However, only do
  2678. * this if no tab array is set on the layout, ie. using default
  2679. * tab positions. If use has set tab positions, respect it to
  2680. * the pixel.
  2681. */
  2682. if (tab_pos >= current_width + (is_default ? space_width : 1))
  2683. {
  2684. glyphs->glyphs[0].geometry.width = tab_pos - current_width;
  2685. break;
  2686. }
  2687. }
  2688. }
  2689. static inline gboolean
  2690. can_break_at (PangoLayout *layout,
  2691. gint offset,
  2692. gboolean always_wrap_char)
  2693. {
  2694. PangoWrapMode wrap;
  2695. /* We probably should have a mode where we treat all white-space as
  2696. * of fungible width - appropriate for typography but not for
  2697. * editing.
  2698. */
  2699. wrap = layout->wrap;
  2700. if (wrap == PANGO_WRAP_WORD_CHAR)
  2701. wrap = always_wrap_char ? PANGO_WRAP_CHAR : PANGO_WRAP_WORD;
  2702. if (offset == layout->n_chars)
  2703. return TRUE;
  2704. else if (wrap == PANGO_WRAP_WORD)
  2705. return layout->log_attrs[offset].is_line_break;
  2706. else if (wrap == PANGO_WRAP_CHAR)
  2707. return layout->log_attrs[offset].is_char_break;
  2708. else
  2709. {
  2710. g_warning (G_STRLOC": broken PangoLayout");
  2711. return TRUE;
  2712. }
  2713. }
  2714. static inline gboolean
  2715. can_break_in (PangoLayout *layout,
  2716. int start_offset,
  2717. int num_chars,
  2718. gboolean allow_break_at_start)
  2719. {
  2720. int i;
  2721. for (i = allow_break_at_start ? 0 : 1; i < num_chars; i++)
  2722. if (can_break_at (layout, start_offset + i, FALSE))
  2723. return TRUE;
  2724. return FALSE;
  2725. }
  2726. static inline void
  2727. distribute_letter_spacing (int letter_spacing,
  2728. int *space_left,
  2729. int *space_right)
  2730. {
  2731. *space_left = letter_spacing / 2;
  2732. /* hinting */
  2733. if ((letter_spacing & (PANGO_SCALE - 1)) == 0)
  2734. {
  2735. *space_left = PANGO_UNITS_ROUND (*space_left);
  2736. }
  2737. *space_right = letter_spacing - *space_left;
  2738. }
  2739. typedef enum
  2740. {
  2741. BREAK_NONE_FIT,
  2742. BREAK_SOME_FIT,
  2743. BREAK_ALL_FIT,
  2744. BREAK_EMPTY_FIT,
  2745. BREAK_LINE_SEPARATOR
  2746. } BreakResult;
  2747. struct _ParaBreakState
  2748. {
  2749. /* maintained per layout */
  2750. int line_height; /* Estimate of height of current line; < 0 is no estimate */
  2751. int remaining_height; /* Remaining height of the layout; only defined if layout->height >= 0 */
  2752. /* maintained per paragraph */
  2753. PangoAttrList *attrs; /* Attributes being used for itemization */
  2754. GList *items; /* This paragraph turned into items */
  2755. PangoDirection base_dir; /* Current resolved base direction */
  2756. gboolean line_of_par; /* Line of the paragraph, starting at 1 for first line */
  2757. PangoGlyphString *glyphs; /* Glyphs for the first item in state->items */
  2758. int start_offset; /* Character offset of first item in state->items in layout->text */
  2759. ItemProperties properties; /* Properties for the first item in state->items */
  2760. int *log_widths; /* Logical widths for first item in state->items.. */
  2761. int log_widths_offset; /* Offset into log_widths to the point corresponding
  2762. * to the remaining portion of the first item */
  2763. int line_start_index; /* Start index (byte offset) of line in layout->text */
  2764. int line_start_offset; /* Character offset of line in layout->text */
  2765. /* maintained per line */
  2766. int line_width; /* Goal width of line currently processing; < 0 is infinite */
  2767. int remaining_width; /* Amount of space remaining on line; < 0 is infinite */
  2768. };
  2769. static gboolean
  2770. should_ellipsize_current_line (PangoLayout *layout,
  2771. ParaBreakState *state);
  2772. static PangoGlyphString *
  2773. shape_run (PangoLayoutLine *line,
  2774. ParaBreakState *state,
  2775. PangoItem *item)
  2776. {
  2777. PangoLayout *layout = line->layout;
  2778. PangoGlyphString *glyphs = pango_glyph_string_new ();
  2779. if (layout->text[item->offset] == '\t')
  2780. shape_tab (line, glyphs);
  2781. else
  2782. {
  2783. if (state->properties.shape_set)
  2784. _pango_shape_shape (layout->text + item->offset, item->num_chars,
  2785. state->properties.shape_ink_rect, state->properties.shape_logical_rect,
  2786. glyphs);
  2787. else
  2788. pango_shape_full (layout->text + item->offset, item->length,
  2789. layout->text, layout->length,
  2790. &item->analysis, glyphs);
  2791. if (state->properties.letter_spacing)
  2792. {
  2793. PangoGlyphItem glyph_item;
  2794. int space_left, space_right;
  2795. glyph_item.item = item;
  2796. glyph_item.glyphs = glyphs;
  2797. pango_glyph_item_letter_space (&glyph_item,
  2798. layout->text,
  2799. layout->log_attrs + state->start_offset,
  2800. state->properties.letter_spacing);
  2801. distribute_letter_spacing (state->properties.letter_spacing, &space_left, &space_right);
  2802. glyphs->glyphs[0].geometry.width += space_left;
  2803. glyphs->glyphs[0].geometry.x_offset += space_left;
  2804. glyphs->glyphs[glyphs->num_glyphs - 1].geometry.width += space_right;
  2805. }
  2806. }
  2807. return glyphs;
  2808. }
  2809. static void
  2810. insert_run (PangoLayoutLine *line,
  2811. ParaBreakState *state,
  2812. PangoItem *run_item,
  2813. gboolean last_run)
  2814. {
  2815. PangoLayoutRun *run = g_slice_new (PangoLayoutRun);
  2816. run->item = run_item;
  2817. if (last_run && state->log_widths_offset == 0)
  2818. run->glyphs = state->glyphs;
  2819. else
  2820. run->glyphs = shape_run (line, state, run_item);
  2821. if (last_run)
  2822. {
  2823. if (state->log_widths_offset > 0)
  2824. pango_glyph_string_free (state->glyphs);
  2825. state->glyphs = NULL;
  2826. g_free (state->log_widths);
  2827. state->log_widths = NULL;
  2828. }
  2829. line->runs = g_slist_prepend (line->runs, run);
  2830. line->length += run_item->length;
  2831. }
  2832. #if 0
  2833. # define DEBUG debug
  2834. void
  2835. debug (const char *where, PangoLayoutLine *line, ParaBreakState *state)
  2836. {
  2837. int line_width = pango_layout_line_get_width (line);
  2838. g_debug ("rem %d + line %d = %d %s",
  2839. state->remaining_width,
  2840. line_width,
  2841. state->remaining_width + line_width,
  2842. where);
  2843. }
  2844. #else
  2845. # define DEBUG(where, line, state) do { } while (0)
  2846. #endif
  2847. /* Tries to insert as much as possible of the item at the head of
  2848. * state->items onto @line. Five results are possible:
  2849. *
  2850. * %BREAK_NONE_FIT: Couldn't fit anything.
  2851. * %BREAK_SOME_FIT: The item was broken in the middle.
  2852. * %BREAK_ALL_FIT: Everything fit.
  2853. * %BREAK_EMPTY_FIT: Nothing fit, but that was ok, as we can break at the first char.
  2854. * %BREAK_LINE_SEPARATOR: Item begins with a line separator.
  2855. *
  2856. * If @force_fit is %TRUE, then %BREAK_NONE_FIT will never
  2857. * be returned, a run will be added even if inserting the minimum amount
  2858. * will cause the line to overflow. This is used at the start of a line
  2859. * and until we've found at least some place to break.
  2860. *
  2861. * If @no_break_at_end is %TRUE, then %BREAK_ALL_FIT will never be
  2862. * returned even everything fits; the run will be broken earlier,
  2863. * or %BREAK_NONE_FIT returned. This is used when the end of the
  2864. * run is not a break position.
  2865. */
  2866. static BreakResult
  2867. process_item (PangoLayout *layout,
  2868. PangoLayoutLine *line,
  2869. ParaBreakState *state,
  2870. gboolean force_fit,
  2871. gboolean no_break_at_end)
  2872. {
  2873. PangoItem *item = state->items->data;
  2874. gboolean shape_set = FALSE;
  2875. int width;
  2876. int length;
  2877. int i;
  2878. gboolean processing_new_item = FALSE;
  2879. /* Only one character has type G_UNICODE_LINE_SEPARATOR in Unicode 5.0;
  2880. * update this if that changes. */
  2881. #define LINE_SEPARATOR 0x2028
  2882. if (!state->glyphs)
  2883. {
  2884. pango_layout_get_item_properties (item, &state->properties);
  2885. state->glyphs = shape_run (line, state, item);
  2886. state->log_widths = NULL;
  2887. state->log_widths_offset = 0;
  2888. processing_new_item = TRUE;
  2889. }
  2890. if (!layout->single_paragraph &&
  2891. g_utf8_get_char (layout->text + item->offset) == LINE_SEPARATOR &&
  2892. !should_ellipsize_current_line (layout, state))
  2893. {
  2894. insert_run (line, state, item, TRUE);
  2895. state->log_widths_offset += item->num_chars;
  2896. return BREAK_LINE_SEPARATOR;
  2897. }
  2898. if (state->remaining_width < 0 && !no_break_at_end) /* Wrapping off */
  2899. {
  2900. insert_run (line, state, item, TRUE);
  2901. return BREAK_ALL_FIT;
  2902. }
  2903. width = 0;
  2904. if (processing_new_item)
  2905. {
  2906. width = pango_glyph_string_get_width (state->glyphs);
  2907. }
  2908. else
  2909. {
  2910. for (i = 0; i < item->num_chars; i++)
  2911. width += state->log_widths[state->log_widths_offset + i];
  2912. }
  2913. if ((width <= state->remaining_width || (item->num_chars == 1 && !line->runs)) &&
  2914. !no_break_at_end)
  2915. {
  2916. state->remaining_width -= width;
  2917. state->remaining_width = MAX (state->remaining_width, 0);
  2918. insert_run (line, state, item, TRUE);
  2919. return BREAK_ALL_FIT;
  2920. }
  2921. else
  2922. {
  2923. int num_chars = item->num_chars;
  2924. int break_num_chars = num_chars;
  2925. int break_width = width;
  2926. int orig_width = width;
  2927. gboolean retrying_with_char_breaks = FALSE;
  2928. if (processing_new_item)
  2929. {
  2930. PangoGlyphItem glyph_item = {item, state->glyphs};
  2931. state->log_widths = g_new (int, item->num_chars);
  2932. pango_glyph_item_get_logical_widths (&glyph_item, layout->text, state->log_widths);
  2933. }
  2934. retry_break:
  2935. /* See how much of the item we can stuff in the line. */
  2936. width = 0;
  2937. for (num_chars = 0; num_chars < item->num_chars; num_chars++)
  2938. {
  2939. if (width > state->remaining_width && break_num_chars < item->num_chars)
  2940. break;
  2941. /* If there are no previous runs we have to take care to grab at least one char. */
  2942. if (can_break_at (layout, state->start_offset + num_chars, retrying_with_char_breaks) &&
  2943. (num_chars > 0 || line->runs))
  2944. {
  2945. break_num_chars = num_chars;
  2946. break_width = width;
  2947. }
  2948. width += state->log_widths[state->log_widths_offset + num_chars];
  2949. }
  2950. /* If there's a space at the end of the line, include that also.
  2951. * The logic here should match zero_line_final_space().
  2952. * XXX Currently it doesn't quite match the logic there. We don't check
  2953. * the cluster here. But should be fine in practice. */
  2954. if (break_num_chars > 0 && break_num_chars < item->num_chars &&
  2955. layout->log_attrs[state->start_offset + break_num_chars - 1].is_white)
  2956. {
  2957. break_width -= state->log_widths[state->log_widths_offset + break_num_chars - 1];
  2958. }
  2959. if (layout->wrap == PANGO_WRAP_WORD_CHAR && force_fit && break_width > state->remaining_width && !retrying_with_char_breaks)
  2960. {
  2961. retrying_with_char_breaks = TRUE;
  2962. num_chars = item->num_chars;
  2963. width = orig_width;
  2964. break_num_chars = num_chars;
  2965. break_width = width;
  2966. goto retry_break;
  2967. }
  2968. if (force_fit || break_width <= state->remaining_width) /* Successfully broke the item */
  2969. {
  2970. if (state->remaining_width >= 0)
  2971. {
  2972. state->remaining_width -= break_width;
  2973. state->remaining_width = MAX (state->remaining_width, 0);
  2974. }
  2975. if (break_num_chars == item->num_chars)
  2976. {
  2977. insert_run (line, state, item, TRUE);
  2978. return BREAK_ALL_FIT;
  2979. }
  2980. else if (break_num_chars == 0)
  2981. {
  2982. return BREAK_EMPTY_FIT;
  2983. }
  2984. else
  2985. {
  2986. PangoItem *new_item;
  2987. length = g_utf8_offset_to_pointer (layout->text + item->offset, break_num_chars) - (layout->text + item->offset);
  2988. new_item = pango_item_split (item, length, break_num_chars);
  2989. /* Add the width back, to the line, reshape, subtract the new width */
  2990. state->remaining_width += break_width;
  2991. insert_run (line, state, new_item, FALSE);
  2992. break_width = pango_glyph_string_get_width (((PangoGlyphItem *)(line->runs->data))->glyphs);
  2993. state->remaining_width -= break_width;
  2994. state->log_widths_offset += break_num_chars;
  2995. /* Shaped items should never be broken */
  2996. g_assert (!shape_set);
  2997. return BREAK_SOME_FIT;
  2998. }
  2999. }
  3000. else
  3001. {
  3002. pango_glyph_string_free (state->glyphs);
  3003. state->glyphs = NULL;
  3004. g_free (state->log_widths);
  3005. state->log_widths = NULL;
  3006. return BREAK_NONE_FIT;
  3007. }
  3008. }
  3009. }
  3010. /* The resolved direction for the line is always one
  3011. * of LTR/RTL; not a week or neutral directions
  3012. */
  3013. static void
  3014. line_set_resolved_dir (PangoLayoutLine *line,
  3015. PangoDirection direction)
  3016. {
  3017. switch (direction)
  3018. {
  3019. default:
  3020. case PANGO_DIRECTION_LTR:
  3021. case PANGO_DIRECTION_TTB_RTL:
  3022. case PANGO_DIRECTION_WEAK_LTR:
  3023. case PANGO_DIRECTION_NEUTRAL:
  3024. line->resolved_dir = PANGO_DIRECTION_LTR;
  3025. break;
  3026. case PANGO_DIRECTION_RTL:
  3027. case PANGO_DIRECTION_WEAK_RTL:
  3028. case PANGO_DIRECTION_TTB_LTR:
  3029. line->resolved_dir = PANGO_DIRECTION_RTL;
  3030. break;
  3031. }
  3032. /* The direction vs. gravity dance:
  3033. * - If gravity is SOUTH, leave direction untouched.
  3034. * - If gravity is NORTH, switch direction.
  3035. * - If gravity is EAST, set to LTR, as
  3036. * it's a clockwise-rotated layout, so the rotated
  3037. * top is unrotated left.
  3038. * - If gravity is WEST, set to RTL, as
  3039. * it's a counter-clockwise-rotated layout, so the rotated
  3040. * top is unrotated right.
  3041. *
  3042. * A similar dance is performed in pango-context.c:
  3043. * itemize_state_add_character(). Keep in synch.
  3044. */
  3045. switch (pango_context_get_gravity (line->layout->context))
  3046. {
  3047. default:
  3048. case PANGO_GRAVITY_AUTO:
  3049. case PANGO_GRAVITY_SOUTH:
  3050. break;
  3051. case PANGO_GRAVITY_NORTH:
  3052. line->resolved_dir = PANGO_DIRECTION_LTR
  3053. + PANGO_DIRECTION_RTL
  3054. - line->resolved_dir;
  3055. break;
  3056. case PANGO_GRAVITY_EAST:
  3057. /* This is in fact why deprecated TTB_RTL is LTR */
  3058. line->resolved_dir = PANGO_DIRECTION_LTR;
  3059. break;
  3060. case PANGO_GRAVITY_WEST:
  3061. /* This is in fact why deprecated TTB_LTR is RTL */
  3062. line->resolved_dir = PANGO_DIRECTION_RTL;
  3063. break;
  3064. }
  3065. }
  3066. static gboolean
  3067. should_ellipsize_current_line (PangoLayout *layout,
  3068. ParaBreakState *state)
  3069. {
  3070. if (G_LIKELY (layout->ellipsize == PANGO_ELLIPSIZE_NONE || layout->width < 0))
  3071. return FALSE;
  3072. if (layout->height >= 0)
  3073. {
  3074. /* state->remaining_height is height of layout left */
  3075. /* if we can't stuff two more lines at the current guess of line height,
  3076. * the line we are going to produce is going to be the last line */
  3077. return state->line_height * 2 > state->remaining_height;
  3078. }
  3079. else
  3080. {
  3081. /* -layout->height is number of lines per paragraph to show */
  3082. return state->line_of_par == - layout->height;
  3083. }
  3084. }
  3085. static void
  3086. add_line (PangoLayoutLine *line,
  3087. ParaBreakState *state)
  3088. {
  3089. PangoLayout *layout = line->layout;
  3090. /* we prepend, then reverse the list later */
  3091. layout->lines = g_slist_prepend (layout->lines, line);
  3092. layout->line_count++;
  3093. if (layout->height >= 0)
  3094. {
  3095. PangoRectangle logical_rect;
  3096. pango_layout_line_get_extents (line, NULL, &logical_rect);
  3097. state->remaining_height -= logical_rect.height;
  3098. state->remaining_height -= layout->spacing;
  3099. state->line_height = logical_rect.height;
  3100. }
  3101. }
  3102. static void
  3103. process_line (PangoLayout *layout,
  3104. ParaBreakState *state)
  3105. {
  3106. PangoLayoutLine *line;
  3107. gboolean have_break = FALSE; /* If we've seen a possible break yet */
  3108. int break_remaining_width = 0; /* Remaining width before adding run with break */
  3109. int break_start_offset = 0; /* Start offset before adding run with break */
  3110. GSList *break_link = NULL; /* Link holding run before break */
  3111. gboolean wrapped = FALSE; /* If we had to wrap the line */
  3112. line = pango_layout_line_new (layout);
  3113. line->start_index = state->line_start_index;
  3114. line->is_paragraph_start = state->line_of_par == 1;
  3115. line_set_resolved_dir (line, state->base_dir);
  3116. state->line_width = layout->width;
  3117. if (state->line_width >= 0 && layout->alignment != PANGO_ALIGN_CENTER)
  3118. {
  3119. if (line->is_paragraph_start && layout->indent >= 0)
  3120. state->line_width -= layout->indent;
  3121. else if (!line->is_paragraph_start && layout->indent < 0)
  3122. state->line_width += layout->indent;
  3123. if (state->line_width < 0)
  3124. state->line_width = 0;
  3125. }
  3126. if (G_UNLIKELY (should_ellipsize_current_line (layout, state)))
  3127. state->remaining_width = -1;
  3128. else
  3129. state->remaining_width = state->line_width;
  3130. DEBUG ("starting to fill line", line, state);
  3131. while (state->items)
  3132. {
  3133. PangoItem *item = state->items->data;
  3134. BreakResult result;
  3135. int old_num_chars;
  3136. int old_remaining_width;
  3137. gboolean first_item_in_line;
  3138. old_num_chars = item->num_chars;
  3139. old_remaining_width = state->remaining_width;
  3140. first_item_in_line = line->runs != NULL;
  3141. result = process_item (layout, line, state, !have_break, FALSE);
  3142. switch (result)
  3143. {
  3144. case BREAK_ALL_FIT:
  3145. if (can_break_in (layout, state->start_offset, old_num_chars, first_item_in_line))
  3146. {
  3147. have_break = TRUE;
  3148. break_remaining_width = old_remaining_width;
  3149. break_start_offset = state->start_offset;
  3150. break_link = line->runs->next;
  3151. }
  3152. state->items = g_list_delete_link (state->items, state->items);
  3153. state->start_offset += old_num_chars;
  3154. break;
  3155. case BREAK_EMPTY_FIT:
  3156. wrapped = TRUE;
  3157. goto done;
  3158. case BREAK_SOME_FIT:
  3159. state->start_offset += old_num_chars - item->num_chars;
  3160. wrapped = TRUE;
  3161. goto done;
  3162. case BREAK_NONE_FIT:
  3163. /* Back up over unused runs to run where there is a break */
  3164. while (line->runs && line->runs != break_link)
  3165. state->items = g_list_prepend (state->items, uninsert_run (line));
  3166. state->start_offset = break_start_offset;
  3167. state->remaining_width = break_remaining_width;
  3168. /* Reshape run to break */
  3169. item = state->items->data;
  3170. old_num_chars = item->num_chars;
  3171. result = process_item (layout, line, state, TRUE, TRUE);
  3172. g_assert (result == BREAK_SOME_FIT || result == BREAK_EMPTY_FIT);
  3173. state->start_offset += old_num_chars - item->num_chars;
  3174. wrapped = TRUE;
  3175. goto done;
  3176. case BREAK_LINE_SEPARATOR:
  3177. state->items = g_list_delete_link (state->items, state->items);
  3178. state->start_offset += old_num_chars;
  3179. /* A line-separate is just a forced break. Set wrapped, so we do
  3180. * justification */
  3181. wrapped = TRUE;
  3182. goto done;
  3183. }
  3184. }
  3185. done:
  3186. pango_layout_line_postprocess (line, state, wrapped);
  3187. add_line (line, state);
  3188. state->line_of_par++;
  3189. state->line_start_index += line->length;
  3190. state->line_start_offset = state->start_offset;
  3191. }
  3192. static void
  3193. get_items_log_attrs (const char *text,
  3194. GList *items,
  3195. PangoLogAttr *log_attrs,
  3196. int para_delimiter_len)
  3197. {
  3198. int offset = 0;
  3199. int index = 0;
  3200. while (items)
  3201. {
  3202. PangoItem tmp_item = *(PangoItem *)items->data;
  3203. /* Accumulate all the consecutive items that match in language
  3204. * characteristics, ignoring font, style tags, etc.
  3205. */
  3206. while (items->next)
  3207. {
  3208. PangoItem *next_item = items->next->data;
  3209. /* FIXME: Handle language tags */
  3210. if (next_item->analysis.lang_engine != tmp_item.analysis.lang_engine)
  3211. break;
  3212. else
  3213. {
  3214. tmp_item.length += next_item->length;
  3215. tmp_item.num_chars += next_item->num_chars;
  3216. }
  3217. items = items->next;
  3218. }
  3219. /* Break the paragraph delimiters with the last item */
  3220. if (items->next == NULL)
  3221. {
  3222. tmp_item.num_chars += pango_utf8_strlen (text + index + tmp_item.length, para_delimiter_len);
  3223. tmp_item.length += para_delimiter_len;
  3224. }
  3225. /* XXX This is wrong. we should call pango_default_break on the entire
  3226. * layout text and then tailor_break on each lang_engine change, like
  3227. * pango_get_log_attrs does.
  3228. */
  3229. pango_break (text + index, tmp_item.length, &tmp_item.analysis,
  3230. log_attrs + offset, tmp_item.num_chars + 1);
  3231. offset += tmp_item.num_chars;
  3232. index += tmp_item.length;
  3233. items = items->next;
  3234. }
  3235. }
  3236. static PangoAttrList *
  3237. pango_layout_get_effective_attributes (PangoLayout *layout)
  3238. {
  3239. PangoAttrList *attrs;
  3240. if (layout->attrs)
  3241. attrs = pango_attr_list_copy (layout->attrs);
  3242. else
  3243. attrs = pango_attr_list_new ();
  3244. if (layout->font_desc)
  3245. {
  3246. PangoAttribute *attr = pango_attr_font_desc_new (layout->font_desc);
  3247. pango_attr_list_insert_before (attrs, attr);
  3248. }
  3249. return attrs;
  3250. }
  3251. static gboolean
  3252. no_shape_filter_func (PangoAttribute *attribute,
  3253. gpointer data G_GNUC_UNUSED)
  3254. {
  3255. static const PangoAttrType no_shape_types[] = {
  3256. PANGO_ATTR_FOREGROUND,
  3257. PANGO_ATTR_BACKGROUND,
  3258. PANGO_ATTR_UNDERLINE,
  3259. PANGO_ATTR_STRIKETHROUGH,
  3260. PANGO_ATTR_RISE
  3261. /* Ideally we want font-features here, because we don't
  3262. * want it to break shaping runs. But if we put it here,
  3263. * it won't show up in the shaper anymore :(. To be
  3264. * fixed later. */
  3265. /* PANGO_ATTR_FONT_FEATURES */
  3266. };
  3267. int i;
  3268. for (i = 0; i < (int)G_N_ELEMENTS (no_shape_types); i++)
  3269. if (attribute->klass->type == no_shape_types[i])
  3270. return TRUE;
  3271. return FALSE;
  3272. }
  3273. static PangoAttrList *
  3274. filter_no_shape_attributes (PangoAttrList *attrs)
  3275. {
  3276. return pango_attr_list_filter (attrs,
  3277. no_shape_filter_func,
  3278. NULL);
  3279. }
  3280. static void
  3281. apply_no_shape_attributes (PangoLayout *layout,
  3282. PangoAttrList *no_shape_attrs)
  3283. {
  3284. GSList *line_list;
  3285. for (line_list = layout->lines; line_list; line_list = line_list->next)
  3286. {
  3287. PangoLayoutLine *line = line_list->data;
  3288. GSList *old_runs = g_slist_reverse (line->runs);
  3289. GSList *run_list;
  3290. line->runs = NULL;
  3291. for (run_list = old_runs; run_list; run_list = run_list->next)
  3292. {
  3293. PangoGlyphItem *glyph_item = run_list->data;
  3294. GSList *new_runs;
  3295. new_runs = pango_glyph_item_apply_attrs (glyph_item,
  3296. layout->text,
  3297. no_shape_attrs);
  3298. line->runs = g_slist_concat (new_runs, line->runs);
  3299. }
  3300. g_slist_free (old_runs);
  3301. }
  3302. }
  3303. static void
  3304. pango_layout_check_lines (PangoLayout *layout)
  3305. {
  3306. const char *start;
  3307. gboolean done = FALSE;
  3308. int start_offset;
  3309. PangoAttrList *attrs;
  3310. PangoAttrList *no_shape_attrs;
  3311. PangoAttrIterator *iter;
  3312. PangoDirection prev_base_dir = PANGO_DIRECTION_NEUTRAL, base_dir = PANGO_DIRECTION_NEUTRAL;
  3313. ParaBreakState state;
  3314. check_context_changed (layout);
  3315. if (G_LIKELY (layout->lines))
  3316. return;
  3317. g_assert (!layout->log_attrs);
  3318. /* For simplicity, we make sure at this point that layout->text
  3319. * is non-NULL even if it is zero length
  3320. */
  3321. if (G_UNLIKELY (!layout->text))
  3322. pango_layout_set_text (layout, NULL, 0);
  3323. attrs = pango_layout_get_effective_attributes (layout);
  3324. no_shape_attrs = filter_no_shape_attributes (attrs);
  3325. iter = pango_attr_list_get_iterator (attrs);
  3326. layout->log_attrs = g_new (PangoLogAttr, layout->n_chars + 1);
  3327. start_offset = 0;
  3328. start = layout->text;
  3329. /* Find the first strong direction of the text */
  3330. if (layout->auto_dir)
  3331. {
  3332. prev_base_dir = pango_find_base_dir (layout->text, layout->length);
  3333. if (prev_base_dir == PANGO_DIRECTION_NEUTRAL)
  3334. prev_base_dir = pango_context_get_base_dir (layout->context);
  3335. }
  3336. else
  3337. base_dir = pango_context_get_base_dir (layout->context);
  3338. /* these are only used if layout->height >= 0 */
  3339. state.remaining_height = layout->height;
  3340. state.line_height = -1;
  3341. if (layout->height >= 0)
  3342. {
  3343. PangoRectangle logical;
  3344. pango_layout_get_empty_extents_at_index (layout, 0, &logical);
  3345. state.line_height = logical.height;
  3346. }
  3347. do
  3348. {
  3349. int delim_len;
  3350. const char *end;
  3351. int delimiter_index, next_para_index;
  3352. if (layout->single_paragraph)
  3353. {
  3354. delimiter_index = layout->length;
  3355. next_para_index = layout->length;
  3356. }
  3357. else
  3358. {
  3359. pango_find_paragraph_boundary (start,
  3360. (layout->text + layout->length) - start,
  3361. &delimiter_index,
  3362. &next_para_index);
  3363. }
  3364. g_assert (next_para_index >= delimiter_index);
  3365. if (layout->auto_dir)
  3366. {
  3367. base_dir = pango_find_base_dir (start, delimiter_index);
  3368. /* Propagate the base direction for neutral paragraphs */
  3369. if (base_dir == PANGO_DIRECTION_NEUTRAL)
  3370. base_dir = prev_base_dir;
  3371. else
  3372. prev_base_dir = base_dir;
  3373. }
  3374. end = start + delimiter_index;
  3375. delim_len = next_para_index - delimiter_index;
  3376. if (end == (layout->text + layout->length))
  3377. done = TRUE;
  3378. g_assert (end <= (layout->text + layout->length));
  3379. g_assert (start <= (layout->text + layout->length));
  3380. g_assert (delim_len < 4); /* PS is 3 bytes */
  3381. g_assert (delim_len >= 0);
  3382. state.attrs = attrs;
  3383. state.items = pango_itemize_with_base_dir (layout->context,
  3384. base_dir,
  3385. layout->text,
  3386. start - layout->text,
  3387. end - start,
  3388. attrs,
  3389. iter);
  3390. get_items_log_attrs (start, state.items,
  3391. layout->log_attrs + start_offset,
  3392. delim_len);
  3393. state.base_dir = base_dir;
  3394. state.line_of_par = 1;
  3395. state.start_offset = start_offset;
  3396. state.line_start_offset = start_offset;
  3397. state.line_start_index = start - layout->text;
  3398. state.glyphs = NULL;
  3399. state.log_widths = NULL;
  3400. /* for deterministic bug hunting's sake set everything! */
  3401. state.line_width = -1;
  3402. state.remaining_width = -1;
  3403. state.log_widths_offset = 0;
  3404. if (state.items)
  3405. {
  3406. while (state.items)
  3407. process_line (layout, &state);
  3408. }
  3409. else
  3410. {
  3411. PangoLayoutLine *empty_line;
  3412. empty_line = pango_layout_line_new (layout);
  3413. empty_line->start_index = state.line_start_index;
  3414. empty_line->is_paragraph_start = TRUE;
  3415. line_set_resolved_dir (empty_line, base_dir);
  3416. add_line (empty_line, &state);
  3417. }
  3418. if (layout->height >= 0 && state.remaining_height < state.line_height)
  3419. done = TRUE;
  3420. if (!done)
  3421. start_offset += pango_utf8_strlen (start, (end - start) + delim_len);
  3422. start = end + delim_len;
  3423. }
  3424. while (!done);
  3425. pango_attr_iterator_destroy (iter);
  3426. pango_attr_list_unref (attrs);
  3427. if (no_shape_attrs)
  3428. {
  3429. apply_no_shape_attributes (layout, no_shape_attrs);
  3430. pango_attr_list_unref (no_shape_attrs);
  3431. }
  3432. layout->lines = g_slist_reverse (layout->lines);
  3433. }
  3434. /**
  3435. * pango_layout_line_ref:
  3436. * @line: (nullable): a #PangoLayoutLine, may be %NULL
  3437. *
  3438. * Increase the reference count of a #PangoLayoutLine by one.
  3439. *
  3440. * Return value: the line passed in.
  3441. *
  3442. * Since: 1.10
  3443. **/
  3444. PangoLayoutLine *
  3445. pango_layout_line_ref (PangoLayoutLine *line)
  3446. {
  3447. PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
  3448. if (line == NULL)
  3449. return NULL;
  3450. g_atomic_int_inc ((int *) &private->ref_count);
  3451. return line;
  3452. }
  3453. /**
  3454. * pango_layout_line_unref:
  3455. * @line: a #PangoLayoutLine
  3456. *
  3457. * Decrease the reference count of a #PangoLayoutLine by one.
  3458. * If the result is zero, the line and all associated memory
  3459. * will be freed.
  3460. **/
  3461. void
  3462. pango_layout_line_unref (PangoLayoutLine *line)
  3463. {
  3464. PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
  3465. if (line == NULL)
  3466. return;
  3467. g_return_if_fail (private->ref_count > 0);
  3468. if (g_atomic_int_dec_and_test ((int *) &private->ref_count))
  3469. {
  3470. g_slist_foreach (line->runs, (GFunc)free_run, GINT_TO_POINTER (1));
  3471. g_slist_free (line->runs);
  3472. g_slice_free (PangoLayoutLinePrivate, private);
  3473. }
  3474. }
  3475. G_DEFINE_BOXED_TYPE (PangoLayoutLine, pango_layout_line,
  3476. pango_layout_line_ref,
  3477. pango_layout_line_unref);
  3478. /**
  3479. * pango_layout_line_x_to_index:
  3480. * @line: a #PangoLayoutLine
  3481. * @x_pos: the X offset (in Pango units)
  3482. * from the left edge of the line.
  3483. * @index_: (out): location to store calculated byte index for
  3484. * the grapheme in which the user clicked.
  3485. * @trailing: (out): location to store an integer indicating where
  3486. * in the grapheme the user clicked. It will either
  3487. * be zero, or the number of characters in the
  3488. * grapheme. 0 represents the leading edge of the grapheme.
  3489. *
  3490. * Converts from x offset to the byte index of the corresponding
  3491. * character within the text of the layout. If @x_pos is outside the line,
  3492. * @index_ and @trailing will point to the very first or very last position
  3493. * in the line. This determination is based on the resolved direction
  3494. * of the paragraph; for example, if the resolved direction is
  3495. * right-to-left, then an X position to the right of the line (after it)
  3496. * results in 0 being stored in @index_ and @trailing. An X position to the
  3497. * left of the line results in @index_ pointing to the (logical) last
  3498. * grapheme in the line and @trailing being set to the number of characters
  3499. * in that grapheme. The reverse is true for a left-to-right line.
  3500. *
  3501. * Return value: %FALSE if @x_pos was outside the line, %TRUE if inside
  3502. **/
  3503. gboolean
  3504. pango_layout_line_x_to_index (PangoLayoutLine *line,
  3505. int x_pos,
  3506. int *index,
  3507. int *trailing)
  3508. {
  3509. GSList *tmp_list;
  3510. gint start_pos = 0;
  3511. gint first_index = 0; /* line->start_index */
  3512. gint first_offset;
  3513. gint last_index; /* start of last grapheme in line */
  3514. gint last_offset;
  3515. gint end_index; /* end iterator for line */
  3516. gint end_offset; /* end iterator for line */
  3517. PangoLayout *layout;
  3518. gint last_trailing;
  3519. gboolean suppress_last_trailing;
  3520. g_return_val_if_fail (LINE_IS_VALID (line), FALSE);
  3521. layout = line->layout;
  3522. /* Find the last index in the line
  3523. */
  3524. first_index = line->start_index;
  3525. if (line->length == 0)
  3526. {
  3527. if (index)
  3528. *index = first_index;
  3529. if (trailing)
  3530. *trailing = 0;
  3531. return FALSE;
  3532. }
  3533. g_assert (line->length > 0);
  3534. first_offset = g_utf8_pointer_to_offset (layout->text, layout->text + line->start_index);
  3535. end_index = first_index + line->length;
  3536. end_offset = first_offset + g_utf8_pointer_to_offset (layout->text + first_index, layout->text + end_index);
  3537. last_index = end_index;
  3538. last_offset = end_offset;
  3539. last_trailing = 0;
  3540. do
  3541. {
  3542. last_index = g_utf8_prev_char (layout->text + last_index) - layout->text;
  3543. last_offset--;
  3544. last_trailing++;
  3545. }
  3546. while (last_offset > first_offset && !layout->log_attrs[last_offset].is_cursor_position);
  3547. /* This is a HACK. If a program only keeps track of cursor (etc)
  3548. * indices and not the trailing flag, then the trailing index of the
  3549. * last character on a wrapped line is identical to the leading
  3550. * index of the next line. So, we fake it and set the trailing flag
  3551. * to zero.
  3552. *
  3553. * That is, if the text is "now is the time", and is broken between
  3554. * 'now' and 'is'
  3555. *
  3556. * Then when the cursor is actually at:
  3557. *
  3558. * n|o|w| |i|s|
  3559. * ^
  3560. * we lie and say it is at:
  3561. *
  3562. * n|o|w| |i|s|
  3563. * ^
  3564. *
  3565. * So the cursor won't appear on the next line before 'the'.
  3566. *
  3567. * Actually, any program keeping cursor
  3568. * positions with wrapped lines should distinguish leading and
  3569. * trailing cursors.
  3570. */
  3571. tmp_list = layout->lines;
  3572. while (tmp_list->data != line)
  3573. tmp_list = tmp_list->next;
  3574. if (tmp_list->next &&
  3575. line->start_index + line->length == ((PangoLayoutLine *)tmp_list->next->data)->start_index)
  3576. suppress_last_trailing = TRUE;
  3577. else
  3578. suppress_last_trailing = FALSE;
  3579. if (x_pos < 0)
  3580. {
  3581. /* pick the leftmost char */
  3582. if (index)
  3583. *index = (line->resolved_dir == PANGO_DIRECTION_LTR) ? first_index : last_index;
  3584. /* and its leftmost edge */
  3585. if (trailing)
  3586. *trailing = (line->resolved_dir == PANGO_DIRECTION_LTR || suppress_last_trailing) ? 0 : last_trailing;
  3587. return FALSE;
  3588. }
  3589. tmp_list = line->runs;
  3590. while (tmp_list)
  3591. {
  3592. PangoLayoutRun *run = tmp_list->data;
  3593. int logical_width;
  3594. logical_width = pango_glyph_string_get_width (run->glyphs);
  3595. if (x_pos >= start_pos && x_pos < start_pos + logical_width)
  3596. {
  3597. int offset;
  3598. gboolean char_trailing;
  3599. int grapheme_start_index;
  3600. int grapheme_start_offset;
  3601. int grapheme_end_offset;
  3602. int pos;
  3603. int char_index;
  3604. pango_glyph_string_x_to_index (run->glyphs,
  3605. layout->text + run->item->offset, run->item->length,
  3606. &run->item->analysis,
  3607. x_pos - start_pos,
  3608. &pos, &char_trailing);
  3609. char_index = run->item->offset + pos;
  3610. /* Convert from characters to graphemes */
  3611. offset = g_utf8_pointer_to_offset (layout->text, layout->text + char_index);
  3612. grapheme_start_offset = offset;
  3613. grapheme_start_index = char_index;
  3614. while (grapheme_start_offset > first_offset &&
  3615. !layout->log_attrs[grapheme_start_offset].is_cursor_position)
  3616. {
  3617. grapheme_start_index = g_utf8_prev_char (layout->text + grapheme_start_index) - layout->text;
  3618. grapheme_start_offset--;
  3619. }
  3620. grapheme_end_offset = offset;
  3621. do
  3622. {
  3623. grapheme_end_offset++;
  3624. }
  3625. while (grapheme_end_offset < end_offset &&
  3626. !layout->log_attrs[grapheme_end_offset].is_cursor_position);
  3627. if (index)
  3628. *index = grapheme_start_index;
  3629. if (trailing)
  3630. {
  3631. if ((grapheme_end_offset == end_offset && suppress_last_trailing) ||
  3632. offset + char_trailing <= (grapheme_start_offset + grapheme_end_offset) / 2)
  3633. *trailing = 0;
  3634. else
  3635. *trailing = grapheme_end_offset - grapheme_start_offset;
  3636. }
  3637. return TRUE;
  3638. }
  3639. start_pos += logical_width;
  3640. tmp_list = tmp_list->next;
  3641. }
  3642. /* pick the rightmost char */
  3643. if (index)
  3644. *index = (line->resolved_dir == PANGO_DIRECTION_LTR) ? last_index : first_index;
  3645. /* and its rightmost edge */
  3646. if (trailing)
  3647. *trailing = (line->resolved_dir == PANGO_DIRECTION_LTR && !suppress_last_trailing) ? last_trailing : 0;
  3648. return FALSE;
  3649. }
  3650. static int
  3651. pango_layout_line_get_width (PangoLayoutLine *line)
  3652. {
  3653. int width = 0;
  3654. GSList *tmp_list = line->runs;
  3655. while (tmp_list)
  3656. {
  3657. PangoLayoutRun *run = tmp_list->data;
  3658. width += pango_glyph_string_get_width (run->glyphs);
  3659. tmp_list = tmp_list->next;
  3660. }
  3661. return width;
  3662. }
  3663. /**
  3664. * pango_layout_line_get_x_ranges:
  3665. * @line: a #PangoLayoutLine
  3666. * @start_index: Start byte index of the logical range. If this value
  3667. * is less than the start index for the line, then
  3668. * the first range will extend all the way to the leading
  3669. * edge of the layout. Otherwise it will start at the
  3670. * leading edge of the first character.
  3671. * @end_index: Ending byte index of the logical range. If this value
  3672. * is greater than the end index for the line, then
  3673. * the last range will extend all the way to the trailing
  3674. * edge of the layout. Otherwise, it will end at the
  3675. * trailing edge of the last character.
  3676. * @ranges: (out) (array length=n_ranges) (transfer full):
  3677. * location to store a pointer to an array of ranges.
  3678. * The array will be of length <literal>2*n_ranges</literal>,
  3679. * with each range starting at <literal>(*ranges)[2*n]</literal>
  3680. * and of width <literal>(*ranges)[2*n + 1] - (*ranges)[2*n]</literal>.
  3681. * This array must be freed with g_free(). The coordinates are relative
  3682. * to the layout and are in Pango units.
  3683. * @n_ranges: The number of ranges stored in @ranges.
  3684. *
  3685. * Gets a list of visual ranges corresponding to a given logical range.
  3686. * This list is not necessarily minimal - there may be consecutive
  3687. * ranges which are adjacent. The ranges will be sorted from left to
  3688. * right. The ranges are with respect to the left edge of the entire
  3689. * layout, not with respect to the line.
  3690. **/
  3691. void
  3692. pango_layout_line_get_x_ranges (PangoLayoutLine *line,
  3693. int start_index,
  3694. int end_index,
  3695. int **ranges,
  3696. int *n_ranges)
  3697. {
  3698. gint line_start_index = 0;
  3699. GSList *tmp_list;
  3700. int range_count = 0;
  3701. int accumulated_width = 0;
  3702. int x_offset;
  3703. int width, line_width;
  3704. PangoAlignment alignment;
  3705. g_return_if_fail (line != NULL);
  3706. g_return_if_fail (line->layout != NULL);
  3707. g_return_if_fail (start_index <= end_index);
  3708. alignment = get_alignment (line->layout, line);
  3709. width = line->layout->width;
  3710. if (width == -1 && alignment != PANGO_ALIGN_LEFT)
  3711. {
  3712. PangoRectangle logical_rect;
  3713. pango_layout_get_extents (line->layout, NULL, &logical_rect);
  3714. width = logical_rect.width;
  3715. }
  3716. /* FIXME: The computations here could be optimized, by moving the
  3717. * computations of the x_offset after we go through and figure
  3718. * out where each range is.
  3719. */
  3720. {
  3721. PangoRectangle logical_rect;
  3722. pango_layout_line_get_extents (line, NULL, &logical_rect);
  3723. line_width = logical_rect.width;
  3724. }
  3725. get_x_offset (line->layout, line, width, line_width, &x_offset);
  3726. line_start_index = line->start_index;
  3727. /* Allocate the maximum possible size */
  3728. if (ranges)
  3729. *ranges = g_new (int, 2 * (2 + g_slist_length (line->runs)));
  3730. if (x_offset > 0 &&
  3731. ((line->resolved_dir == PANGO_DIRECTION_LTR && start_index < line_start_index) ||
  3732. (line->resolved_dir == PANGO_DIRECTION_RTL && end_index > line_start_index + line->length)))
  3733. {
  3734. if (ranges)
  3735. {
  3736. (*ranges)[2*range_count] = 0;
  3737. (*ranges)[2*range_count + 1] = x_offset;
  3738. }
  3739. range_count ++;
  3740. }
  3741. tmp_list = line->runs;
  3742. while (tmp_list)
  3743. {
  3744. PangoLayoutRun *run = (PangoLayoutRun *)tmp_list->data;
  3745. if ((start_index < run->item->offset + run->item->length &&
  3746. end_index > run->item->offset))
  3747. {
  3748. if (ranges)
  3749. {
  3750. int run_start_index = MAX (start_index, run->item->offset);
  3751. int run_end_index = MIN (end_index, run->item->offset + run->item->length);
  3752. int run_start_x, run_end_x;
  3753. g_assert (run_end_index > 0);
  3754. /* Back the end_index off one since we want to find the trailing edge of the preceding character */
  3755. run_end_index = g_utf8_prev_char (line->layout->text + run_end_index) - line->layout->text;
  3756. pango_glyph_string_index_to_x (run->glyphs,
  3757. line->layout->text + run->item->offset,
  3758. run->item->length,
  3759. &run->item->analysis,
  3760. run_start_index - run->item->offset, FALSE,
  3761. &run_start_x);
  3762. pango_glyph_string_index_to_x (run->glyphs,
  3763. line->layout->text + run->item->offset,
  3764. run->item->length,
  3765. &run->item->analysis,
  3766. run_end_index - run->item->offset, TRUE,
  3767. &run_end_x);
  3768. (*ranges)[2*range_count] = x_offset + accumulated_width + MIN (run_start_x, run_end_x);
  3769. (*ranges)[2*range_count + 1] = x_offset + accumulated_width + MAX (run_start_x, run_end_x);
  3770. }
  3771. range_count++;
  3772. }
  3773. if (tmp_list->next)
  3774. accumulated_width += pango_glyph_string_get_width (run->glyphs);
  3775. tmp_list = tmp_list->next;
  3776. }
  3777. if (x_offset + line_width < line->layout->width &&
  3778. ((line->resolved_dir == PANGO_DIRECTION_LTR && end_index > line_start_index + line->length) ||
  3779. (line->resolved_dir == PANGO_DIRECTION_RTL && start_index < line_start_index)))
  3780. {
  3781. if (ranges)
  3782. {
  3783. (*ranges)[2*range_count] = x_offset + line_width;
  3784. (*ranges)[2*range_count + 1] = line->layout->width;
  3785. }
  3786. range_count ++;
  3787. }
  3788. if (n_ranges)
  3789. *n_ranges = range_count;
  3790. }
  3791. static void
  3792. pango_layout_get_empty_extents_at_index (PangoLayout *layout,
  3793. int index,
  3794. PangoRectangle *logical_rect)
  3795. {
  3796. if (logical_rect)
  3797. {
  3798. PangoFont *font;
  3799. PangoFontDescription *font_desc = NULL;
  3800. gboolean free_font_desc = FALSE;
  3801. font_desc = pango_context_get_font_description (layout->context);
  3802. if (layout->font_desc)
  3803. {
  3804. font_desc = pango_font_description_copy_static (font_desc);
  3805. pango_font_description_merge (font_desc, layout->font_desc, TRUE);
  3806. free_font_desc = TRUE;
  3807. }
  3808. /* Find the font description for this line
  3809. */
  3810. if (layout->attrs)
  3811. {
  3812. PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->attrs);
  3813. int start, end;
  3814. do
  3815. {
  3816. pango_attr_iterator_range (iter, &start, &end);
  3817. if (start <= index && index < end)
  3818. {
  3819. if (!free_font_desc)
  3820. {
  3821. font_desc = pango_font_description_copy_static (font_desc);
  3822. free_font_desc = TRUE;
  3823. }
  3824. pango_attr_iterator_get_font (iter,
  3825. font_desc,
  3826. NULL,
  3827. NULL);
  3828. break;
  3829. }
  3830. }
  3831. while (pango_attr_iterator_next (iter));
  3832. pango_attr_iterator_destroy (iter);
  3833. }
  3834. font = pango_context_load_font (layout->context, font_desc);
  3835. if (font)
  3836. {
  3837. PangoFontMetrics *metrics;
  3838. metrics = pango_font_get_metrics (font,
  3839. pango_context_get_language (layout->context));
  3840. if (metrics)
  3841. {
  3842. logical_rect->y = - pango_font_metrics_get_ascent (metrics);
  3843. logical_rect->height = - logical_rect->y + pango_font_metrics_get_descent (metrics);
  3844. pango_font_metrics_unref (metrics);
  3845. }
  3846. else
  3847. {
  3848. logical_rect->y = 0;
  3849. logical_rect->height = 0;
  3850. }
  3851. g_object_unref (font);
  3852. }
  3853. else
  3854. {
  3855. logical_rect->y = 0;
  3856. logical_rect->height = 0;
  3857. }
  3858. if (free_font_desc)
  3859. pango_font_description_free (font_desc);
  3860. logical_rect->x = 0;
  3861. logical_rect->width = 0;
  3862. }
  3863. }
  3864. static void
  3865. pango_layout_line_get_empty_extents (PangoLayoutLine *line,
  3866. PangoRectangle *logical_rect)
  3867. {
  3868. pango_layout_get_empty_extents_at_index (line->layout, line->start_index, logical_rect);
  3869. }
  3870. static void
  3871. pango_layout_run_get_extents (PangoLayoutRun *run,
  3872. PangoRectangle *run_ink,
  3873. PangoRectangle *run_logical)
  3874. {
  3875. PangoRectangle logical;
  3876. ItemProperties properties;
  3877. if (G_UNLIKELY (!run_ink && !run_logical))
  3878. return;
  3879. pango_layout_get_item_properties (run->item, &properties);
  3880. if (!run_logical && (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE))
  3881. run_logical = &logical;
  3882. if (!run_logical && (properties.uline != PANGO_UNDERLINE_NONE || properties.strikethrough))
  3883. run_logical = &logical;
  3884. if (properties.shape_set)
  3885. _pango_shape_get_extents (run->item->num_chars,
  3886. properties.shape_ink_rect,
  3887. properties.shape_logical_rect,
  3888. run_ink, run_logical);
  3889. else
  3890. pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
  3891. run_ink, run_logical);
  3892. if (run_ink && (properties.uline != PANGO_UNDERLINE_NONE || properties.strikethrough))
  3893. {
  3894. PangoFontMetrics *metrics = pango_font_get_metrics (run->item->analysis.font,
  3895. run->item->analysis.language);
  3896. int underline_thickness = pango_font_metrics_get_underline_thickness (metrics);
  3897. int underline_position = pango_font_metrics_get_underline_position (metrics);
  3898. int strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness (metrics);
  3899. int strikethrough_position = pango_font_metrics_get_strikethrough_position (metrics);
  3900. int new_pos;
  3901. /* the underline/strikethrough takes x,width of logical_rect. reflect
  3902. * that into ink_rect.
  3903. */
  3904. new_pos = MIN (run_ink->x, run_logical->x);
  3905. run_ink->width = MAX (run_ink->x + run_ink->width, run_logical->x + run_logical->width) - new_pos;
  3906. run_ink->x = new_pos;
  3907. /* We should better handle the case of height==0 in the following cases.
  3908. * If run_ink->height == 0, we should adjust run_ink->y appropriately.
  3909. */
  3910. if (properties.strikethrough)
  3911. {
  3912. if (run_ink->height == 0)
  3913. {
  3914. run_ink->height = strikethrough_thickness;
  3915. run_ink->y = -strikethrough_position;
  3916. }
  3917. }
  3918. switch (properties.uline)
  3919. {
  3920. case PANGO_UNDERLINE_ERROR:
  3921. run_ink->height = MAX (run_ink->height,
  3922. 3 * underline_thickness - underline_position - run_ink->y);
  3923. break;
  3924. case PANGO_UNDERLINE_SINGLE:
  3925. run_ink->height = MAX (run_ink->height,
  3926. underline_thickness - underline_position - run_ink->y);
  3927. break;
  3928. case PANGO_UNDERLINE_DOUBLE:
  3929. run_ink->height = MAX (run_ink->height,
  3930. 3 * underline_thickness - underline_position - run_ink->y);
  3931. break;
  3932. case PANGO_UNDERLINE_LOW:
  3933. run_ink->height += 2 * underline_thickness;
  3934. break;
  3935. case PANGO_UNDERLINE_NONE:
  3936. break;
  3937. default:
  3938. g_critical ("unknown underline mode");
  3939. break;
  3940. }
  3941. pango_font_metrics_unref (metrics);
  3942. }
  3943. if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE)
  3944. {
  3945. gboolean is_hinted = (run_logical->y & run_logical->height & (PANGO_SCALE - 1)) == 0;
  3946. int adjustment = run_logical->y + run_logical->height / 2;
  3947. if (is_hinted)
  3948. adjustment = PANGO_UNITS_ROUND (adjustment);
  3949. properties.rise += adjustment;
  3950. }
  3951. if (properties.rise != 0)
  3952. {
  3953. if (run_ink)
  3954. run_ink->y -= properties.rise;
  3955. if (run_logical)
  3956. run_logical->y -= properties.rise;
  3957. }
  3958. }
  3959. /**
  3960. * pango_layout_line_get_extents:
  3961. * @line: a #PangoLayoutLine
  3962. * @ink_rect: (out) (allow-none): rectangle used to store the extents of
  3963. * the glyph string as drawn, or %NULL
  3964. * @logical_rect: (out) (allow-none):rectangle used to store the logical
  3965. * extents of the glyph string, or %NULL
  3966. *
  3967. * Computes the logical and ink extents of a layout line. See
  3968. * pango_font_get_glyph_extents() for details about the interpretation
  3969. * of the rectangles.
  3970. */
  3971. void
  3972. pango_layout_line_get_extents (PangoLayoutLine *line,
  3973. PangoRectangle *ink_rect,
  3974. PangoRectangle *logical_rect)
  3975. {
  3976. PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
  3977. GSList *tmp_list;
  3978. int x_pos = 0;
  3979. gboolean caching = FALSE;
  3980. g_return_if_fail (LINE_IS_VALID (line));
  3981. if (G_UNLIKELY (!ink_rect && !logical_rect))
  3982. return;
  3983. switch (private->cache_status)
  3984. {
  3985. case CACHED:
  3986. {
  3987. if (ink_rect)
  3988. *ink_rect = private->ink_rect;
  3989. if (logical_rect)
  3990. *logical_rect = private->logical_rect;
  3991. return;
  3992. }
  3993. case NOT_CACHED:
  3994. {
  3995. caching = TRUE;
  3996. if (!ink_rect)
  3997. ink_rect = &private->ink_rect;
  3998. if (!logical_rect)
  3999. logical_rect = &private->logical_rect;
  4000. break;
  4001. }
  4002. case LEAKED:
  4003. {
  4004. break;
  4005. }
  4006. }
  4007. if (ink_rect)
  4008. {
  4009. ink_rect->x = 0;
  4010. ink_rect->y = 0;
  4011. ink_rect->width = 0;
  4012. ink_rect->height = 0;
  4013. }
  4014. if (logical_rect)
  4015. {
  4016. logical_rect->x = 0;
  4017. logical_rect->y = 0;
  4018. logical_rect->width = 0;
  4019. logical_rect->height = 0;
  4020. }
  4021. tmp_list = line->runs;
  4022. while (tmp_list)
  4023. {
  4024. PangoLayoutRun *run = tmp_list->data;
  4025. int new_pos;
  4026. PangoRectangle run_ink;
  4027. PangoRectangle run_logical;
  4028. pango_layout_run_get_extents (run,
  4029. ink_rect ? &run_ink : NULL,
  4030. &run_logical);
  4031. if (ink_rect)
  4032. {
  4033. if (ink_rect->width == 0 || ink_rect->height == 0)
  4034. {
  4035. *ink_rect = run_ink;
  4036. ink_rect->x += x_pos;
  4037. }
  4038. else if (run_ink.width != 0 && run_ink.height != 0)
  4039. {
  4040. new_pos = MIN (ink_rect->x, x_pos + run_ink.x);
  4041. ink_rect->width = MAX (ink_rect->x + ink_rect->width,
  4042. x_pos + run_ink.x + run_ink.width) - new_pos;
  4043. ink_rect->x = new_pos;
  4044. new_pos = MIN (ink_rect->y, run_ink.y);
  4045. ink_rect->height = MAX (ink_rect->y + ink_rect->height,
  4046. run_ink.y + run_ink.height) - new_pos;
  4047. ink_rect->y = new_pos;
  4048. }
  4049. }
  4050. if (logical_rect)
  4051. {
  4052. new_pos = MIN (logical_rect->x, x_pos + run_logical.x);
  4053. logical_rect->width = MAX (logical_rect->x + logical_rect->width,
  4054. x_pos + run_logical.x + run_logical.width) - new_pos;
  4055. logical_rect->x = new_pos;
  4056. new_pos = MIN (logical_rect->y, run_logical.y);
  4057. logical_rect->height = MAX (logical_rect->y + logical_rect->height,
  4058. run_logical.y + run_logical.height) - new_pos;
  4059. logical_rect->y = new_pos;
  4060. }
  4061. x_pos += run_logical.width;
  4062. tmp_list = tmp_list->next;
  4063. }
  4064. if (logical_rect && !line->runs)
  4065. pango_layout_line_get_empty_extents (line, logical_rect);
  4066. if (caching)
  4067. {
  4068. if (&private->ink_rect != ink_rect)
  4069. private->ink_rect = *ink_rect;
  4070. if (&private->logical_rect != logical_rect)
  4071. private->logical_rect = *logical_rect;
  4072. private->cache_status = CACHED;
  4073. }
  4074. }
  4075. static PangoLayoutLine *
  4076. pango_layout_line_new (PangoLayout *layout)
  4077. {
  4078. PangoLayoutLinePrivate *private = g_slice_new (PangoLayoutLinePrivate);
  4079. private->ref_count = 1;
  4080. private->line.layout = layout;
  4081. private->line.runs = NULL;
  4082. private->line.length = 0;
  4083. private->cache_status = NOT_CACHED;
  4084. /* Note that we leave start_index, resolved_dir, and is_paragraph_start
  4085. * uninitialized */
  4086. return (PangoLayoutLine *) private;
  4087. }
  4088. /**
  4089. * pango_layout_line_get_pixel_extents:
  4090. * @layout_line: a #PangoLayoutLine
  4091. * @ink_rect: (out) (allow-none): rectangle used to store the extents of
  4092. * the glyph string as drawn, or %NULL
  4093. * @logical_rect: (out) (allow-none): rectangle used to store the logical
  4094. * extents of the glyph string, or %NULL
  4095. *
  4096. * Computes the logical and ink extents of @layout_line in device units.
  4097. * This function just calls pango_layout_line_get_extents() followed by
  4098. * two pango_extents_to_pixels() calls, rounding @ink_rect and @logical_rect
  4099. * such that the rounded rectangles fully contain the unrounded one (that is,
  4100. * passes them as first argument to pango_extents_to_pixels()).
  4101. **/
  4102. void
  4103. pango_layout_line_get_pixel_extents (PangoLayoutLine *layout_line,
  4104. PangoRectangle *ink_rect,
  4105. PangoRectangle *logical_rect)
  4106. {
  4107. g_return_if_fail (LINE_IS_VALID (layout_line));
  4108. pango_layout_line_get_extents (layout_line, ink_rect, logical_rect);
  4109. pango_extents_to_pixels (ink_rect, NULL);
  4110. pango_extents_to_pixels (logical_rect, NULL);
  4111. }
  4112. /*
  4113. * NB: This implement the exact same algorithm as
  4114. * reorder-items.c:pango_reorder_items().
  4115. */
  4116. static GSList *
  4117. reorder_runs_recurse (GSList *items, int n_items)
  4118. {
  4119. GSList *tmp_list, *level_start_node;
  4120. int i, level_start_i;
  4121. int min_level = G_MAXINT;
  4122. GSList *result = NULL;
  4123. if (n_items == 0)
  4124. return NULL;
  4125. tmp_list = items;
  4126. for (i=0; i<n_items; i++)
  4127. {
  4128. PangoLayoutRun *run = tmp_list->data;
  4129. min_level = MIN (min_level, run->item->analysis.level);
  4130. tmp_list = tmp_list->next;
  4131. }
  4132. level_start_i = 0;
  4133. level_start_node = items;
  4134. tmp_list = items;
  4135. for (i=0; i<n_items; i++)
  4136. {
  4137. PangoLayoutRun *run = tmp_list->data;
  4138. if (run->item->analysis.level == min_level)
  4139. {
  4140. if (min_level % 2)
  4141. {
  4142. if (i > level_start_i)
  4143. result = g_slist_concat (reorder_runs_recurse (level_start_node, i - level_start_i), result);
  4144. result = g_slist_prepend (result, run);
  4145. }
  4146. else
  4147. {
  4148. if (i > level_start_i)
  4149. result = g_slist_concat (result, reorder_runs_recurse (level_start_node, i - level_start_i));
  4150. result = g_slist_append (result, run);
  4151. }
  4152. level_start_i = i + 1;
  4153. level_start_node = tmp_list->next;
  4154. }
  4155. tmp_list = tmp_list->next;
  4156. }
  4157. if (min_level % 2)
  4158. {
  4159. if (i > level_start_i)
  4160. result = g_slist_concat (reorder_runs_recurse (level_start_node, i - level_start_i), result);
  4161. }
  4162. else
  4163. {
  4164. if (i > level_start_i)
  4165. result = g_slist_concat (result, reorder_runs_recurse (level_start_node, i - level_start_i));
  4166. }
  4167. return result;
  4168. }
  4169. static void
  4170. pango_layout_line_reorder (PangoLayoutLine *line)
  4171. {
  4172. GSList *logical_runs = line->runs;
  4173. GSList *tmp_list;
  4174. gboolean all_even, all_odd;
  4175. guint8 level_or = 0, level_and = 1;
  4176. int length = 0;
  4177. /* Check if all items are in the same direction, in that case, the
  4178. * line does not need modification and we can avoid the expensive
  4179. * reorder runs recurse procedure.
  4180. */
  4181. for (tmp_list = logical_runs; tmp_list != NULL; tmp_list = tmp_list->next)
  4182. {
  4183. PangoLayoutRun *run = tmp_list->data;
  4184. level_or |= run->item->analysis.level;
  4185. level_and &= run->item->analysis.level;
  4186. length++;
  4187. }
  4188. /* If none of the levels had the LSB set, all numbers were even. */
  4189. all_even = (level_or & 0x1) == 0;
  4190. /* If all of the levels had the LSB set, all numbers were odd. */
  4191. all_odd = (level_and & 0x1) == 1;
  4192. if (!all_even && !all_odd)
  4193. {
  4194. line->runs = reorder_runs_recurse (logical_runs, length);
  4195. g_slist_free (logical_runs);
  4196. }
  4197. else if (all_odd)
  4198. line->runs = g_slist_reverse (logical_runs);
  4199. }
  4200. static int
  4201. get_item_letter_spacing (PangoItem *item)
  4202. {
  4203. ItemProperties properties;
  4204. pango_layout_get_item_properties (item, &properties);
  4205. return properties.letter_spacing;
  4206. }
  4207. static void
  4208. pad_glyphstring_right (PangoGlyphString *glyphs,
  4209. ParaBreakState *state,
  4210. int adjustment)
  4211. {
  4212. int glyph = glyphs->num_glyphs - 1;
  4213. while (glyph >= 0 && glyphs->glyphs[glyph].geometry.width == 0)
  4214. glyph--;
  4215. if (glyph < 0)
  4216. return;
  4217. state->remaining_width -= adjustment;
  4218. glyphs->glyphs[glyph].geometry.width += adjustment;
  4219. if (glyphs->glyphs[glyph].geometry.width < 0)
  4220. {
  4221. state->remaining_width += glyphs->glyphs[glyph].geometry.width;
  4222. glyphs->glyphs[glyph].geometry.width = 0;
  4223. }
  4224. }
  4225. static void
  4226. pad_glyphstring_left (PangoGlyphString *glyphs,
  4227. ParaBreakState *state,
  4228. int adjustment)
  4229. {
  4230. int glyph = 0;
  4231. while (glyph < glyphs->num_glyphs && glyphs->glyphs[glyph].geometry.width == 0)
  4232. glyph++;
  4233. if (glyph == glyphs->num_glyphs)
  4234. return;
  4235. state->remaining_width -= adjustment;
  4236. glyphs->glyphs[glyph].geometry.width += adjustment;
  4237. glyphs->glyphs[glyph].geometry.x_offset += adjustment;
  4238. }
  4239. static gboolean
  4240. is_tab_run (PangoLayout *layout,
  4241. PangoLayoutRun *run)
  4242. {
  4243. return (layout->text[run->item->offset] == '\t');
  4244. }
  4245. static void
  4246. zero_line_final_space (PangoLayoutLine *line,
  4247. ParaBreakState *state,
  4248. PangoLayoutRun *run)
  4249. {
  4250. PangoLayout *layout = line->layout;
  4251. PangoItem *item = run->item;
  4252. PangoGlyphString *glyphs = run->glyphs;
  4253. int glyph = item->analysis.level % 2 ? 0 : glyphs->num_glyphs - 1;
  4254. /* if the final char of line forms a cluster, and it's
  4255. * a whitespace char, zero its glyph's width as it's been wrapped
  4256. */
  4257. if (glyphs->num_glyphs < 1 || state->start_offset == 0 ||
  4258. !layout->log_attrs[state->start_offset - 1].is_white)
  4259. return;
  4260. if (glyphs->num_glyphs >= 2 &&
  4261. glyphs->log_clusters[glyph] == glyphs->log_clusters[glyph + (item->analysis.level % 2 ? 1 : -1)])
  4262. return;
  4263. state->remaining_width += glyphs->glyphs[glyph].geometry.width;
  4264. glyphs->glyphs[glyph].geometry.width = 0;
  4265. }
  4266. /* When doing shaping, we add the letter spacing value for a
  4267. * run after every grapheme in the run. This produces ugly
  4268. * asymmetrical results, so what this routine is redistributes
  4269. * that space to the beginning and the end of the run.
  4270. *
  4271. * We also trim the letter spacing from runs adjacent to
  4272. * tabs and from the outside runs of the lines so that things
  4273. * line up properly. The line breaking and tab positioning
  4274. * were computed without this trimming so they are no longer
  4275. * exactly correct, but this won't be very noticeable in most
  4276. * cases.
  4277. */
  4278. static void
  4279. adjust_line_letter_spacing (PangoLayoutLine *line,
  4280. ParaBreakState *state)
  4281. {
  4282. PangoLayout *layout = line->layout;
  4283. gboolean reversed;
  4284. PangoLayoutRun *last_run;
  4285. int tab_adjustment;
  4286. GSList *l;
  4287. /* If we have tab stops and the resolved direction of the
  4288. * line is RTL, then we need to walk through the line
  4289. * in reverse direction to figure out the corrections for
  4290. * tab stops.
  4291. */
  4292. reversed = FALSE;
  4293. if (line->resolved_dir == PANGO_DIRECTION_RTL)
  4294. {
  4295. for (l = line->runs; l; l = l->next)
  4296. if (is_tab_run (layout, l->data))
  4297. {
  4298. line->runs = g_slist_reverse (line->runs);
  4299. reversed = TRUE;
  4300. break;
  4301. }
  4302. }
  4303. /* Walk over the runs in the line, redistributing letter
  4304. * spacing from the end of the run to the start of the
  4305. * run and trimming letter spacing from the ends of the
  4306. * runs adjacent to the ends of the line or tab stops.
  4307. *
  4308. * We accumulate a correction factor from this trimming
  4309. * which we add onto the next tab stop space to keep the
  4310. * things properly aligned.
  4311. */
  4312. last_run = NULL;
  4313. tab_adjustment = 0;
  4314. for (l = line->runs; l; l = l->next)
  4315. {
  4316. PangoLayoutRun *run = l->data;
  4317. PangoLayoutRun *next_run = l->next ? l->next->data : NULL;
  4318. if (is_tab_run (layout, run))
  4319. {
  4320. pad_glyphstring_right (run->glyphs, state, tab_adjustment);
  4321. tab_adjustment = 0;
  4322. }
  4323. else
  4324. {
  4325. PangoLayoutRun *visual_next_run = reversed ? last_run : next_run;
  4326. PangoLayoutRun *visual_last_run = reversed ? next_run : last_run;
  4327. int run_spacing = get_item_letter_spacing (run->item);
  4328. int space_left, space_right;
  4329. distribute_letter_spacing (run_spacing, &space_left, &space_right);
  4330. if (run->glyphs->glyphs[0].geometry.width == 0)
  4331. {
  4332. /* we've zeroed this space glyph at the end of line, now remove
  4333. * the letter spacing added to its adjacent glyph */
  4334. pad_glyphstring_left (run->glyphs, state, - space_left);
  4335. }
  4336. else if (!visual_last_run || is_tab_run (layout, visual_last_run))
  4337. {
  4338. pad_glyphstring_left (run->glyphs, state, - space_left);
  4339. tab_adjustment += space_left;
  4340. }
  4341. if (run->glyphs->glyphs[run->glyphs->num_glyphs - 1].geometry.width == 0)
  4342. {
  4343. /* we've zeroed this space glyph at the end of line, now remove
  4344. * the letter spacing added to its adjacent glyph */
  4345. pad_glyphstring_right (run->glyphs, state, - space_right);
  4346. }
  4347. else if (!visual_next_run || is_tab_run (layout, visual_next_run))
  4348. {
  4349. pad_glyphstring_right (run->glyphs, state, - space_right);
  4350. tab_adjustment += space_right;
  4351. }
  4352. }
  4353. last_run = run;
  4354. }
  4355. if (reversed)
  4356. line->runs = g_slist_reverse (line->runs);
  4357. }
  4358. static void
  4359. justify_clusters (PangoLayoutLine *line,
  4360. ParaBreakState *state)
  4361. {
  4362. const gchar *text = line->layout->text;
  4363. const PangoLogAttr *log_attrs = line->layout->log_attrs;
  4364. int total_remaining_width, total_gaps = 0;
  4365. int added_so_far, gaps_so_far;
  4366. gboolean is_hinted;
  4367. GSList *run_iter;
  4368. enum {
  4369. MEASURE,
  4370. ADJUST
  4371. } mode;
  4372. total_remaining_width = state->remaining_width;
  4373. if (total_remaining_width <= 0)
  4374. return;
  4375. /* hint to full pixel if total remaining width was so */
  4376. is_hinted = (total_remaining_width & (PANGO_SCALE - 1)) == 0;
  4377. for (mode = MEASURE; mode <= ADJUST; mode++)
  4378. {
  4379. gboolean leftedge = TRUE;
  4380. PangoGlyphString *rightmost_glyphs = NULL;
  4381. int rightmost_space = 0;
  4382. int residual = 0;
  4383. added_so_far = 0;
  4384. gaps_so_far = 0;
  4385. for (run_iter = line->runs; run_iter; run_iter = run_iter->next)
  4386. {
  4387. PangoLayoutRun *run = run_iter->data;
  4388. PangoGlyphString *glyphs = run->glyphs;
  4389. PangoGlyphItemIter cluster_iter;
  4390. gboolean have_cluster;
  4391. int dir;
  4392. int offset;
  4393. dir = run->item->analysis.level % 2 == 0 ? +1 : -1;
  4394. /* We need character offset of the start of the run. We don't have this.
  4395. * Compute by counting from the beginning of the line. The naming is
  4396. * confusing. Note that:
  4397. *
  4398. * run->item->offset is byte offset of start of run in layout->text.
  4399. * state->line_start_index is byte offset of start of line in layout->text.
  4400. * state->line_start_offset is character offset of start of line in layout->text.
  4401. */
  4402. g_assert (run->item->offset >= state->line_start_index);
  4403. offset = state->line_start_offset
  4404. + pango_utf8_strlen (text + state->line_start_index,
  4405. run->item->offset - state->line_start_index);
  4406. for (have_cluster = dir > 0 ?
  4407. pango_glyph_item_iter_init_start (&cluster_iter, run, text) :
  4408. pango_glyph_item_iter_init_end (&cluster_iter, run, text);
  4409. have_cluster;
  4410. have_cluster = dir > 0 ?
  4411. pango_glyph_item_iter_next_cluster (&cluster_iter) :
  4412. pango_glyph_item_iter_prev_cluster (&cluster_iter))
  4413. {
  4414. int i;
  4415. int width = 0;
  4416. /* don't expand in the middle of graphemes */
  4417. if (!log_attrs[offset + cluster_iter.start_char].is_cursor_position)
  4418. continue;
  4419. for (i = cluster_iter.start_glyph; i != cluster_iter.end_glyph; i += dir)
  4420. width += glyphs->glyphs[i].geometry.width;
  4421. /* also don't expand zero-width clusters. */
  4422. if (width == 0)
  4423. continue;
  4424. gaps_so_far++;
  4425. if (mode == ADJUST)
  4426. {
  4427. int leftmost, rightmost;
  4428. int adjustment, space_left, space_right;
  4429. adjustment = total_remaining_width / total_gaps + residual;
  4430. if (is_hinted)
  4431. {
  4432. int old_adjustment = adjustment;
  4433. adjustment = PANGO_UNITS_ROUND (adjustment);
  4434. residual = old_adjustment - adjustment;
  4435. }
  4436. /* distribute to before/after */
  4437. distribute_letter_spacing (adjustment, &space_left, &space_right);
  4438. if (cluster_iter.start_glyph < cluster_iter.end_glyph)
  4439. {
  4440. /* LTR */
  4441. leftmost = cluster_iter.start_glyph;
  4442. rightmost = cluster_iter.end_glyph - 1;
  4443. }
  4444. else
  4445. {
  4446. /* RTL */
  4447. leftmost = cluster_iter.end_glyph + 1;
  4448. rightmost = cluster_iter.start_glyph;
  4449. }
  4450. /* Don't add to left-side of left-most glyph of left-most non-zero run. */
  4451. if (leftedge)
  4452. leftedge = FALSE;
  4453. else
  4454. {
  4455. glyphs->glyphs[leftmost].geometry.width += space_left ;
  4456. glyphs->glyphs[leftmost].geometry.x_offset += space_left ;
  4457. added_so_far += space_left;
  4458. }
  4459. /* Don't add to right-side of right-most glyph of right-most non-zero run. */
  4460. {
  4461. /* Save so we can undo later. */
  4462. rightmost_glyphs = glyphs;
  4463. rightmost_space = space_right;
  4464. glyphs->glyphs[rightmost].geometry.width += space_right;
  4465. added_so_far += space_right;
  4466. }
  4467. }
  4468. }
  4469. }
  4470. if (mode == MEASURE)
  4471. {
  4472. total_gaps = gaps_so_far - 1;
  4473. if (total_gaps == 0)
  4474. {
  4475. /* a single cluster, can't really justify it */
  4476. return;
  4477. }
  4478. }
  4479. else /* mode == ADJUST */
  4480. {
  4481. if (rightmost_glyphs)
  4482. {
  4483. rightmost_glyphs->glyphs[rightmost_glyphs->num_glyphs - 1].geometry.width -= rightmost_space;
  4484. added_so_far -= rightmost_space;
  4485. }
  4486. }
  4487. }
  4488. state->remaining_width -= added_so_far;
  4489. }
  4490. static void
  4491. justify_words (PangoLayoutLine *line,
  4492. ParaBreakState *state)
  4493. {
  4494. const gchar *text = line->layout->text;
  4495. const PangoLogAttr *log_attrs = line->layout->log_attrs;
  4496. int total_remaining_width, total_space_width = 0;
  4497. int added_so_far, spaces_so_far;
  4498. gboolean is_hinted;
  4499. GSList *run_iter;
  4500. enum {
  4501. MEASURE,
  4502. ADJUST
  4503. } mode;
  4504. total_remaining_width = state->remaining_width;
  4505. if (total_remaining_width <= 0)
  4506. return;
  4507. /* hint to full pixel if total remaining width was so */
  4508. is_hinted = (total_remaining_width & (PANGO_SCALE - 1)) == 0;
  4509. for (mode = MEASURE; mode <= ADJUST; mode++)
  4510. {
  4511. added_so_far = 0;
  4512. spaces_so_far = 0;
  4513. for (run_iter = line->runs; run_iter; run_iter = run_iter->next)
  4514. {
  4515. PangoLayoutRun *run = run_iter->data;
  4516. PangoGlyphString *glyphs = run->glyphs;
  4517. PangoGlyphItemIter cluster_iter;
  4518. gboolean have_cluster;
  4519. int offset;
  4520. /* We need character offset of the start of the run. We don't have this.
  4521. * Compute by counting from the beginning of the line. The naming is
  4522. * confusing. Note that:
  4523. *
  4524. * run->item->offset is byte offset of start of run in layout->text.
  4525. * state->line_start_index is byte offset of start of line in layout->text.
  4526. * state->line_start_offset is character offset of start of line in layout->text.
  4527. */
  4528. g_assert (run->item->offset >= state->line_start_index);
  4529. offset = state->line_start_offset
  4530. + pango_utf8_strlen (text + state->line_start_index,
  4531. run->item->offset - state->line_start_index);
  4532. for (have_cluster = pango_glyph_item_iter_init_start (&cluster_iter, run, text);
  4533. have_cluster;
  4534. have_cluster = pango_glyph_item_iter_next_cluster (&cluster_iter))
  4535. {
  4536. int i;
  4537. int dir;
  4538. if (!log_attrs[offset + cluster_iter.start_char].is_expandable_space)
  4539. continue;
  4540. dir = (cluster_iter.start_glyph < cluster_iter.end_glyph) ? 1 : -1;
  4541. for (i = cluster_iter.start_glyph; i != cluster_iter.end_glyph; i += dir)
  4542. {
  4543. int glyph_width = glyphs->glyphs[i].geometry.width;
  4544. if (glyph_width == 0)
  4545. continue;
  4546. spaces_so_far += glyph_width;
  4547. if (mode == ADJUST)
  4548. {
  4549. int adjustment;
  4550. adjustment = ((guint64) spaces_so_far * total_remaining_width) / total_space_width - added_so_far;
  4551. if (is_hinted)
  4552. adjustment = PANGO_UNITS_ROUND (adjustment);
  4553. glyphs->glyphs[i].geometry.width += adjustment;
  4554. added_so_far += adjustment;
  4555. }
  4556. }
  4557. }
  4558. }
  4559. if (mode == MEASURE)
  4560. {
  4561. total_space_width = spaces_so_far;
  4562. if (total_space_width == 0)
  4563. {
  4564. justify_clusters (line, state);
  4565. return;
  4566. }
  4567. }
  4568. }
  4569. state->remaining_width -= added_so_far;
  4570. }
  4571. static void
  4572. pango_layout_line_postprocess (PangoLayoutLine *line,
  4573. ParaBreakState *state,
  4574. gboolean wrapped)
  4575. {
  4576. gboolean ellipsized = FALSE;
  4577. DEBUG ("postprocessing", line, state);
  4578. /* Truncate the logical-final whitespace in the line if we broke the line at it
  4579. */
  4580. if (wrapped)
  4581. /* The runs are in reverse order at this point, since we prepended them to the list.
  4582. * So, the first run is the last logical run. */
  4583. zero_line_final_space (line, state, line->runs->data);
  4584. /* Reverse the runs
  4585. */
  4586. line->runs = g_slist_reverse (line->runs);
  4587. /* Ellipsize the line if necessary
  4588. */
  4589. if (G_UNLIKELY (state->line_width >= 0 &&
  4590. should_ellipsize_current_line (line->layout, state)))
  4591. {
  4592. ellipsized = _pango_layout_line_ellipsize (line, state->attrs, state->line_width);
  4593. }
  4594. DEBUG ("after removing final space", line, state);
  4595. /* Now convert logical to visual order
  4596. */
  4597. pango_layout_line_reorder (line);
  4598. DEBUG ("after reordering", line, state);
  4599. /* Fixup letter spacing between runs
  4600. */
  4601. adjust_line_letter_spacing (line, state);
  4602. DEBUG ("after letter spacing", line, state);
  4603. /* Distribute extra space between words if justifying and line was wrapped
  4604. */
  4605. if (line->layout->justify && (wrapped || ellipsized))
  4606. {
  4607. /* if we ellipsized, we don't have remaining_width set */
  4608. if (state->remaining_width < 0)
  4609. state->remaining_width = state->line_width - pango_layout_line_get_width (line);
  4610. justify_words (line, state);
  4611. }
  4612. DEBUG ("after justification", line, state);
  4613. line->layout->is_wrapped |= wrapped;
  4614. line->layout->is_ellipsized |= ellipsized;
  4615. }
  4616. static void
  4617. pango_layout_get_item_properties (PangoItem *item,
  4618. ItemProperties *properties)
  4619. {
  4620. GSList *tmp_list = item->analysis.extra_attrs;
  4621. properties->uline = PANGO_UNDERLINE_NONE;
  4622. properties->strikethrough = FALSE;
  4623. properties->letter_spacing = 0;
  4624. properties->rise = 0;
  4625. properties->shape_set = FALSE;
  4626. properties->shape_ink_rect = NULL;
  4627. properties->shape_logical_rect = NULL;
  4628. while (tmp_list)
  4629. {
  4630. PangoAttribute *attr = tmp_list->data;
  4631. switch ((int) attr->klass->type)
  4632. {
  4633. case PANGO_ATTR_UNDERLINE:
  4634. properties->uline = ((PangoAttrInt *)attr)->value;
  4635. break;
  4636. case PANGO_ATTR_STRIKETHROUGH:
  4637. properties->strikethrough = ((PangoAttrInt *)attr)->value;
  4638. break;
  4639. case PANGO_ATTR_RISE:
  4640. properties->rise = ((PangoAttrInt *)attr)->value;
  4641. break;
  4642. case PANGO_ATTR_LETTER_SPACING:
  4643. properties->letter_spacing = ((PangoAttrInt *)attr)->value;
  4644. break;
  4645. case PANGO_ATTR_SHAPE:
  4646. properties->shape_set = TRUE;
  4647. properties->shape_logical_rect = &((PangoAttrShape *)attr)->logical_rect;
  4648. properties->shape_ink_rect = &((PangoAttrShape *)attr)->ink_rect;
  4649. break;
  4650. default:
  4651. break;
  4652. }
  4653. tmp_list = tmp_list->next;
  4654. }
  4655. }
  4656. static int
  4657. next_cluster_start (PangoGlyphString *gs,
  4658. int cluster_start)
  4659. {
  4660. int i;
  4661. i = cluster_start + 1;
  4662. while (i < gs->num_glyphs)
  4663. {
  4664. if (gs->glyphs[i].attr.is_cluster_start)
  4665. return i;
  4666. i++;
  4667. }
  4668. return gs->num_glyphs;
  4669. }
  4670. static int
  4671. cluster_width (PangoGlyphString *gs,
  4672. int cluster_start)
  4673. {
  4674. int i;
  4675. int width;
  4676. width = gs->glyphs[cluster_start].geometry.width;
  4677. i = cluster_start + 1;
  4678. while (i < gs->num_glyphs)
  4679. {
  4680. if (gs->glyphs[i].attr.is_cluster_start)
  4681. break;
  4682. width += gs->glyphs[i].geometry.width;
  4683. i++;
  4684. }
  4685. return width;
  4686. }
  4687. static inline void
  4688. offset_y (PangoLayoutIter *iter,
  4689. int *y)
  4690. {
  4691. *y += iter->line_extents[iter->line_index].baseline;
  4692. }
  4693. /* Sets up the iter for the start of a new cluster. cluster_start_index
  4694. * is the byte index of the cluster start relative to the run.
  4695. */
  4696. static void
  4697. update_cluster (PangoLayoutIter *iter,
  4698. int cluster_start_index)
  4699. {
  4700. char *cluster_text;
  4701. PangoGlyphString *gs;
  4702. int cluster_length;
  4703. iter->character_position = 0;
  4704. gs = iter->run->glyphs;
  4705. iter->cluster_width = cluster_width (gs, iter->cluster_start);
  4706. iter->next_cluster_glyph = next_cluster_start (gs, iter->cluster_start);
  4707. if (iter->ltr)
  4708. {
  4709. /* For LTR text, finding the length of the cluster is easy
  4710. * since logical and visual runs are in the same direction.
  4711. */
  4712. if (iter->next_cluster_glyph < gs->num_glyphs)
  4713. cluster_length = gs->log_clusters[iter->next_cluster_glyph] - cluster_start_index;
  4714. else
  4715. cluster_length = iter->run->item->length - cluster_start_index;
  4716. }
  4717. else
  4718. {
  4719. /* For RTL text, we have to scan backwards to find the previous
  4720. * visual cluster which is the next logical cluster.
  4721. */
  4722. int i = iter->cluster_start;
  4723. while (i > 0 && gs->log_clusters[i - 1] == cluster_start_index)
  4724. i--;
  4725. if (i == 0)
  4726. cluster_length = iter->run->item->length - cluster_start_index;
  4727. else
  4728. cluster_length = gs->log_clusters[i - 1] - cluster_start_index;
  4729. }
  4730. cluster_text = iter->layout->text + iter->run->item->offset + cluster_start_index;
  4731. iter->cluster_num_chars = pango_utf8_strlen (cluster_text, cluster_length);
  4732. if (iter->ltr)
  4733. iter->index = cluster_text - iter->layout->text;
  4734. else
  4735. iter->index = g_utf8_prev_char (cluster_text + cluster_length) - iter->layout->text;
  4736. }
  4737. static void
  4738. update_run (PangoLayoutIter *iter,
  4739. int run_start_index)
  4740. {
  4741. const Extents *line_ext = &iter->line_extents[iter->line_index];
  4742. /* Note that in iter_new() the iter->run_width
  4743. * is garbage but we don't use it since we're on the first run of
  4744. * a line.
  4745. */
  4746. if (iter->run_list_link == iter->line->runs)
  4747. iter->run_x = line_ext->logical_rect.x;
  4748. else
  4749. iter->run_x += iter->run_width;
  4750. if (iter->run)
  4751. {
  4752. iter->run_width = pango_glyph_string_get_width (iter->run->glyphs);
  4753. }
  4754. else
  4755. {
  4756. /* The empty run at the end of a line */
  4757. iter->run_width = 0;
  4758. }
  4759. if (iter->run)
  4760. iter->ltr = (iter->run->item->analysis.level % 2) == 0;
  4761. else
  4762. iter->ltr = TRUE;
  4763. iter->cluster_start = 0;
  4764. iter->cluster_x = iter->run_x;
  4765. if (iter->run)
  4766. {
  4767. update_cluster (iter, iter->run->glyphs->log_clusters[0]);
  4768. }
  4769. else
  4770. {
  4771. iter->cluster_width = 0;
  4772. iter->character_position = 0;
  4773. iter->cluster_num_chars = 0;
  4774. iter->index = run_start_index;
  4775. }
  4776. }
  4777. /**
  4778. * pango_layout_iter_copy:
  4779. * @iter: (nullable): a #PangoLayoutIter, may be %NULL
  4780. *
  4781. * Copies a #PangoLayoutIter.
  4782. *
  4783. * Return value: (nullable): the newly allocated #PangoLayoutIter,
  4784. * which should be freed with pango_layout_iter_free(),
  4785. * or %NULL if @iter was %NULL.
  4786. *
  4787. * Since: 1.20
  4788. **/
  4789. PangoLayoutIter *
  4790. pango_layout_iter_copy (PangoLayoutIter *iter)
  4791. {
  4792. PangoLayoutIter *new;
  4793. if (iter == NULL)
  4794. return NULL;
  4795. new = g_slice_new (PangoLayoutIter);
  4796. new->layout = g_object_ref (iter->layout);
  4797. new->line_list_link = iter->line_list_link;
  4798. new->line = iter->line;
  4799. pango_layout_line_ref (new->line);
  4800. new->run_list_link = iter->run_list_link;
  4801. new->run = iter->run;
  4802. new->index = iter->index;
  4803. new->line_extents = NULL;
  4804. if (iter->line_extents != NULL)
  4805. {
  4806. new->line_extents = g_memdup (iter->line_extents,
  4807. iter->layout->line_count * sizeof (Extents));
  4808. }
  4809. new->line_index = iter->line_index;
  4810. new->run_x = iter->run_x;
  4811. new->run_width = iter->run_width;
  4812. new->ltr = iter->ltr;
  4813. new->cluster_x = iter->cluster_x;
  4814. new->cluster_width = iter->cluster_width;
  4815. new->cluster_start = iter->cluster_start;
  4816. new->next_cluster_glyph = iter->next_cluster_glyph;
  4817. new->cluster_num_chars = iter->cluster_num_chars;
  4818. new->character_position = iter->character_position;
  4819. new->layout_width = iter->layout_width;
  4820. return new;
  4821. }
  4822. G_DEFINE_BOXED_TYPE (PangoLayoutIter, pango_layout_iter,
  4823. pango_layout_iter_copy,
  4824. pango_layout_iter_free);
  4825. /**
  4826. * pango_layout_get_iter:
  4827. * @layout: a #PangoLayout
  4828. *
  4829. * Returns an iterator to iterate over the visual extents of the layout.
  4830. *
  4831. * Return value: the new #PangoLayoutIter that should be freed using
  4832. * pango_layout_iter_free().
  4833. **/
  4834. PangoLayoutIter*
  4835. pango_layout_get_iter (PangoLayout *layout)
  4836. {
  4837. PangoLayoutIter *iter;
  4838. g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
  4839. iter = g_slice_new (PangoLayoutIter);
  4840. _pango_layout_get_iter (layout, iter);
  4841. return iter;
  4842. }
  4843. void
  4844. _pango_layout_get_iter (PangoLayout *layout,
  4845. PangoLayoutIter*iter)
  4846. {
  4847. int run_start_index;
  4848. PangoRectangle logical_rect;
  4849. g_return_if_fail (PANGO_IS_LAYOUT (layout));
  4850. iter->layout = g_object_ref (layout);
  4851. pango_layout_check_lines (layout);
  4852. iter->line_list_link = layout->lines;
  4853. iter->line = iter->line_list_link->data;
  4854. pango_layout_line_ref (iter->line);
  4855. run_start_index = iter->line->start_index;
  4856. iter->run_list_link = iter->line->runs;
  4857. if (iter->run_list_link)
  4858. {
  4859. iter->run = iter->run_list_link->data;
  4860. run_start_index = iter->run->item->offset;
  4861. }
  4862. else
  4863. iter->run = NULL;
  4864. iter->line_extents = NULL;
  4865. pango_layout_get_extents_internal (layout,
  4866. NULL,
  4867. &logical_rect,
  4868. &iter->line_extents);
  4869. iter->layout_width = layout->width == -1 ? logical_rect.width : layout->width;
  4870. iter->line_index = 0;
  4871. update_run (iter, run_start_index);
  4872. }
  4873. void
  4874. _pango_layout_iter_destroy (PangoLayoutIter *iter)
  4875. {
  4876. if (iter == NULL)
  4877. return;
  4878. g_free (iter->line_extents);
  4879. pango_layout_line_unref (iter->line);
  4880. g_object_unref (iter->layout);
  4881. }
  4882. /**
  4883. * pango_layout_iter_free:
  4884. * @iter: (nullable): a #PangoLayoutIter, may be %NULL
  4885. *
  4886. * Frees an iterator that's no longer in use.
  4887. **/
  4888. void
  4889. pango_layout_iter_free (PangoLayoutIter *iter)
  4890. {
  4891. if (iter == NULL)
  4892. return;
  4893. _pango_layout_iter_destroy (iter);
  4894. g_slice_free (PangoLayoutIter, iter);
  4895. }
  4896. /**
  4897. * pango_layout_iter_get_index:
  4898. * @iter: a #PangoLayoutIter
  4899. *
  4900. * Gets the current byte index. Note that iterating forward by char
  4901. * moves in visual order, not logical order, so indexes may not be
  4902. * sequential. Also, the index may be equal to the length of the text
  4903. * in the layout, if on the %NULL run (see pango_layout_iter_get_run()).
  4904. *
  4905. * Return value: current byte index.
  4906. **/
  4907. int
  4908. pango_layout_iter_get_index (PangoLayoutIter *iter)
  4909. {
  4910. if (ITER_IS_INVALID (iter))
  4911. return 0;
  4912. return iter->index;
  4913. }
  4914. /**
  4915. * pango_layout_iter_get_run:
  4916. * @iter: a #PangoLayoutIter
  4917. *
  4918. * Gets the current run. When iterating by run, at the end of each
  4919. * line, there's a position with a %NULL run, so this function can return
  4920. * %NULL. The %NULL run at the end of each line ensures that all lines have
  4921. * at least one run, even lines consisting of only a newline.
  4922. *
  4923. * Use the faster pango_layout_iter_get_run_readonly() if you do not plan
  4924. * to modify the contents of the run (glyphs, glyph widths, etc.).
  4925. *
  4926. * Return value: (transfer none) (nullable): the current run.
  4927. **/
  4928. PangoLayoutRun*
  4929. pango_layout_iter_get_run (PangoLayoutIter *iter)
  4930. {
  4931. if (ITER_IS_INVALID (iter))
  4932. return NULL;
  4933. pango_layout_line_leaked (iter->line);
  4934. return iter->run;
  4935. }
  4936. /**
  4937. * pango_layout_iter_get_run_readonly:
  4938. * @iter: a #PangoLayoutIter
  4939. *
  4940. * Gets the current run. When iterating by run, at the end of each
  4941. * line, there's a position with a %NULL run, so this function can return
  4942. * %NULL. The %NULL run at the end of each line ensures that all lines have
  4943. * at least one run, even lines consisting of only a newline.
  4944. *
  4945. * This is a faster alternative to pango_layout_iter_get_run(),
  4946. * but the user is not expected
  4947. * to modify the contents of the run (glyphs, glyph widths, etc.).
  4948. *
  4949. * Return value: (transfer none) (nullable): the current run, that
  4950. * should not be modified.
  4951. *
  4952. * Since: 1.16
  4953. **/
  4954. PangoLayoutRun*
  4955. pango_layout_iter_get_run_readonly (PangoLayoutIter *iter)
  4956. {
  4957. if (ITER_IS_INVALID (iter))
  4958. return NULL;
  4959. pango_layout_line_leaked (iter->line);
  4960. return iter->run;
  4961. }
  4962. /* an inline-able version for local use */
  4963. static PangoLayoutLine*
  4964. _pango_layout_iter_get_line (PangoLayoutIter *iter)
  4965. {
  4966. return iter->line;
  4967. }
  4968. /**
  4969. * pango_layout_iter_get_line:
  4970. * @iter: a #PangoLayoutIter
  4971. *
  4972. * Gets the current line.
  4973. *
  4974. * Use the faster pango_layout_iter_get_line_readonly() if you do not plan
  4975. * to modify the contents of the line (glyphs, glyph widths, etc.).
  4976. *
  4977. * Return value: the current line.
  4978. **/
  4979. PangoLayoutLine*
  4980. pango_layout_iter_get_line (PangoLayoutIter *iter)
  4981. {
  4982. if (ITER_IS_INVALID (iter))
  4983. return NULL;
  4984. pango_layout_line_leaked (iter->line);
  4985. return iter->line;
  4986. }
  4987. /**
  4988. * pango_layout_iter_get_line_readonly:
  4989. * @iter: a #PangoLayoutIter
  4990. *
  4991. * Gets the current line for read-only access.
  4992. *
  4993. * This is a faster alternative to pango_layout_iter_get_line(),
  4994. * but the user is not expected
  4995. * to modify the contents of the line (glyphs, glyph widths, etc.).
  4996. *
  4997. * Return value: (transfer none): the current line, that should not be
  4998. * modified.
  4999. *
  5000. * Since: 1.16
  5001. **/
  5002. PangoLayoutLine*
  5003. pango_layout_iter_get_line_readonly (PangoLayoutIter *iter)
  5004. {
  5005. if (ITER_IS_INVALID (iter))
  5006. return NULL;
  5007. return iter->line;
  5008. }
  5009. /**
  5010. * pango_layout_iter_at_last_line:
  5011. * @iter: a #PangoLayoutIter
  5012. *
  5013. * Determines whether @iter is on the last line of the layout.
  5014. *
  5015. * Return value: %TRUE if @iter is on the last line.
  5016. **/
  5017. gboolean
  5018. pango_layout_iter_at_last_line (PangoLayoutIter *iter)
  5019. {
  5020. if (ITER_IS_INVALID (iter))
  5021. return FALSE;
  5022. return iter->line_index == iter->layout->line_count - 1;
  5023. }
  5024. /**
  5025. * pango_layout_iter_get_layout:
  5026. * @iter: a #PangoLayoutIter
  5027. *
  5028. * Gets the layout associated with a #PangoLayoutIter.
  5029. *
  5030. * Return value: (transfer none): the layout associated with @iter.
  5031. *
  5032. * Since: 1.20
  5033. **/
  5034. PangoLayout*
  5035. pango_layout_iter_get_layout (PangoLayoutIter *iter)
  5036. {
  5037. /* check is redundant as it simply checks that iter->layout is not NULL */
  5038. if (ITER_IS_INVALID (iter))
  5039. return NULL;
  5040. return iter->layout;
  5041. }
  5042. static gboolean
  5043. line_is_terminated (PangoLayoutIter *iter)
  5044. {
  5045. /* There is a real terminator at the end of each paragraph other
  5046. * than the last.
  5047. */
  5048. if (iter->line_list_link->next)
  5049. {
  5050. PangoLayoutLine *next_line = iter->line_list_link->next->data;
  5051. if (next_line->is_paragraph_start)
  5052. return TRUE;
  5053. }
  5054. return FALSE;
  5055. }
  5056. /* Moves to the next non-empty line. If @include_terminators
  5057. * is set, a line with just an explicit paragraph separator
  5058. * is considered non-empty.
  5059. */
  5060. static gboolean
  5061. next_nonempty_line (PangoLayoutIter *iter,
  5062. gboolean include_terminators)
  5063. {
  5064. gboolean result;
  5065. while (TRUE)
  5066. {
  5067. result = pango_layout_iter_next_line (iter);
  5068. if (!result)
  5069. break;
  5070. if (iter->line->runs)
  5071. break;
  5072. if (include_terminators && line_is_terminated (iter))
  5073. break;
  5074. }
  5075. return result;
  5076. }
  5077. /* Moves to the next non-empty run. If @include_terminators
  5078. * is set, the trailing run at the end of a line with an explicit
  5079. * paragraph separator is considered non-empty.
  5080. */
  5081. static gboolean
  5082. next_nonempty_run (PangoLayoutIter *iter,
  5083. gboolean include_terminators)
  5084. {
  5085. gboolean result;
  5086. while (TRUE)
  5087. {
  5088. result = pango_layout_iter_next_run (iter);
  5089. if (!result)
  5090. break;
  5091. if (iter->run)
  5092. break;
  5093. if (include_terminators && line_is_terminated (iter))
  5094. break;
  5095. }
  5096. return result;
  5097. }
  5098. /* Like pango_layout_next_cluster(), but if @include_terminators
  5099. * is set, includes the fake runs/clusters for empty lines.
  5100. * (But not positions introduced by line wrapping).
  5101. */
  5102. static gboolean
  5103. next_cluster_internal (PangoLayoutIter *iter,
  5104. gboolean include_terminators)
  5105. {
  5106. PangoGlyphString *gs;
  5107. int next_start;
  5108. if (ITER_IS_INVALID (iter))
  5109. return FALSE;
  5110. if (iter->run == NULL)
  5111. return next_nonempty_line (iter, include_terminators);
  5112. gs = iter->run->glyphs;
  5113. next_start = iter->next_cluster_glyph;
  5114. if (next_start == gs->num_glyphs)
  5115. {
  5116. return next_nonempty_run (iter, include_terminators);
  5117. }
  5118. else
  5119. {
  5120. iter->cluster_start = next_start;
  5121. iter->cluster_x += iter->cluster_width;
  5122. update_cluster(iter, gs->log_clusters[iter->cluster_start]);
  5123. return TRUE;
  5124. }
  5125. }
  5126. /**
  5127. * pango_layout_iter_next_char:
  5128. * @iter: a #PangoLayoutIter
  5129. *
  5130. * Moves @iter forward to the next character in visual order. If @iter was already at
  5131. * the end of the layout, returns %FALSE.
  5132. *
  5133. * Return value: whether motion was possible.
  5134. **/
  5135. gboolean
  5136. pango_layout_iter_next_char (PangoLayoutIter *iter)
  5137. {
  5138. const char *text;
  5139. if (ITER_IS_INVALID (iter))
  5140. return FALSE;
  5141. if (iter->run == NULL)
  5142. {
  5143. /* We need to fake an iterator position in the middle of a \r\n line terminator */
  5144. if (line_is_terminated (iter) &&
  5145. strncmp (iter->layout->text + iter->line->start_index + iter->line->length, "\r\n", 2) == 0 &&
  5146. iter->character_position == 0)
  5147. {
  5148. iter->character_position++;
  5149. return TRUE;
  5150. }
  5151. return next_nonempty_line (iter, TRUE);
  5152. }
  5153. iter->character_position++;
  5154. if (iter->character_position >= iter->cluster_num_chars)
  5155. return next_cluster_internal (iter, TRUE);
  5156. text = iter->layout->text;
  5157. if (iter->ltr)
  5158. iter->index = g_utf8_next_char (text + iter->index) - text;
  5159. else
  5160. iter->index = g_utf8_prev_char (text + iter->index) - text;
  5161. return TRUE;
  5162. }
  5163. /**
  5164. * pango_layout_iter_next_cluster:
  5165. * @iter: a #PangoLayoutIter
  5166. *
  5167. * Moves @iter forward to the next cluster in visual order. If @iter
  5168. * was already at the end of the layout, returns %FALSE.
  5169. *
  5170. * Return value: whether motion was possible.
  5171. **/
  5172. gboolean
  5173. pango_layout_iter_next_cluster (PangoLayoutIter *iter)
  5174. {
  5175. return next_cluster_internal (iter, FALSE);
  5176. }
  5177. /**
  5178. * pango_layout_iter_next_run:
  5179. * @iter: a #PangoLayoutIter
  5180. *
  5181. * Moves @iter forward to the next run in visual order. If @iter was
  5182. * already at the end of the layout, returns %FALSE.
  5183. *
  5184. * Return value: whether motion was possible.
  5185. **/
  5186. gboolean
  5187. pango_layout_iter_next_run (PangoLayoutIter *iter)
  5188. {
  5189. int next_run_start; /* byte index */
  5190. GSList *next_link;
  5191. if (ITER_IS_INVALID (iter))
  5192. return FALSE;
  5193. if (iter->run == NULL)
  5194. return pango_layout_iter_next_line (iter);
  5195. next_link = iter->run_list_link->next;
  5196. if (next_link == NULL)
  5197. {
  5198. /* Moving on to the zero-width "virtual run" at the end of each
  5199. * line
  5200. */
  5201. next_run_start = iter->run->item->offset + iter->run->item->length;
  5202. iter->run = NULL;
  5203. iter->run_list_link = NULL;
  5204. }
  5205. else
  5206. {
  5207. iter->run_list_link = next_link;
  5208. iter->run = iter->run_list_link->data;
  5209. next_run_start = iter->run->item->offset;
  5210. }
  5211. update_run (iter, next_run_start);
  5212. return TRUE;
  5213. }
  5214. /**
  5215. * pango_layout_iter_next_line:
  5216. * @iter: a #PangoLayoutIter
  5217. *
  5218. * Moves @iter forward to the start of the next line. If @iter is
  5219. * already on the last line, returns %FALSE.
  5220. *
  5221. * Return value: whether motion was possible.
  5222. **/
  5223. gboolean
  5224. pango_layout_iter_next_line (PangoLayoutIter *iter)
  5225. {
  5226. GSList *next_link;
  5227. if (ITER_IS_INVALID (iter))
  5228. return FALSE;
  5229. next_link = iter->line_list_link->next;
  5230. if (next_link == NULL)
  5231. return FALSE;
  5232. iter->line_list_link = next_link;
  5233. pango_layout_line_unref (iter->line);
  5234. iter->line = iter->line_list_link->data;
  5235. pango_layout_line_ref (iter->line);
  5236. iter->run_list_link = iter->line->runs;
  5237. if (iter->run_list_link)
  5238. iter->run = iter->run_list_link->data;
  5239. else
  5240. iter->run = NULL;
  5241. iter->line_index ++;
  5242. update_run (iter, iter->line->start_index);
  5243. return TRUE;
  5244. }
  5245. /**
  5246. * pango_layout_iter_get_char_extents:
  5247. * @iter: a #PangoLayoutIter
  5248. * @logical_rect: (out caller-allocates): rectangle to fill with
  5249. * logical extents
  5250. *
  5251. * Gets the extents of the current character, in layout coordinates
  5252. * (origin is the top left of the entire layout). Only logical extents
  5253. * can sensibly be obtained for characters; ink extents make sense only
  5254. * down to the level of clusters.
  5255. *
  5256. **/
  5257. void
  5258. pango_layout_iter_get_char_extents (PangoLayoutIter *iter,
  5259. PangoRectangle *logical_rect)
  5260. {
  5261. PangoRectangle cluster_rect;
  5262. int x0, x1;
  5263. if (ITER_IS_INVALID (iter))
  5264. return;
  5265. if (logical_rect == NULL)
  5266. return;
  5267. pango_layout_iter_get_cluster_extents (iter, NULL, &cluster_rect);
  5268. if (iter->run == NULL)
  5269. {
  5270. /* When on the NULL run, cluster, char, and run all have the
  5271. * same extents
  5272. */
  5273. *logical_rect = cluster_rect;
  5274. return;
  5275. }
  5276. if (iter->cluster_num_chars)
  5277. {
  5278. x0 = (iter->character_position * cluster_rect.width) / iter->cluster_num_chars;
  5279. x1 = ((iter->character_position + 1) * cluster_rect.width) / iter->cluster_num_chars;
  5280. }
  5281. else
  5282. {
  5283. x0 = x1 = 0;
  5284. }
  5285. logical_rect->width = x1 - x0;
  5286. logical_rect->height = cluster_rect.height;
  5287. logical_rect->y = cluster_rect.y;
  5288. logical_rect->x = cluster_rect.x + x0;
  5289. }
  5290. /**
  5291. * pango_layout_iter_get_cluster_extents:
  5292. * @iter: a #PangoLayoutIter
  5293. * @ink_rect: (out) (allow-none): rectangle to fill with ink extents, or %NULL
  5294. * @logical_rect: (out) (allow-none): rectangle to fill with logical extents, or %NULL
  5295. *
  5296. * Gets the extents of the current cluster, in layout coordinates
  5297. * (origin is the top left of the entire layout).
  5298. *
  5299. **/
  5300. void
  5301. pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter,
  5302. PangoRectangle *ink_rect,
  5303. PangoRectangle *logical_rect)
  5304. {
  5305. if (ITER_IS_INVALID (iter))
  5306. return;
  5307. if (iter->run == NULL)
  5308. {
  5309. /* When on the NULL run, cluster, char, and run all have the
  5310. * same extents
  5311. */
  5312. pango_layout_iter_get_run_extents (iter, ink_rect, logical_rect);
  5313. return;
  5314. }
  5315. pango_glyph_string_extents_range (iter->run->glyphs,
  5316. iter->cluster_start,
  5317. iter->next_cluster_glyph,
  5318. iter->run->item->analysis.font,
  5319. ink_rect,
  5320. logical_rect);
  5321. if (ink_rect)
  5322. {
  5323. ink_rect->x += iter->cluster_x;
  5324. offset_y (iter, &ink_rect->y);
  5325. }
  5326. if (logical_rect)
  5327. {
  5328. g_assert (logical_rect->width == iter->cluster_width);
  5329. logical_rect->x += iter->cluster_x;
  5330. offset_y (iter, &logical_rect->y);
  5331. }
  5332. }
  5333. /**
  5334. * pango_layout_iter_get_run_extents:
  5335. * @iter: a #PangoLayoutIter
  5336. * @ink_rect: (out) (allow-none): rectangle to fill with ink extents, or %NULL
  5337. * @logical_rect: (out) (allow-none): rectangle to fill with logical extents, or %NULL
  5338. *
  5339. * Gets the extents of the current run in layout coordinates
  5340. * (origin is the top left of the entire layout).
  5341. *
  5342. **/
  5343. void
  5344. pango_layout_iter_get_run_extents (PangoLayoutIter *iter,
  5345. PangoRectangle *ink_rect,
  5346. PangoRectangle *logical_rect)
  5347. {
  5348. if (G_UNLIKELY (!ink_rect && !logical_rect))
  5349. return;
  5350. if (ITER_IS_INVALID (iter))
  5351. return;
  5352. if (iter->run)
  5353. {
  5354. pango_layout_run_get_extents (iter->run, ink_rect, logical_rect);
  5355. if (ink_rect)
  5356. {
  5357. offset_y (iter, &ink_rect->y);
  5358. ink_rect->x += iter->run_x;
  5359. }
  5360. if (logical_rect)
  5361. {
  5362. offset_y (iter, &logical_rect->y);
  5363. logical_rect->x += iter->run_x;
  5364. }
  5365. }
  5366. else
  5367. {
  5368. /* The empty run at the end of a line */
  5369. pango_layout_iter_get_line_extents (iter, ink_rect, logical_rect);
  5370. if (ink_rect)
  5371. {
  5372. ink_rect->x = iter->run_x;
  5373. ink_rect->width = 0;
  5374. }
  5375. if (logical_rect)
  5376. {
  5377. logical_rect->x = iter->run_x;
  5378. logical_rect->width = 0;
  5379. }
  5380. }
  5381. }
  5382. /**
  5383. * pango_layout_iter_get_line_extents:
  5384. * @iter: a #PangoLayoutIter
  5385. * @ink_rect: (out) (allow-none): rectangle to fill with ink extents, or %NULL
  5386. * @logical_rect: (out) (allow-none): rectangle to fill with logical extents, or %NULL
  5387. *
  5388. * Obtains the extents of the current line. @ink_rect or @logical_rect
  5389. * can be %NULL if you aren't interested in them. Extents are in layout
  5390. * coordinates (origin is the top-left corner of the entire
  5391. * #PangoLayout). Thus the extents returned by this function will be
  5392. * the same width/height but not at the same x/y as the extents
  5393. * returned from pango_layout_line_get_extents().
  5394. *
  5395. **/
  5396. void
  5397. pango_layout_iter_get_line_extents (PangoLayoutIter *iter,
  5398. PangoRectangle *ink_rect,
  5399. PangoRectangle *logical_rect)
  5400. {
  5401. const Extents *ext;
  5402. if (ITER_IS_INVALID (iter))
  5403. return;
  5404. ext = &iter->line_extents[iter->line_index];
  5405. if (ink_rect)
  5406. {
  5407. get_line_extents_layout_coords (iter->layout, iter->line,
  5408. iter->layout_width,
  5409. ext->logical_rect.y,
  5410. NULL,
  5411. ink_rect,
  5412. NULL);
  5413. }
  5414. if (logical_rect)
  5415. *logical_rect = ext->logical_rect;
  5416. }
  5417. /**
  5418. * pango_layout_iter_get_line_yrange:
  5419. * @iter: a #PangoLayoutIter
  5420. * @y0_: (out) (allow-none): start of line, or %NULL
  5421. * @y1_: (out) (allow-none): end of line, or %NULL
  5422. *
  5423. * Divides the vertical space in the #PangoLayout being iterated over
  5424. * between the lines in the layout, and returns the space belonging to
  5425. * the current line. A line's range includes the line's logical
  5426. * extents, plus half of the spacing above and below the line, if
  5427. * pango_layout_set_spacing() has been called to set layout spacing.
  5428. * The Y positions are in layout coordinates (origin at top left of the
  5429. * entire layout).
  5430. *
  5431. **/
  5432. void
  5433. pango_layout_iter_get_line_yrange (PangoLayoutIter *iter,
  5434. int *y0,
  5435. int *y1)
  5436. {
  5437. const Extents *ext;
  5438. int half_spacing;
  5439. if (ITER_IS_INVALID (iter))
  5440. return;
  5441. ext = &iter->line_extents[iter->line_index];
  5442. half_spacing = iter->layout->spacing / 2;
  5443. /* Note that if layout->spacing is odd, the remainder spacing goes
  5444. * above the line (this is pretty arbitrary of course)
  5445. */
  5446. if (y0)
  5447. {
  5448. /* No spacing above the first line */
  5449. if (iter->line_index == 0)
  5450. *y0 = ext->logical_rect.y;
  5451. else
  5452. *y0 = ext->logical_rect.y - (iter->layout->spacing - half_spacing);
  5453. }
  5454. if (y1)
  5455. {
  5456. /* No spacing below the last line */
  5457. if (iter->line_index == iter->layout->line_count - 1)
  5458. *y1 = ext->logical_rect.y + ext->logical_rect.height;
  5459. else
  5460. *y1 = ext->logical_rect.y + ext->logical_rect.height + half_spacing;
  5461. }
  5462. }
  5463. /**
  5464. * pango_layout_iter_get_baseline:
  5465. * @iter: a #PangoLayoutIter
  5466. *
  5467. * Gets the Y position of the current line's baseline, in layout
  5468. * coordinates (origin at top left of the entire layout).
  5469. *
  5470. * Return value: baseline of current line.
  5471. **/
  5472. int
  5473. pango_layout_iter_get_baseline (PangoLayoutIter *iter)
  5474. {
  5475. if (ITER_IS_INVALID (iter))
  5476. return 0;
  5477. return iter->line_extents[iter->line_index].baseline;
  5478. }
  5479. /**
  5480. * pango_layout_iter_get_layout_extents:
  5481. * @iter: a #PangoLayoutIter
  5482. * @ink_rect: (out) (allow-none): rectangle to fill with ink extents,
  5483. * or %NULL
  5484. * @logical_rect: (out) (allow-none): rectangle to fill with logical
  5485. * extents, or %NULL
  5486. *
  5487. * Obtains the extents of the #PangoLayout being iterated
  5488. * over. @ink_rect or @logical_rect can be %NULL if you
  5489. * aren't interested in them.
  5490. *
  5491. **/
  5492. void
  5493. pango_layout_iter_get_layout_extents (PangoLayoutIter *iter,
  5494. PangoRectangle *ink_rect,
  5495. PangoRectangle *logical_rect)
  5496. {
  5497. if (ITER_IS_INVALID (iter))
  5498. return;
  5499. pango_layout_get_extents (iter->layout, ink_rect, logical_rect);
  5500. }