chromium/content/common/mac/attributed_string_type_converters.mm

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/mac/attributed_string_type_converters.h"

#include <AppKit/AppKit.h>

#include "base/apple/bridging.h"
#include "base/apple/foundation_util.h"
#include "base/check.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/common/common_param_traits.h"

namespace mojo {

namespace {

NSDictionary* ToAttributesDictionary(std::u16string name, float font_size) {
  DCHECK(!name.empty());
  NSFont* font = [NSFont fontWithName:base::SysUTF16ToNSString(name)
                                 size:font_size];
  if (!font) {
    // This can happen if:
    // - The specified font is unavailable in this process.
    // - The specified font is a system font unavailable through `+[NSFont
    //   fontWithName:size]`.
    // In either case, the system font is a reasonable substitute.
    font = [NSFont systemFontOfSize:font_size];
  }
  return @{NSFontAttributeName : font};
}

}  // namespace

CFAttributedStringRef
TypeConverter<CFAttributedStringRef, ui::mojom::AttributedStringPtr>::Convert(
    const ui::mojom::AttributedStringPtr& mojo_attributed_string) {
  // Create the return value.
  NSString* plain_text =
      base::SysUTF16ToNSString(mojo_attributed_string->string);
  NSMutableAttributedString* decoded_string =
      [[NSMutableAttributedString alloc] initWithString:plain_text];
  // Iterate over all the encoded attributes, attaching each to the string.
  const std::vector<ui::mojom::FontAttributePtr>& attributes =
      mojo_attributed_string->attributes;
  for (const auto& attribute : attributes) {
    // Protect against ranges that are outside the range of the string.
    const gfx::Range& range = attribute.get()->effective_range;
    if (range.GetMin() > plain_text.length ||
        range.GetMax() > plain_text.length) {
      continue;
    }
    [decoded_string
        addAttributes:ToAttributesDictionary(attribute.get()->font_name,
                                             attribute.get()->font_point_size)
                range:range.ToNSRange()];
  }

  return static_cast<CFAttributedStringRef>(
      CFAutorelease(base::apple::NSToCFOwnershipCast(decoded_string)));
}

ui::mojom::AttributedStringPtr
TypeConverter<ui::mojom::AttributedStringPtr, CFAttributedStringRef>::Convert(
    const CFAttributedStringRef cf_attributed_string) {
  NSAttributedString* ns_attributed_string =
      base::apple::CFToNSPtrCast(cf_attributed_string);

  // Create the return value.
  ui::mojom::AttributedStringPtr attributed_string =
      ui::mojom::AttributedString::New();
  attributed_string->string =
      base::SysNSStringToUTF16(ns_attributed_string.string);

  // Iterate over all the attributes in the string.
  NSUInteger length = ns_attributed_string.length;
  for (NSUInteger i = 0; i < length;) {
    NSRange effective_range;
    NSDictionary* ns_attributes =
        [ns_attributed_string attributesAtIndex:i
                                 effectiveRange:&effective_range];

    NSFont* font = ns_attributes[NSFontAttributeName];
    std::u16string font_name;
    float font_point_size;
    // Only encode the attributes if the filtered set contains font information.
    if (font) {
      font_name = base::SysNSStringToUTF16(font.fontName);
      font_point_size = font.pointSize;
      if (!font_name.empty()) {
        // Convert the attributes.
        ui::mojom::FontAttributePtr attrs = ui::mojom::FontAttribute::New(
            font_name, font_point_size, gfx::Range(effective_range));
        attributed_string->attributes.push_back(std::move(attrs));
      }
    }
    // Advance the iterator to the position outside of the effective range.
    i = NSMaxRange(effective_range);
  }
  return attributed_string;
}

}  // namespace mojo