Commit c018ddc1 authored by lliabraa's avatar lliabraa Committed by Commit bot

Add NSString category for providing iOS6-style string drawing APIs.

The iOS6-style APIs have been deprecated, but the replacements are
more verbose and brittle so this category wraps the replacements in
methods similar to the iOS6-style APIs.

This category lives in a new target and directory: src/ui/ios, so
this CL sets up all the gyp/DEPS stuff and only adds a single
method. There will be more methods added in subsequent CLs.

BUG=364419

Review URL: https://codereview.chromium.org/497503004

Cr-Commit-Position: refs/heads/master@{#293193}
parent 0bd44664
......@@ -40,6 +40,7 @@
'dependencies': [
'../ios/ios.gyp:*',
'../ui/base/ui_base_tests.gyp:ui_unittests',
'../ui/ios/ui_ios_tests.gyp:ui_ios_unittests',
'../ui/gfx/gfx_tests.gyp:gfx_unittests',
],
}],
......
......@@ -3,4 +3,5 @@ include_rules = [
"+skia/ext",
"+third_party/harfbuzz-ng",
"+third_party/skia",
"+ui/ios",
]
......@@ -309,6 +309,9 @@
],
'conditions': [
['OS=="ios"', {
'dependencies': [
'<(DEPTH)/ui/ios/ui_ios.gyp:ui_ios',
],
# iOS only uses a subset of UI.
'sources/': [
['exclude', '^codec/jpeg_codec\\.cc$'],
......
......@@ -13,6 +13,7 @@
#include "base/strings/utf_string_conversions.h"
#include "ui/gfx/font.h"
#include "ui/gfx/font_render_params.h"
#include "ui/ios/NSString+CrStringDrawing.h"
namespace gfx {
......@@ -112,12 +113,7 @@ void PlatformFontIOS::CalculateMetrics() {
height_ = font.lineHeight;
ascent_ = font.ascender;
cap_height_ = font.capHeight;
if (font) {
NSDictionary* attributes = @{ NSFontAttributeName : font };
average_width_ = std::ceil([@"x" sizeWithAttributes:attributes].width);
} else {
average_width_ = 0;
}
average_width_ = [@"x" cr_sizeWithFont:font].width;
}
////////////////////////////////////////////////////////////////////////////////
......
......@@ -10,6 +10,7 @@
#include "base/strings/sys_string_conversions.h"
#include "ui/gfx/font_list.h"
#include "ui/ios/NSString+CrStringDrawing.h"
namespace gfx {
......@@ -20,10 +21,7 @@ int GetStringWidth(const base::string16& text, const FontList& font_list) {
float GetStringWidthF(const base::string16& text, const FontList& font_list) {
NSString* ns_text = base::SysUTF16ToNSString(text);
NativeFont native_font = font_list.GetPrimaryFont().GetNativeFont();
if (!native_font)
return 0;
NSDictionary* attributes = @{ NSFontAttributeName : native_font };
return std::ceil([ns_text sizeWithAttributes:attributes].width);
return [ns_text cr_sizeWithFont:native_font].width;
}
} // namespace gfx
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_IOS_NSSTRING_CR_STRING_DRAWING_H_
#define UI_IOS_NSSTRING_CR_STRING_DRAWING_H_
#import <UIKit/UIKit.h>
@interface NSString (CrStringDrawing)
// Returns the size of the string if it were to be rendered with the specified
// font on a single line. The width and height of the CGSize returned are
// pixel-aligned.
//
// This method is a convenience wrapper around sizeWithAttributes: to avoid
// boilerplate required to put |font| in a dictionary of attributes. Do not pass
// nil into this method.
- (CGSize)cr_pixelAlignedSizeWithFont:(UIFont*)font;
// Deprecated: Use cr_pixelAlignedSizeWithFont: or sizeWithAttributes:
// Provides a drop-in replacement for sizeWithFont:, which was deprecated in iOS
// 7 in favor of -sizeWithAttributes:. Specifically, this method will return
// CGSizeZero if |font| is nil, and the width and height returned are rounded up
// to integer values.
// TODO(lliabraa): This method was added to ease the transition off of the
// deprecated sizeWithFont: method. New call sites should not be added and
// existing call sites should be audited to determine the correct behavior for
// nil |font| and rounding, then replaced with cr_pixelAlignedSizeWithFont: or
// sizeWithAttributes: (crbug.com/364419).
- (CGSize)cr_sizeWithFont:(UIFont*)font;
@end
#endif // UI_IOS_NSSTRING_CR_STRING_DRAWING_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ui/ios/NSString+CrStringDrawing.h"
#include "base/logging.h"
namespace {
// Returns the closest pixel-aligned value higher than |value|, taking the scale
// factor into account. At a scale of 1, equivalent to ceil().
// TODO(lliabraa): Move this method to a common util file (crbug.com/409823).
CGFloat alignValueToUpperPixel(CGFloat value) {
CGFloat scale = [[UIScreen mainScreen] scale];
return ceil(value * scale) / scale;
}
} // namespace
@implementation NSString (CrStringDrawing)
- (CGSize)cr_pixelAlignedSizeWithFont:(UIFont*)font {
DCHECK(font) << "|font| can not be nil; it is used as a NSDictionary value";
NSDictionary* attributes = @{ NSFontAttributeName : font };
CGSize size = [self sizeWithAttributes:attributes];
return CGSizeMake(alignValueToUpperPixel(size.width),
alignValueToUpperPixel(size.height));
}
- (CGSize)cr_sizeWithFont:(UIFont*)font {
if (!font)
return CGSizeZero;
NSDictionary* attributes = @{ NSFontAttributeName : font };
CGSize size = [self sizeWithAttributes:attributes];
return CGSizeMake(ceil(size.width), ceil(size.height));
}
@end
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ui/ios/NSString+CrStringDrawing.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
namespace {
typedef PlatformTest NSStringCrStringDrawing;
// These test verifies that the category methods return the same values as the
// deprecated methods, so ignore warnings about using deprecated methods.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
TEST_F(NSStringCrStringDrawing, SizeWithFont) {
NSArray* fonts = @[
[NSNull null],
[UIFont systemFontOfSize:16],
[UIFont boldSystemFontOfSize:10],
[UIFont fontWithName:@"Helvetica" size:12.0],
];
for (UIFont* font in fonts) {
if ([font isEqual:[NSNull null]])
font = nil;
std::string font_tag = "with font ";
font_tag.append(
base::SysNSStringToUTF8(font ? [font description] : @"nil"));
EXPECT_EQ([@"" sizeWithFont:font].width,
[@"" cr_sizeWithFont:font].width) << font_tag;
EXPECT_EQ([@"" sizeWithFont:font].height,
[@"" cr_sizeWithFont:font].height) << font_tag;
EXPECT_EQ([@"Test" sizeWithFont:font].width,
[@"Test" cr_sizeWithFont:font].width) << font_tag;
EXPECT_EQ([@"Test" sizeWithFont:font].height,
[@"Test" cr_sizeWithFont:font].height) << font_tag;
EXPECT_EQ([@"你好" sizeWithFont:font].width,
[@"你好" cr_sizeWithFont:font].width) << font_tag;
EXPECT_EQ([@"你好" sizeWithFont:font].height,
[@"你好" cr_sizeWithFont:font].height) << font_tag;
NSString* long_string = @"★ This is a test string that is very long.";
EXPECT_EQ([long_string sizeWithFont:font].width,
[long_string cr_sizeWithFont:font].width) << font_tag;
EXPECT_EQ([long_string sizeWithFont:font].height,
[long_string cr_sizeWithFont:font].height) << font_tag;
}
}
#pragma clang diagnostic pop // ignored "-Wdeprecated-declarations"
TEST_F(NSStringCrStringDrawing, PixelAlignedSizeWithFont) {
NSArray* fonts = @[
[UIFont systemFontOfSize:16],
[UIFont boldSystemFontOfSize:10],
[UIFont fontWithName:@"Helvetica" size:12.0],
];
NSArray* strings = @[
@"",
@"Test",
@"你好",
@"★ This is a test string that is very long.",
];
for (UIFont* font in fonts) {
NSDictionary* attributes = @{ NSFontAttributeName : font };
for (NSString* string in strings) {
std::string test_tag = base::StringPrintf("for string '%s' with font %s",
base::SysNSStringToUTF8(string).c_str(),
base::SysNSStringToUTF8([font description]).c_str());
CGSize size_with_attributes = [string sizeWithAttributes:attributes];
CGSize size_with_pixel_aligned =
[string cr_pixelAlignedSizeWithFont:font];
// Verify that the pixel_aligned size is always rounded up (i.e. the size
// returned from sizeWithAttributes: is less than or equal to the pixel-
// aligned size).
EXPECT_LE(size_with_attributes.width,
size_with_pixel_aligned.width) << test_tag;
EXPECT_LE(size_with_attributes.height,
size_with_pixel_aligned.height) << test_tag;
// Verify that the pixel_aligned size is never more than a pixel different
// than the size returned from sizeWithAttributes:.
static CGFloat scale = [[UIScreen mainScreen] scale];
EXPECT_NEAR(size_with_attributes.width * scale,
size_with_pixel_aligned.width * scale,
0.9999) << test_tag;
EXPECT_NEAR(size_with_attributes.height * scale,
size_with_pixel_aligned.height * scale,
0.9999) << test_tag;
// Verify that the pixel-aligned value is pixel-aligned.
EXPECT_FLOAT_EQ(roundf(size_with_pixel_aligned.width * scale),
size_with_pixel_aligned.width * scale) << test_tag;
EXPECT_FLOAT_EQ(roundf(size_with_pixel_aligned.height * scale),
size_with_pixel_aligned.height * scale) << test_tag;
}
}
}
} // namespace
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'variables': {
'chromium_code': 1,
},
'targets': [
{
'target_name': 'ui_ios',
'type': 'static_library',
# Linkable dependents need to set the linker flag '-ObjC' in order to use
# the categories in this target (e.g. NSString+CrStringDrawing.h).
'link_settings': {
'xcode_settings': {'OTHER_LDFLAGS': ['-ObjC']},
},
'include_dirs': [
'../..',
],
'sources': [
'NSString+CrStringDrawing.h',
'NSString+CrStringDrawing.mm',
],
},
],
}
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'variables': {
'chromium_code': 1,
},
'targets': [
{
'target_name': 'ui_ios_unittests',
'type': '<(gtest_target_type)',
'dependencies': [
'../../base/base.gyp:base',
'../../base/base.gyp:run_all_unittests',
'../../base/base.gyp:test_support_base',
'../../testing/gtest.gyp:gtest',
'ui_ios.gyp:ui_ios',
],
'sources' : [
'NSString+CrStringDrawing_unittest.mm',
],
},
],
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment