Fix UTF-8 truncation
This fixes #1389. The new test in format-test.cc fails in the absence of changes to format.h.
{"label": "fmtlib:master", "ref": "master", "sha": "d6eede9e085f0b36edcf0a2f6dff5f7875181019"}
I've done some tests with UTF-8 strings, and think string formatting with precision doesn't work as expected.

fmt::format(u8"{:.4}", u8"cafés");

The format call above, even if done properly with char8_t based strings returns "caf\xc3" instead of "café", where "\xc3" is the first byte of the UTF-8 sequence "\xc3\xa9" for "é".  
UTF-8 truncation
diff --git a/include/fmt/format.h b/include/fmt/format.h
index 6ed7fc431be6..4c9e33fddd4a 100644
--- a/include/fmt/format.h
+++ b/include/fmt/format.h
@@ -436,6 +436,24 @@ inline size_t count_code_points(basic_string_view<char8_t> s) {
   return num_code_points;
 }
 
+template <typename Char>
+inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
+  size_t size = s.size();
+  return n < size ? n : size;
+}
+
+// Calculates the index of the nth code point in a UTF-8 string.
+inline size_t code_point_index(basic_string_view<char8_t> s, size_t n) {
+  const char8_t* data = s.data();
+  size_t num_code_points = 0;
+  for (size_t i = 0, size = s.size(); i != size; ++i) {
+    if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) {
+      return i;
+    }
+  }
+  return s.size();
+}
+
 inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
 
 template <typename InputIt, typename OutChar>
@@ -1729,7 +1747,8 @@ template <typename Range> class basic_writer {
     const Char* data = s.data();
     std::size_t size = s.size();
     if (specs.precision >= 0 && internal::to_unsigned(specs.precision) < size)
-      size = internal::to_unsigned(specs.precision);
+      size = internal::code_point_index(s,
+                                        internal::to_unsigned(specs.precision));
     write(data, size, specs);
   }
 

diff --git a/test/format-test.cc b/test/format-test.cc
index f5a3556944b4..2173936b206e 100644
--- a/test/format-test.cc
+++ b/test/format-test.cc
@@ -2621,3 +2621,13 @@ TEST(FormatTest, FormatCustomChar) {
   EXPECT_EQ(result.size(), 1);
   EXPECT_EQ(result[0], mychar('x'));
 }
+
+TEST(FormatTest, FormatUTF8Precision) {
+  using str_type = std::basic_string<char8_t>;
+  str_type format(reinterpret_cast<const char8_t*>(u8"{:.4}"));
+  str_type str(reinterpret_cast<const char8_t*>(u8"caf\u00e9s")); // cafés
+  auto result = fmt::format(format, str);
+  EXPECT_EQ(fmt::internal::count_code_points(result), 4);
+  EXPECT_EQ(result.size(), 5);
+  EXPECT_EQ(result, str.substr(0, 5));
+}

{"format-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}}
{"locale-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "compile-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "ostream-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "ranges-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "core-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "scan-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "color-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "grisu-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "chrono-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "gtest-extra-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "posix-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "assert-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "posix-mock-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "printf-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "format-impl-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "custom-formatter-test": {"run": "PASS", "test": "PASS", "fix": "PASS"}}
{"format-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}}
{"passed_count": 17, "failed_count": 0, "skipped_count": 0, "passed_tests": ["format-test", "gtest-extra-test", "locale-test", "compile-test", "posix-test", "ostream-test", "ranges-test", "assert-test", "posix-mock-test", "core-test", "printf-test", "color-test", "scan-test", "format-impl-test", "grisu-test", "custom-formatter-test", "chrono-test"], "failed_tests": [], "skipped_tests": []}
{"passed_count": 16, "failed_count": 0, "skipped_count": 0, "passed_tests": ["gtest-extra-test", "locale-test", "compile-test", "posix-test", "ostream-test", "ranges-test", "assert-test", "posix-mock-test", "core-test", "printf-test", "color-test", "scan-test", "format-impl-test", "grisu-test", "custom-formatter-test", "chrono-test"], "failed_tests": [], "skipped_tests": []}
{"passed_count": 17, "failed_count": 0, "skipped_count": 0, "passed_tests": ["format-test", "gtest-extra-test", "locale-test", "compile-test", "posix-test", "ostream-test", "ranges-test", "assert-test", "posix-mock-test", "core-test", "printf-test", "color-test", "scan-test", "format-impl-test", "grisu-test", "custom-formatter-test", "chrono-test"], "failed_tests": [], "skipped_tests": []}