Home | History | Annotate | Download | only in child
      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 #include "content/child/browser_font_resource_trusted.h"
      6 
      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"
     26 
     27 using ppapi::StringVar;
     28 using ppapi::thunk::EnterResourceNoLock;
     29 using ppapi::thunk::PPB_ImageData_API;
     30 using blink::WebFloatPoint;
     31 using blink::WebFloatRect;
     32 using blink::WebFont;
     33 using blink::WebFontDescription;
     34 using blink::WebRect;
     35 using blink::WebTextRun;
     36 using blink::WebCanvas;
     37 
     38 namespace content {
     39 
     40 namespace {
     41 
     42 // Same as WebPreferences::kCommonScript. I'd use that directly here, but get an
     43 // undefined reference linker error.
     44 const char kCommonScript[] = "Zyyy";
     45 
     46 base::string16 GetFontFromMap(
     47     const webkit_glue::ScriptFontFamilyMap& map,
     48     const std::string& script) {
     49   webkit_glue::ScriptFontFamilyMap::const_iterator it =
     50       map.find(script);
     51   if (it != map.end())
     52     return it->second;
     53   return base::string16();
     54 }
     55 
     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 {
     61  public:
     62   explicit TextRunCollection(const PP_BrowserFont_Trusted_TextRun& run)
     63       : bidi_(NULL),
     64         num_runs_(0) {
     65     StringVar* text_string = StringVar::FromPPVar(run.text);
     66     if (!text_string)
     67       return;  // Leave num_runs_ = 0 so we'll do nothing.
     68     text_ = base::UTF8ToUTF16(text_string->value());
     69 
     70     if (run.override_direction) {
     71       // Skip autodetection.
     72       num_runs_ = 1;
     73       override_run_ = WebTextRun(text_, PP_ToBool(run.rtl), true);
     74     } else {
     75       bidi_ = ubidi_open();
     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);
     80     }
     81   }
     82 
     83   ~TextRunCollection() {
     84     if (bidi_)
     85       ubidi_close(bidi_);
     86   }
     87 
     88   const base::string16& text() const { return text_; }
     89   int num_runs() const { return num_runs_; }
     90 
     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_);
     95     if (bidi_) {
     96       bool run_rtl = !!ubidi_getVisualRun(bidi_, index, run_start, run_len);
     97       return WebTextRun(base::string16(&text_[*run_start], *run_len),
     98                         run_rtl, true);
     99     }
    100 
    101     // Override run, return the single one.
    102     DCHECK(index == 0);
    103     *run_start = 0;
    104     *run_len = static_cast<int32_t>(text_.size());
    105     return override_run_;
    106   }
    107 
    108  private:
    109   // Will be null if we skipped autodetection.
    110   UBiDi* bidi_;
    111 
    112   // Text of all the runs.
    113   base::string16 text_;
    114 
    115   int num_runs_;
    116 
    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_;
    120 
    121   DISALLOW_COPY_AND_ASSIGN(TextRunCollection);
    122 };
    123 
    124 bool PPTextRunToWebTextRun(const PP_BrowserFont_Trusted_TextRun& text,
    125                            WebTextRun* run) {
    126   StringVar* text_string = StringVar::FromPPVar(text.text);
    127   if (!text_string)
    128     return false;
    129 
    130   *run = WebTextRun(base::UTF8ToUTF16(text_string->value()),
    131                     PP_ToBool(text.rtl),
    132                     PP_ToBool(text.override_direction));
    133   return true;
    134 }
    135 
    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)
    142 
    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),
    150                  FontWeight100);
    151   COMPILE_ASSERT(static_cast<int>(WebFontDescription::Weight900) ==
    152                  static_cast<int>(PP_BROWSERFONT_TRUSTED_WEIGHT_900),
    153                  FontWeight900);
    154   COMPILE_ASSERT(WebFontDescription::GenericFamilyStandard ==
    155                  PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_DEFAULT),
    156                  StandardFamily);
    157   COMPILE_ASSERT(WebFontDescription::GenericFamilySerif ==
    158                  PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_SERIF),
    159                  SerifFamily);
    160   COMPILE_ASSERT(WebFontDescription::GenericFamilySansSerif ==
    161                  PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_SANSSERIF),
    162                  SansSerifFamily);
    163   COMPILE_ASSERT(WebFontDescription::GenericFamilyMonospace ==
    164                  PP_FAMILY_TO_WEB_FAMILY(PP_FONTFAMILY_MONOSPACE),
    165                  MonospaceFamily);
    166 
    167   StringVar* face_name = StringVar::FromPPVar(font.face);  // Possibly null.
    168 
    169   WebFontDescription result;
    170   base::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,
    176                                          kCommonScript);
    177         break;
    178       case PP_BROWSERFONT_TRUSTED_FAMILY_SANSSERIF:
    179         resolved_family = GetFontFromMap(prefs.sans_serif_font_family_map,
    180                                          kCommonScript);
    181         break;
    182       case PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE:
    183         resolved_family = GetFontFromMap(prefs.fixed_font_family_map,
    184                                          kCommonScript);
    185         break;
    186       case PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT:
    187       default:
    188         resolved_family = GetFontFromMap(prefs.standard_font_family_map,
    189                                          kCommonScript);
    190         break;
    191     }
    192   } else {
    193     // Use the exact font.
    194     resolved_family = base::UTF8ToUTF16(face_name->value());
    195   }
    196   result.family = resolved_family;
    197 
    198   result.genericFamily = PP_FAMILY_TO_WEB_FAMILY(font.family);
    199 
    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,
    207                                           kCommonScript)))
    208       result.size = static_cast<float>(prefs.default_fixed_font_size);
    209     else
    210       result.size = static_cast<float>(prefs.default_font_size);
    211   } else {
    212     // Use the exact size.
    213     result.size = static_cast<float>(font.size);
    214   }
    215 
    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);
    221   return result;
    222 }
    223 
    224 }  // namespace
    225 
    226 // static
    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)
    233     return false;
    234 
    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)
    238     return false;
    239   if (static_cast<int>(desc.weight) < PP_BROWSERFONT_TRUSTED_WEIGHT_100 ||
    240       static_cast<int>(desc.weight) > PP_BROWSERFONT_TRUSTED_WEIGHT_900)
    241     return false;
    242 
    243   // Check for excessive sizes which may cause layout to get confused.
    244   if (desc.size > 200)
    245     return false;
    246 
    247   return true;
    248 }
    249 
    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))) {
    257 }
    258 
    259 BrowserFontResource_Trusted::~BrowserFontResource_Trusted() {
    260 }
    261 
    262 ppapi::thunk::PPB_BrowserFont_Trusted_API*
    263 BrowserFontResource_Trusted::AsPPB_BrowserFont_Trusted_API() {
    264   return this;
    265 }
    266 
    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)
    271     return PP_FALSE;
    272 
    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 =
    277       StringVar::StringToPPVar(base::UTF16ToUTF8(web_desc.family));
    278   description->family =
    279       static_cast<PP_BrowserFont_Trusted_Family>(web_desc.genericFamily);
    280   description->size = static_cast<uint32_t>(web_desc.size);
    281   description->weight = static_cast<PP_BrowserFont_Trusted_Weight>(
    282       web_desc.weight);
    283   description->italic = web_desc.italic ? PP_TRUE : PP_FALSE;
    284   description->small_caps = web_desc.smallCaps ? PP_TRUE : PP_FALSE;
    285   description->letter_spacing = static_cast<int32_t>(web_desc.letterSpacing);
    286   description->word_spacing = static_cast<int32_t>(web_desc.wordSpacing);
    287 
    288   metrics->height = font_->height();
    289   metrics->ascent = font_->ascent();
    290   metrics->descent = font_->descent();
    291   metrics->line_spacing = font_->lineSpacing();
    292   metrics->x_height = static_cast<int32_t>(font_->xHeight());
    293 
    294   // Convert the string.
    295   return PP_TRUE;
    296 }
    297 
    298 PP_Bool BrowserFontResource_Trusted::DrawTextAt(
    299     PP_Resource image_data,
    300     const PP_BrowserFont_Trusted_TextRun* text,
    301     const PP_Point* position,
    302     uint32_t color,
    303     const PP_Rect* clip,
    304     PP_Bool image_data_is_opaque) {
    305   PP_Bool result = PP_FALSE;
    306   // Get and map the image data we're painting to.
    307   EnterResourceNoLock<PPB_ImageData_API> enter(image_data, true);
    308   if (enter.failed())
    309     return result;
    310 
    311   PPB_ImageData_API* image = static_cast<PPB_ImageData_API*>(
    312       enter.object());
    313   SkCanvas* canvas = image->GetPlatformCanvas();
    314   bool needs_unmapping = false;
    315   if (!canvas) {
    316     needs_unmapping = true;
    317     image->Map();
    318     canvas = image->GetPlatformCanvas();
    319     if (!canvas)
    320       return result;  // Failure mapping.
    321   }
    322 
    323   DrawTextToCanvas(canvas, *text, position, color, clip, image_data_is_opaque);
    324 
    325   if (needs_unmapping)
    326     image->Unmap();
    327   return PP_TRUE;
    328 }
    329 
    330 int32_t BrowserFontResource_Trusted::MeasureText(
    331     const PP_BrowserFont_Trusted_TextRun* text) {
    332   WebTextRun run;
    333   if (!PPTextRunToWebTextRun(*text, &run))
    334     return -1;
    335   return font_->calculateWidth(run);
    336 }
    337 
    338 uint32_t BrowserFontResource_Trusted::CharacterOffsetForPixel(
    339     const PP_BrowserFont_Trusted_TextRun* text,
    340     int32_t pixel_position) {
    341   TextRunCollection runs(*text);
    342   int32_t cur_pixel_offset = 0;
    343   for (int i = 0; i < runs.num_runs(); i++) {
    344     int32_t run_begin = 0;
    345     int32_t run_len = 0;
    346     WebTextRun run = runs.GetRunAt(i, &run_begin, &run_len);
    347     int run_width = font_->calculateWidth(run);
    348     if (pixel_position < cur_pixel_offset + run_width) {
    349       // Offset is in this run.
    350       return static_cast<uint32_t>(font_->offsetForPosition(
    351               run, static_cast<float>(pixel_position - cur_pixel_offset))) +
    352           run_begin;
    353     }
    354     cur_pixel_offset += run_width;
    355   }
    356   return runs.text().size();
    357 }
    358 
    359 int32_t BrowserFontResource_Trusted::PixelOffsetForCharacter(
    360     const PP_BrowserFont_Trusted_TextRun* text,
    361     uint32_t char_offset) {
    362   TextRunCollection runs(*text);
    363   int32_t cur_pixel_offset = 0;
    364   for (int i = 0; i < runs.num_runs(); i++) {
    365     int32_t run_begin = 0;
    366     int32_t run_len = 0;
    367     WebTextRun run = runs.GetRunAt(i, &run_begin, &run_len);
    368     if (char_offset >= static_cast<uint32_t>(run_begin) &&
    369         char_offset < static_cast<uint32_t>(run_begin + run_len)) {
    370       // Character we're looking for is in this run.
    371       //
    372       // Here we ask WebKit to give us the rectangle around the character in
    373       // question, and then return the left edge. If we asked for a range of
    374       // 0 characters starting at the character in question, it would give us
    375       // a 0-width rect around the insertion point. But that will be on the
    376       // right side of the character for an RTL run, which would be wrong.
    377       WebFloatRect rect = font_->selectionRectForText(
    378           run, WebFloatPoint(0.0f, 0.0f), font_->height(),
    379           char_offset - run_begin, char_offset - run_begin + 1);
    380       return cur_pixel_offset + static_cast<int>(rect.x);
    381     } else {
    382       // Character is past this run, account for the pixels and continue
    383       // looking.
    384       cur_pixel_offset += font_->calculateWidth(run);
    385     }
    386   }
    387   return -1;  // Requested a char beyond the end.
    388 }
    389 
    390 void BrowserFontResource_Trusted::DrawTextToCanvas(
    391     SkCanvas* destination,
    392     const PP_BrowserFont_Trusted_TextRun& text,
    393     const PP_Point* position,
    394     uint32_t color,
    395     const PP_Rect* clip,
    396     PP_Bool image_data_is_opaque) {
    397   // Convert position and clip.
    398   WebFloatPoint web_position(static_cast<float>(position->x),
    399                              static_cast<float>(position->y));
    400   WebRect web_clip;
    401   if (!clip) {
    402     // Use entire canvas. SkCanvas doesn't have a size on it, so we just use
    403     // the current clip bounds.
    404     SkRect skclip;
    405     destination->getClipBounds(&skclip);
    406     web_clip = WebRect(skclip.fLeft, skclip.fTop, skclip.fRight - skclip.fLeft,
    407                        skclip.fBottom - skclip.fTop);
    408   } else {
    409     web_clip = WebRect(clip->point.x, clip->point.y,
    410                        clip->size.width, clip->size.height);
    411   }
    412 
    413   TextRunCollection runs(text);
    414   for (int i = 0; i < runs.num_runs(); i++) {
    415     int32_t run_begin = 0;
    416     int32_t run_len = 0;
    417     WebTextRun run = runs.GetRunAt(i, &run_begin, &run_len);
    418     font_->drawText(destination, run, web_position, color, web_clip,
    419                     PP_ToBool(image_data_is_opaque));
    420 
    421     // Advance to the next run. Note that we avoid doing this for the last run
    422     // since it's unnecessary, measuring text is slow, and most of the time
    423     // there will be only one run anyway.
    424     if (i != runs.num_runs() - 1)
    425       web_position.x += font_->calculateWidth(run);
    426   }
    427 }
    428 
    429 }  // namespace content
    430