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 "ppapi/c/dev/ppb_font_dev.h"
10 #include "ppapi/proxy/connection.h"
11 #include "ppapi/shared_impl/ppapi_preferences.h"
12 #include "ppapi/shared_impl/var.h"
13 #include "ppapi/thunk/enter.h"
14 #include "ppapi/thunk/ppb_image_data_api.h"
15 #include "ppapi/thunk/thunk.h"
16 #include "skia/ext/platform_canvas.h"
17 #include "third_party/WebKit/public/platform/WebCanvas.h"
18 #include "third_party/WebKit/public/platform/WebFloatPoint.h"
19 #include "third_party/WebKit/public/platform/WebFloatRect.h"
20 #include "third_party/WebKit/public/platform/WebRect.h"
21 #include "third_party/WebKit/public/web/WebFont.h"
22 #include "third_party/WebKit/public/web/WebFontDescription.h"
23 #include "third_party/WebKit/public/web/WebTextRun.h"
24 #include "third_party/icu/source/common/unicode/ubidi.h"
25 #include "third_party/skia/include/core/SkRect.h"
27 using ppapi::StringVar
;
28 using ppapi::thunk::EnterResourceNoLock
;
29 using ppapi::thunk::PPB_ImageData_API
;
30 using WebKit::WebFloatPoint
;
31 using WebKit::WebFloatRect
;
32 using WebKit::WebFont
;
33 using WebKit::WebFontDescription
;
34 using WebKit::WebRect
;
35 using WebKit::WebTextRun
;
36 using WebKit::WebCanvas
;
42 // Same as WebPreferences::kCommonScript. I'd use that directly here, but get an
43 // undefined reference linker error.
44 const char kCommonScript
[] = "Zyyy";
46 string16
GetFontFromMap(
47 const webkit_glue::ScriptFontFamilyMap
& map
,
48 const std::string
& script
) {
49 webkit_glue::ScriptFontFamilyMap::const_iterator it
=
56 // Splits a PP_BrowserFont_Trusted_TextRun into a sequence or LTR and RTL
57 // WebTextRuns that can be used for WebKit. Normally WebKit does this for us,
58 // but the font drawing and measurement routines we call happen after this
59 // step. So for correct rendering of RTL content, we need to do it ourselves.
60 class TextRunCollection
{
62 explicit TextRunCollection(const PP_BrowserFont_Trusted_TextRun
& run
)
65 StringVar
* text_string
= StringVar::FromPPVar(run
.text
);
67 return; // Leave num_runs_ = 0 so we'll do nothing.
68 text_
= UTF8ToUTF16(text_string
->value());
70 if (run
.override_direction
) {
71 // Skip autodetection.
73 override_run_
= WebTextRun(text_
, PP_ToBool(run
.rtl
), true);
76 UErrorCode uerror
= U_ZERO_ERROR
;
77 ubidi_setPara(bidi_
, text_
.data(), text_
.size(), run
.rtl
, NULL
, &uerror
);
78 if (U_SUCCESS(uerror
))
79 num_runs_
= ubidi_countRuns(bidi_
, &uerror
);
83 ~TextRunCollection() {
88 const string16
& text() const { return text_
; }
89 int num_runs() const { return num_runs_
; }
91 // Returns a WebTextRun with the info for the run at the given index.
92 // The range covered by the run is in the two output params.
93 WebTextRun
GetRunAt(int index
, int32_t* run_start
, int32_t* run_len
) const {
94 DCHECK(index
< num_runs_
);
96 bool run_rtl
= !!ubidi_getVisualRun(bidi_
, index
, run_start
, run_len
);
97 return WebTextRun(string16(&text_
[*run_start
], *run_len
),
101 // Override run, return the single one.
104 *run_len
= static_cast<int32_t>(text_
.size());
105 return override_run_
;
109 // Will be null if we skipped autodetection.
112 // Text of all the runs.
117 // When the content specifies override_direction (bidi_ is null) then this
118 // will contain the single text run for WebKit.
119 WebTextRun override_run_
;
121 DISALLOW_COPY_AND_ASSIGN(TextRunCollection
);
124 bool PPTextRunToWebTextRun(const PP_BrowserFont_Trusted_TextRun
& text
,
126 StringVar
* text_string
= StringVar::FromPPVar(text
.text
);
130 *run
= WebTextRun(UTF8ToUTF16(text_string
->value()),
132 PP_ToBool(text
.override_direction
));
136 // The PP_* version lacks "None", so is just one value shifted from the
137 // WebFontDescription version. These values are checked in
138 // PPFontDescToWebFontDesc to make sure the conversion is correct. This is a
139 // macro so it can also be used in the COMPILE_ASSERTS.
140 #define PP_FAMILY_TO_WEB_FAMILY(f) \
141 static_cast<WebFontDescription::GenericFamily>(f + 1)
143 // Assumes the given PP_FontDescription has been validated.
144 WebFontDescription
PPFontDescToWebFontDesc(
145 const PP_BrowserFont_Trusted_Description
& font
,
146 const ppapi::Preferences
& prefs
) {
147 // Verify that the enums match so we can just static cast.
148 COMPILE_ASSERT(static_cast<int>(WebFontDescription::Weight100
) ==
149 static_cast<int>(PP_BROWSERFONT_TRUSTED_WEIGHT_100
),
151 COMPILE_ASSERT(static_cast<int>(WebFontDescription::Weight900
) ==
152 static_cast<int>(PP_BROWSERFONT_TRUSTED_WEIGHT_900
),
154 COMPILE_ASSERT(WebFontDescription::GenericFamilyStandard
==
155 PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_DEFAULT
),
157 COMPILE_ASSERT(WebFontDescription::GenericFamilySerif
==
158 PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_SERIF
),
160 COMPILE_ASSERT(WebFontDescription::GenericFamilySansSerif
==
161 PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_SANSSERIF
),
163 COMPILE_ASSERT(WebFontDescription::GenericFamilyMonospace
==
164 PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_MONOSPACE
),
167 StringVar
* face_name
= StringVar::FromPPVar(font
.face
); // Possibly null.
169 WebFontDescription result
;
170 string16 resolved_family
;
171 if (!face_name
|| face_name
->value().empty()) {
172 // Resolve the generic family.
173 switch (font
.family
) {
174 case PP_BROWSERFONT_TRUSTED_FAMILY_SERIF
:
175 resolved_family
= GetFontFromMap(prefs
.serif_font_family_map
,
178 case PP_BROWSERFONT_TRUSTED_FAMILY_SANSSERIF
:
179 resolved_family
= GetFontFromMap(prefs
.sans_serif_font_family_map
,
182 case PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE
:
183 resolved_family
= GetFontFromMap(prefs
.fixed_font_family_map
,
186 case PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT
:
188 resolved_family
= GetFontFromMap(prefs
.standard_font_family_map
,
193 // Use the exact font.
194 resolved_family
= UTF8ToUTF16(face_name
->value());
196 result
.family
= resolved_family
;
198 result
.genericFamily
= PP_FAMILY_TO_WEB_FAMILY(font
.family
);
200 if (font
.size
== 0) {
201 // Resolve the default font size, using the resolved family to see if
202 // we should use the fixed or regular font size. It's difficult at this
203 // level to detect if the requested font is fixed width, so we only apply
204 // the alternate font size to the default fixed font family.
205 if (StringToLowerASCII(resolved_family
) ==
206 StringToLowerASCII(GetFontFromMap(prefs
.fixed_font_family_map
,
208 result
.size
= static_cast<float>(prefs
.default_fixed_font_size
);
210 result
.size
= static_cast<float>(prefs
.default_font_size
);
212 // Use the exact size.
213 result
.size
= static_cast<float>(font
.size
);
216 result
.italic
= font
.italic
!= PP_FALSE
;
217 result
.smallCaps
= font
.small_caps
!= PP_FALSE
;
218 result
.weight
= static_cast<WebFontDescription::Weight
>(font
.weight
);
219 result
.letterSpacing
= static_cast<short>(font
.letter_spacing
);
220 result
.wordSpacing
= static_cast<short>(font
.word_spacing
);
227 bool BrowserFontResource_Trusted::IsPPFontDescriptionValid(
228 const PP_BrowserFont_Trusted_Description
& desc
) {
229 // Check validity of string. We can't check the actual text since we could
230 // be on the wrong thread and don't know if we're in the plugin or the host.
231 if (desc
.face
.type
!= PP_VARTYPE_STRING
&&
232 desc
.face
.type
!= PP_VARTYPE_UNDEFINED
)
235 // Check enum ranges.
236 if (static_cast<int>(desc
.family
) < PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT
||
237 static_cast<int>(desc
.family
) > PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE
)
239 if (static_cast<int>(desc
.weight
) < PP_BROWSERFONT_TRUSTED_WEIGHT_100
||
240 static_cast<int>(desc
.weight
) > PP_BROWSERFONT_TRUSTED_WEIGHT_900
)
243 // Check for excessive sizes which may cause layout to get confused.
250 BrowserFontResource_Trusted::BrowserFontResource_Trusted(
251 ppapi::proxy::Connection connection
,
252 PP_Instance instance
,
253 const PP_BrowserFont_Trusted_Description
& desc
,
254 const ppapi::Preferences
& prefs
)
255 : PluginResource(connection
, instance
),
256 font_(WebFont::create(PPFontDescToWebFontDesc(desc
, prefs
))) {
259 BrowserFontResource_Trusted::~BrowserFontResource_Trusted() {
262 ppapi::thunk::PPB_BrowserFont_Trusted_API
*
263 BrowserFontResource_Trusted::AsPPB_BrowserFont_Trusted_API() {
267 PP_Bool
BrowserFontResource_Trusted::Describe(
268 PP_BrowserFont_Trusted_Description
* description
,
269 PP_BrowserFont_Trusted_Metrics
* metrics
) {
270 if (description
->face
.type
!= PP_VARTYPE_UNDEFINED
)
273 // While converting the other way in PPFontDescToWebFontDesc we validated
274 // that the enums can be casted.
275 WebFontDescription web_desc
= font_
->fontDescription();
276 description
->face
= StringVar::StringToPPVar(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 DrawTextToCanvas(canvas
, *text
, position
, color
, clip
, image_data_is_opaque
);
329 int32_t BrowserFontResource_Trusted::MeasureText(
330 const PP_BrowserFont_Trusted_TextRun
* text
) {
332 if (!PPTextRunToWebTextRun(*text
, &run
))
334 return font_
->calculateWidth(run
);
337 uint32_t BrowserFontResource_Trusted::CharacterOffsetForPixel(
338 const PP_BrowserFont_Trusted_TextRun
* text
,
339 int32_t pixel_position
) {
340 TextRunCollection
runs(*text
);
341 int32_t cur_pixel_offset
= 0;
342 for (int i
= 0; i
< runs
.num_runs(); i
++) {
343 int32_t run_begin
= 0;
345 WebTextRun run
= runs
.GetRunAt(i
, &run_begin
, &run_len
);
346 int run_width
= font_
->calculateWidth(run
);
347 if (pixel_position
< cur_pixel_offset
+ run_width
) {
348 // Offset is in this run.
349 return static_cast<uint32_t>(font_
->offsetForPosition(
350 run
, static_cast<float>(pixel_position
- cur_pixel_offset
))) +
353 cur_pixel_offset
+= run_width
;
355 return runs
.text().size();
358 int32_t BrowserFontResource_Trusted::PixelOffsetForCharacter(
359 const PP_BrowserFont_Trusted_TextRun
* text
,
360 uint32_t char_offset
) {
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 if (char_offset
>= static_cast<uint32_t>(run_begin
) &&
368 char_offset
< static_cast<uint32_t>(run_begin
+ run_len
)) {
369 // Character we're looking for is in this run.
371 // Here we ask WebKit to give us the rectangle around the character in
372 // question, and then return the left edge. If we asked for a range of
373 // 0 characters starting at the character in question, it would give us
374 // a 0-width rect around the insertion point. But that will be on the
375 // right side of the character for an RTL run, which would be wrong.
376 WebFloatRect rect
= font_
->selectionRectForText(
377 run
, WebFloatPoint(0.0f
, 0.0f
), font_
->height(),
378 char_offset
- run_begin
, char_offset
- run_begin
+ 1);
379 return cur_pixel_offset
+ static_cast<int>(rect
.x
);
381 // Character is past this run, account for the pixels and continue
383 cur_pixel_offset
+= font_
->calculateWidth(run
);
386 return -1; // Requested a char beyond the end.
389 void BrowserFontResource_Trusted::DrawTextToCanvas(
390 SkCanvas
* destination
,
391 const PP_BrowserFont_Trusted_TextRun
& text
,
392 const PP_Point
* position
,
395 PP_Bool image_data_is_opaque
) {
396 // Convert position and clip.
397 WebFloatPoint
web_position(static_cast<float>(position
->x
),
398 static_cast<float>(position
->y
));
401 // Use entire canvas. SkCanvas doesn't have a size on it, so we just use
402 // the current clip bounds.
404 destination
->getClipBounds(&skclip
);
405 web_clip
= WebRect(skclip
.fLeft
, skclip
.fTop
, skclip
.fRight
- skclip
.fLeft
,
406 skclip
.fBottom
- skclip
.fTop
);
408 web_clip
= WebRect(clip
->point
.x
, clip
->point
.y
,
409 clip
->size
.width
, clip
->size
.height
);
412 TextRunCollection
runs(text
);
413 for (int i
= 0; i
< runs
.num_runs(); i
++) {
414 int32_t run_begin
= 0;
416 WebTextRun run
= runs
.GetRunAt(i
, &run_begin
, &run_len
);
417 font_
->drawText(destination
, run
, web_position
, color
, web_clip
,
418 PP_ToBool(image_data_is_opaque
));
420 // Advance to the next run. Note that we avoid doing this for the last run
421 // since it's unnecessary, measuring text is slow, and most of the time
422 // there will be only one run anyway.
423 if (i
!= runs
.num_runs() - 1)
424 web_position
.x
+= font_
->calculateWidth(run
);
428 } // namespace content