1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This file defines utility functions for eliding and formatting UI text.
6 
7 #ifndef UI_GFX_TEXT_ELIDER_H_
8 #define UI_GFX_TEXT_ELIDER_H_
9 
10 #include <stddef.h>
11 
12 #include <string>
13 #include <vector>
14 
15 #include "base/macros.h"
16 #include "base/optional.h"
17 #include "base/strings/string16.h"
18 #include "build/build_config.h"
19 #include "ui/gfx/gfx_export.h"
20 #include "ui/gfx/text_constants.h"
21 
22 namespace base {
23 class FilePath;
24 }
25 
26 namespace gfx {
27 class FontList;
28 
29 GFX_EXPORT extern const char kEllipsis[];
30 GFX_EXPORT extern const base::char16 kEllipsisUTF16[];
31 GFX_EXPORT extern const base::char16 kForwardSlash;
32 
33 // Helper class to split + elide text, while respecting UTF-16 surrogate pairs
34 // and combining character sequences.
35 class GFX_EXPORT StringSlicer {
36  public:
37   // Warning: Retains a reference to |text| and |ellipsis|. They must have a
38   // longer lifetime than the StringSlicer.
39   //
40   // Note: if |elide_whitespace| is base::nullopt, the default whitespace
41   // elision strategy for the type of elision being done will be chosen.
42   // Defaults are to trim for beginning and end elision; no trimming for middle
43   // elision.
44   StringSlicer(const base::string16& text,
45                const base::string16& ellipsis,
46                bool elide_in_middle,
47                bool elide_at_beginning,
48                base::Optional<bool> elide_whitespace = base::nullopt);
49 
50   // Cuts |text_| to be at most |length| UTF-16 code units long. If
51   // |elide_in_middle_| is true, the middle of the string is removed to leave
52   // equal-length pieces from the beginning and end of the string; otherwise,
53   // the end of the string is removed and only the beginning remains. If
54   // |insert_ellipsis| is true, then an ellipsis character will be inserted at
55   // the cut point (note that the ellipsis will does not count towards the
56   // |length| limit).
57   // Note: Characters may still be omitted even if |length| is the full string
58   // length, if surrogate pairs fall on the split boundary.
59   base::string16 CutString(size_t length, bool insert_ellipsis) const;
60 
61  private:
62   // The text to be sliced.
63   const base::string16& text_;
64 
65   // Ellipsis string to use.
66   const base::string16& ellipsis_;
67 
68   // If true, the middle of the string will be elided.
69   const bool elide_in_middle_;
70 
71   // If true, the beginning of the string will be elided.
72   const bool elide_at_beginning_;
73 
74   // How whitespace around an elision point is handled.
75   const bool elide_whitespace_;
76 
77   DISALLOW_COPY_AND_ASSIGN(StringSlicer);
78 };
79 
80 // Elides |text| to fit the |available_pixel_width| with the specified behavior.
81 GFX_EXPORT base::string16 ElideText(const base::string16& text,
82                                     const gfx::FontList& font_list,
83                                     float available_pixel_width,
84                                     ElideBehavior elide_behavior);
85 
86 // Elide a filename to fit a given pixel width, with an emphasis on not hiding
87 // the extension unless we have to. If filename contains a path, the path will
88 // be removed if filename doesn't fit into available_pixel_width. The elided
89 // filename is forced to have LTR directionality, which means that in RTL UI
90 // the elided filename is wrapped with LRE (Left-To-Right Embedding) mark and
91 // PDF (Pop Directional Formatting) mark.
92 GFX_EXPORT base::string16 ElideFilename(const base::FilePath& filename,
93                                         const gfx::FontList& font_list,
94                                         float available_pixel_width);
95 
96 // Functions to elide strings when the font information is unknown. As opposed
97 // to the above functions, ElideString() and ElideRectangleString() operate in
98 // terms of character units, not pixels.
99 
100 // If the size of |input| is more than |max_len|, this function returns
101 // true and |input| is shortened into |output| by removing chars in the
102 // middle (they are replaced with up to 3 dots, as size permits).
103 // Ex: ElideString(ASCIIToUTF16("Hello"), 10, &str) puts Hello in str and
104 // returns false.  ElideString(ASCIIToUTF16("Hello my name is Tom"), 10, &str)
105 // puts "Hell...Tom" in str and returns true.
106 // TODO(tsepez): Doesn't handle UTF-16 surrogate pairs properly.
107 // TODO(tsepez): Doesn't handle bidi properly.
108 GFX_EXPORT bool ElideString(const base::string16& input, size_t max_len,
109                             base::string16* output);
110 
111 // Reformat |input| into |output| so that it fits into a |max_rows| by
112 // |max_cols| rectangle of characters.  Input newlines are respected, but
113 // lines that are too long are broken into pieces.  If |strict| is true,
114 // we break first at naturally occurring whitespace boundaries, otherwise
115 // we assume some other mechanism will do this in approximately the same
116 // spot after the fact.  If the word itself is too long, we always break
117 // intra-word (respecting UTF-16 surrogate pairs) as necessary. Truncation
118 // (indicated by an added 3 dots) occurs if the result is still too long.
119 //  Returns true if the input had to be truncated (and not just reformatted).
120 GFX_EXPORT bool ElideRectangleString(const base::string16& input,
121                                      size_t max_rows,
122                                      size_t max_cols,
123                                      bool strict,
124                                      base::string16* output);
125 
126 // Indicates whether the |available_pixel_width| by |available_pixel_height|
127 // rectangle passed to |ElideRectangleText()| had insufficient space to
128 // accommodate the given |text|, leading to elision or truncation.
129 enum ReformattingResultFlags {
130   INSUFFICIENT_SPACE_HORIZONTAL = 1 << 0,
131   INSUFFICIENT_SPACE_VERTICAL = 1 << 1,
132   INSUFFICIENT_SPACE_FOR_FIRST_WORD = 1 << 2,
133 };
134 
135 // Reformats |text| into output vector |lines| so that the resulting text fits
136 // into an |available_pixel_width| by |available_pixel_height| rectangle with
137 // the specified |font_list|. Input newlines are respected, but lines that are
138 // too long are broken into pieces. For words that are too wide to fit on a
139 // single line, the wrapping behavior can be specified with the |wrap_behavior|
140 // param. Returns a combination of |ReformattingResultFlags| that indicate
141 // whether the given rectangle had insufficient space to accommodate |text|,
142 // leading to elision or truncation (and not just reformatting).
143 GFX_EXPORT int ElideRectangleText(const base::string16& text,
144                                   const gfx::FontList& font_list,
145                                   float available_pixel_width,
146                                   int available_pixel_height,
147                                   WordWrapBehavior wrap_behavior,
148                                   std::vector<base::string16>* lines);
149 
150 // Truncates |string| to |length| characters. This breaks the string according
151 // to the specified |break_type|, which must be either WORD_BREAK or
152 // CHARACTER_BREAK, and adds the horizontal ellipsis character (unicode
153 // character 0x2026) to render "...". The supplied string is returned if the
154 // string has |length| characters or less.
155 GFX_EXPORT base::string16 TruncateString(const base::string16& string,
156                                          size_t length,
157                                          BreakType break_type);
158 
159 }  // namespace gfx
160 
161 #endif  // UI_GFX_TEXT_ELIDER_H_
162