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.
5 #include "content/child/browser_font_resource_trusted.h"
7 #include "base/strings/string_util.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "content/public/common/web_preferences.h"
10 #include "ppapi/c/dev/ppb_font_dev.h"
11 #include "ppapi/proxy/connection.h"
12 #include "ppapi/shared_impl/ppapi_preferences.h"
13 #include "ppapi/shared_impl/var.h"
14 #include "ppapi/thunk/enter.h"
15 #include "ppapi/thunk/ppb_image_data_api.h"
16 #include "ppapi/thunk/thunk.h"
17 #include "skia/ext/platform_canvas.h"
18 #include "third_party/WebKit/public/platform/WebCanvas.h"
19 #include "third_party/WebKit/public/platform/WebFloatPoint.h"
20 #include "third_party/WebKit/public/platform/WebFloatRect.h"
21 #include "third_party/WebKit/public/platform/WebRect.h"
22 #include "third_party/WebKit/public/web/WebFont.h"
23 #include "third_party/WebKit/public/web/WebFontDescription.h"
24 #include "third_party/WebKit/public/web/WebTextRun.h"
25 #include "third_party/icu/source/common/unicode/ubidi.h"
26 #include "third_party/skia/include/core/SkRect.h"
28 using ppapi::StringVar
;
29 using ppapi::thunk::EnterResourceNoLock
;
30 using ppapi::thunk::PPB_ImageData_API
;
31 using blink::WebFloatPoint
;
32 using blink::WebFloatRect
;
34 using blink::WebFontDescription
;
36 using blink::WebTextRun
;
37 using blink::WebCanvas
;
43 // Same as WebPreferences::kCommonScript. I'd use that directly here, but get an
44 // undefined reference linker error.
45 const char kCommonScript
[] = "Zyyy";
47 base::string16
GetFontFromMap(const ScriptFontFamilyMap
& map
,
48 const std::string
& script
) {
49 ScriptFontFamilyMap::const_iterator it
= map
.find(script
);
52 return base::string16();
55 // Splits a PP_BrowserFont_Trusted_TextRun into a sequence or LTR and RTL
56 // WebTextRuns that can be used for WebKit. Normally WebKit does this for us,
57 // but the font drawing and measurement routines we call happen after this
58 // step. So for correct rendering of RTL content, we need to do it ourselves.
59 class TextRunCollection
{
61 explicit TextRunCollection(const PP_BrowserFont_Trusted_TextRun
& run
)
64 StringVar
* text_string
= StringVar::FromPPVar(run
.text
);
66 return; // Leave num_runs_ = 0 so we'll do nothing.
67 text_
= base::UTF8ToUTF16(text_string
->value());
69 if (run
.override_direction
) {
70 // Skip autodetection.
72 override_run_
= WebTextRun(text_
, PP_ToBool(run
.rtl
), true);
75 UErrorCode uerror
= U_ZERO_ERROR
;
76 ubidi_setPara(bidi_
, text_
.data(), text_
.size(), run
.rtl
, NULL
, &uerror
);
77 if (U_SUCCESS(uerror
))
78 num_runs_
= ubidi_countRuns(bidi_
, &uerror
);
82 ~TextRunCollection() {
87 const base::string16
& text() const { return text_
; }
88 int num_runs() const { return num_runs_
; }
90 // Returns a WebTextRun with the info for the run at the given index.
91 // The range covered by the run is in the two output params.
92 WebTextRun
GetRunAt(int index
, int32_t* run_start
, int32_t* run_len
) const {
93 DCHECK(index
< num_runs_
);
95 bool run_rtl
= !!ubidi_getVisualRun(bidi_
, index
, run_start
, run_len
);
96 return WebTextRun(base::string16(&text_
[*run_start
], *run_len
),
100 // Override run, return the single one.
103 *run_len
= static_cast<int32_t>(text_
.size());
104 return override_run_
;
108 // Will be null if we skipped autodetection.
111 // Text of all the runs.
112 base::string16 text_
;
116 // When the content specifies override_direction (bidi_ is null) then this
117 // will contain the single text run for WebKit.
118 WebTextRun override_run_
;
120 DISALLOW_COPY_AND_ASSIGN(TextRunCollection
);
123 bool PPTextRunToWebTextRun(const PP_BrowserFont_Trusted_TextRun
& text
,
125 StringVar
* text_string
= StringVar::FromPPVar(text
.text
);
129 *run
= WebTextRun(base::UTF8ToUTF16(text_string
->value()),
131 PP_ToBool(text
.override_direction
));
135 // The PP_* version lacks "None", so is just one value shifted from the
136 // WebFontDescription version. These values are checked in
137 // PPFontDescToWebFontDesc to make sure the conversion is correct. This is a
138 // macro so it can also be used in the static_asserts.
139 #define PP_FAMILY_TO_WEB_FAMILY(f) \
140 static_cast<WebFontDescription::GenericFamily>(f + 1)
142 // Assumes the given PP_FontDescription has been validated.
143 WebFontDescription
PPFontDescToWebFontDesc(
144 const PP_BrowserFont_Trusted_Description
& font
,
145 const ppapi::Preferences
& prefs
) {
146 // Verify that the enums match so we can just static cast.
147 static_assert(static_cast<int>(WebFontDescription::Weight100
) ==
148 static_cast<int>(PP_BROWSERFONT_TRUSTED_WEIGHT_100
),
150 static_assert(static_cast<int>(WebFontDescription::Weight900
) ==
151 static_cast<int>(PP_BROWSERFONT_TRUSTED_WEIGHT_900
),
153 static_assert(WebFontDescription::GenericFamilyStandard
==
154 PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_DEFAULT
),
156 static_assert(WebFontDescription::GenericFamilySerif
==
157 PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_SERIF
),
159 static_assert(WebFontDescription::GenericFamilySansSerif
==
160 PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_SANSSERIF
),
162 static_assert(WebFontDescription::GenericFamilyMonospace
==
163 PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_MONOSPACE
),
166 StringVar
* face_name
= StringVar::FromPPVar(font
.face
); // Possibly null.
168 WebFontDescription result
;
169 base::string16 resolved_family
;
170 if (!face_name
|| face_name
->value().empty()) {
171 // Resolve the generic family.
172 switch (font
.family
) {
173 case PP_BROWSERFONT_TRUSTED_FAMILY_SERIF
:
174 resolved_family
= GetFontFromMap(prefs
.serif_font_family_map
,
177 case PP_BROWSERFONT_TRUSTED_FAMILY_SANSSERIF
:
178 resolved_family
= GetFontFromMap(prefs
.sans_serif_font_family_map
,
181 case PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE
:
182 resolved_family
= GetFontFromMap(prefs
.fixed_font_family_map
,
185 case PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT
:
187 resolved_family
= GetFontFromMap(prefs
.standard_font_family_map
,
192 // Use the exact font.
193 resolved_family
= base::UTF8ToUTF16(face_name
->value());
195 result
.family
= resolved_family
;
197 result
.genericFamily
= PP_FAMILY_TO_WEB_FAMILY(font
.family
);
199 if (font
.size
== 0) {
200 // Resolve the default font size, using the resolved family to see if
201 // we should use the fixed or regular font size. It's difficult at this
202 // level to detect if the requested font is fixed width, so we only apply
203 // the alternate font size to the default fixed font family.
204 if (base::StringToLowerASCII(resolved_family
) ==
205 base::StringToLowerASCII(GetFontFromMap(prefs
.fixed_font_family_map
,
207 result
.size
= static_cast<float>(prefs
.default_fixed_font_size
);
209 result
.size
= static_cast<float>(prefs
.default_font_size
);
211 // Use the exact size.
212 result
.size
= static_cast<float>(font
.size
);
215 result
.italic
= font
.italic
!= PP_FALSE
;
216 result
.smallCaps
= font
.small_caps
!= PP_FALSE
;
217 result
.weight
= static_cast<WebFontDescription::Weight
>(font
.weight
);
218 result
.letterSpacing
= static_cast<short>(font
.letter_spacing
);
219 result
.wordSpacing
= static_cast<short>(font
.word_spacing
);
226 bool BrowserFontResource_Trusted::IsPPFontDescriptionValid(
227 const PP_BrowserFont_Trusted_Description
& desc
) {
228 // Check validity of string. We can't check the actual text since we could
229 // be on the wrong thread and don't know if we're in the plugin or the host.
230 if (desc
.face
.type
!= PP_VARTYPE_STRING
&&
231 desc
.face
.type
!= PP_VARTYPE_UNDEFINED
)
234 // Check enum ranges.
235 if (static_cast<int>(desc
.family
) < PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT
||
236 static_cast<int>(desc
.family
) > PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE
)
238 if (static_cast<int>(desc
.weight
) < PP_BROWSERFONT_TRUSTED_WEIGHT_100
||
239 static_cast<int>(desc
.weight
) > PP_BROWSERFONT_TRUSTED_WEIGHT_900
)
242 // Check for excessive sizes which may cause layout to get confused.
249 BrowserFontResource_Trusted::BrowserFontResource_Trusted(
250 ppapi::proxy::Connection connection
,
251 PP_Instance instance
,
252 const PP_BrowserFont_Trusted_Description
& desc
,
253 const ppapi::Preferences
& prefs
)
254 : PluginResource(connection
, instance
),
255 font_(WebFont::create(PPFontDescToWebFontDesc(desc
, prefs
))) {
258 BrowserFontResource_Trusted::~BrowserFontResource_Trusted() {
261 ppapi::thunk::PPB_BrowserFont_Trusted_API
*
262 BrowserFontResource_Trusted::AsPPB_BrowserFont_Trusted_API() {
266 PP_Bool
BrowserFontResource_Trusted::Describe(
267 PP_BrowserFont_Trusted_Description
* description
,
268 PP_BrowserFont_Trusted_Metrics
* metrics
) {
269 if (description
->face
.type
!= PP_VARTYPE_UNDEFINED
)
272 // While converting the other way in PPFontDescToWebFontDesc we validated
273 // that the enums can be casted.
274 WebFontDescription web_desc
= font_
->fontDescription();
276 StringVar::StringToPPVar(base::UTF16ToUTF8(web_desc
.family
));
277 description
->family
=
278 static_cast<PP_BrowserFont_Trusted_Family
>(web_desc
.genericFamily
);
279 description
->size
= static_cast<uint32_t>(web_desc
.size
);
280 description
->weight
= static_cast<PP_BrowserFont_Trusted_Weight
>(
282 description
->italic
= web_desc
.italic
? PP_TRUE
: PP_FALSE
;
283 description
->small_caps
= web_desc
.smallCaps
? PP_TRUE
: PP_FALSE
;
284 description
->letter_spacing
= static_cast<int32_t>(web_desc
.letterSpacing
);
285 description
->word_spacing
= static_cast<int32_t>(web_desc
.wordSpacing
);
287 metrics
->height
= font_
->height();
288 metrics
->ascent
= font_
->ascent();
289 metrics
->descent
= font_
->descent();
290 metrics
->line_spacing
= font_
->lineSpacing();
291 metrics
->x_height
= static_cast<int32_t>(font_
->xHeight());
293 // Convert the string.
297 PP_Bool
BrowserFontResource_Trusted::DrawTextAt(
298 PP_Resource image_data
,
299 const PP_BrowserFont_Trusted_TextRun
* text
,
300 const PP_Point
* position
,
303 PP_Bool image_data_is_opaque
) {
304 PP_Bool result
= PP_FALSE
;
305 // Get and map the image data we're painting to.
306 EnterResourceNoLock
<PPB_ImageData_API
> enter(image_data
, true);
310 PPB_ImageData_API
* image
= static_cast<PPB_ImageData_API
*>(
312 SkCanvas
* canvas
= image
->GetPlatformCanvas();
313 bool needs_unmapping
= false;
315 needs_unmapping
= true;
317 canvas
= image
->GetPlatformCanvas();
319 return result
; // Failure mapping.
322 if (!PP_ToBool(image_data_is_opaque
)) {
323 // Ideally, LCD text should be configured at canvas creation time using
324 // SkSurfaceProps. But because the API exposes image_data_is_opaque per
325 // draw text call (allowing clients to essentially change their mind),
326 // we have to handle it here.
329 void* pixels
= canvas
->accessTopLayerPixels(&info
, &row_bytes
);
334 if (!bm
.installPixels(info
, pixels
, row_bytes
))
337 SkSurfaceProps
props(0, kUnknown_SkPixelGeometry
);
338 SkCanvas
temp_canvas(bm
, props
);
340 DrawTextToCanvas(&temp_canvas
, *text
, position
, color
, clip
);
342 DrawTextToCanvas(canvas
, *text
, position
, color
, clip
);
350 int32_t BrowserFontResource_Trusted::MeasureText(
351 const PP_BrowserFont_Trusted_TextRun
* text
) {
353 if (!PPTextRunToWebTextRun(*text
, &run
))
355 return font_
->calculateWidth(run
);
358 uint32_t BrowserFontResource_Trusted::CharacterOffsetForPixel(
359 const PP_BrowserFont_Trusted_TextRun
* text
,
360 int32_t pixel_position
) {
361 TextRunCollection
runs(*text
);
362 int32_t cur_pixel_offset
= 0;
363 for (int i
= 0; i
< runs
.num_runs(); i
++) {
364 int32_t run_begin
= 0;
366 WebTextRun run
= runs
.GetRunAt(i
, &run_begin
, &run_len
);
367 int run_width
= font_
->calculateWidth(run
);
368 if (pixel_position
< cur_pixel_offset
+ run_width
) {
369 // Offset is in this run.
370 return static_cast<uint32_t>(font_
->offsetForPosition(
371 run
, static_cast<float>(pixel_position
- cur_pixel_offset
))) +
374 cur_pixel_offset
+= run_width
;
376 return runs
.text().size();
379 int32_t BrowserFontResource_Trusted::PixelOffsetForCharacter(
380 const PP_BrowserFont_Trusted_TextRun
* text
,
381 uint32_t char_offset
) {
382 TextRunCollection
runs(*text
);
383 int32_t cur_pixel_offset
= 0;
384 for (int i
= 0; i
< runs
.num_runs(); i
++) {
385 int32_t run_begin
= 0;
387 WebTextRun run
= runs
.GetRunAt(i
, &run_begin
, &run_len
);
388 if (char_offset
>= static_cast<uint32_t>(run_begin
) &&
389 char_offset
< static_cast<uint32_t>(run_begin
+ run_len
)) {
390 // Character we're looking for is in this run.
392 // Here we ask WebKit to give us the rectangle around the character in
393 // question, and then return the left edge. If we asked for a range of
394 // 0 characters starting at the character in question, it would give us
395 // a 0-width rect around the insertion point. But that will be on the
396 // right side of the character for an RTL run, which would be wrong.
397 WebFloatRect rect
= font_
->selectionRectForText(
398 run
, WebFloatPoint(0.0f
, 0.0f
), font_
->height(),
399 char_offset
- run_begin
, char_offset
- run_begin
+ 1);
400 return cur_pixel_offset
+ static_cast<int>(rect
.x
);
402 // Character is past this run, account for the pixels and continue
404 cur_pixel_offset
+= font_
->calculateWidth(run
);
407 return -1; // Requested a char beyond the end.
410 void BrowserFontResource_Trusted::DrawTextToCanvas(
411 SkCanvas
* destination
,
412 const PP_BrowserFont_Trusted_TextRun
& text
,
413 const PP_Point
* position
,
415 const PP_Rect
* clip
) {
416 // Convert position and clip.
417 WebFloatPoint
web_position(static_cast<float>(position
->x
),
418 static_cast<float>(position
->y
));
421 // Use entire canvas. SkCanvas doesn't have a size on it, so we just use
422 // the current clip bounds.
424 destination
->getClipBounds(&skclip
);
425 web_clip
= WebRect(skclip
.fLeft
, skclip
.fTop
, skclip
.fRight
- skclip
.fLeft
,
426 skclip
.fBottom
- skclip
.fTop
);
428 web_clip
= WebRect(clip
->point
.x
, clip
->point
.y
,
429 clip
->size
.width
, clip
->size
.height
);
432 TextRunCollection
runs(text
);
433 for (int i
= 0; i
< runs
.num_runs(); i
++) {
434 int32_t run_begin
= 0;
436 WebTextRun run
= runs
.GetRunAt(i
, &run_begin
, &run_len
);
437 font_
->drawText(destination
, run
, web_position
, color
, web_clip
);
439 // Advance to the next run. Note that we avoid doing this for the last run
440 // since it's unnecessary, measuring text is slow, and most of the time
441 // there will be only one run anyway.
442 if (i
!= runs
.num_runs() - 1)
443 web_position
.x
+= font_
->calculateWidth(run
);
447 } // namespace content