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

#include "pdf/pdfium/pdfium_font_helpers.h"

#include <algorithm>
#include <optional>
#include <string_view>

#include "base/i18n/encoding_detection.h"
#include "base/i18n/icu_string_conversions.h"
#include "base/strings/string_util.h"
#include "third_party/blink/public/platform/web_font_description.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/pdfium/public/fpdf_sysfontinfo.h"

namespace chrome_pdf {

namespace {

struct PdfFontSubstitution {
  std::string_view pdf_name;
  std::string_view face;
  bool bold;
  bool italic;
};

constexpr PdfFontSubstitution kPdfFontSubstitutions[] = {
    {"Courier", "Courier New", false, false},
    {"Courier-Bold", "Courier New", true, false},
    {"Courier-BoldOblique", "Courier New", true, true},
    {"Courier-Oblique", "Courier New", false, true},
    {"Helvetica", "Arial", false, false},
    {"Helvetica-Bold", "Arial", true, false},
    {"Helvetica-BoldOblique", "Arial", true, true},
    {"Helvetica-Oblique", "Arial", false, true},
    {"Times-Roman", "Times New Roman", false, false},
    {"Times-Bold", "Times New Roman", true, false},
    {"Times-BoldItalic", "Times New Roman", true, true},
    {"Times-Italic", "Times New Roman", false, true},

    // MS P?(Mincho|Gothic) are the most notable fonts in Japanese PDF files
    // without embedding the glyphs. Sometimes the font names are encoded
    // in Japanese Windows's locale (CP932/Shift_JIS) without space.
    // Most Linux systems don't have the exact font, but for outsourcing
    // fontconfig to find substitutable font in the system, we pass ASCII
    // font names to it.
    {"MS-PGothic", "MS PGothic", false, false},
    {"MS-Gothic", "MS Gothic", false, false},
    {"MS-PMincho", "MS PMincho", false, false},
    {"MS-Mincho", "MS Mincho", false, false},
    // MS PGothic in Shift_JIS encoding.
    {"\x82\x6C\x82\x72\x82\x6F\x83\x53\x83\x56\x83\x62\x83\x4E", "MS PGothic",
     false, false},
    // MS Gothic in Shift_JIS encoding.
    {"\x82\x6C\x82\x72\x83\x53\x83\x56\x83\x62\x83\x4E", "MS Gothic", false,
     false},
    // MS PMincho in Shift_JIS encoding.
    {"\x82\x6C\x82\x72\x82\x6F\x96\xBE\x92\xA9", "MS PMincho", false, false},
    // MS Mincho in Shift_JIS encoding.
    {"\x82\x6C\x82\x72\x96\xBE\x92\xA9", "MS Mincho", false, false},
};

blink::WebFontDescription::Weight WeightToBlinkWeight(int weight) {
  static_assert(blink::WebFontDescription::kWeight100 == 0, "Blink Weight min");
  static_assert(blink::WebFontDescription::kWeight900 == 8, "Blink Weight max");
  constexpr int kMinimumWeight = 100;
  constexpr int kMaximumWeight = 900;
  int normalized_weight = std::clamp(weight, kMinimumWeight, kMaximumWeight);
  normalized_weight = (normalized_weight / 100) - 1;
  return static_cast<blink::WebFontDescription::Weight>(normalized_weight);
}

}  // namespace

std::optional<blink::WebFontDescription> PdfFontToBlinkFontMapping(
    int weight,
    int italic,
    int pitch_family,
    std::string_view face) {
  // Pretend the system does not have the Symbol font to force a fallback to
  // the built in Symbol font in CFX_FontMapper::FindSubstFont().
  static constexpr std::string_view kSymbol("Symbol");
  if (face == kSymbol) {
    return std::nullopt;
  }

  blink::WebFontDescription desc;
  if (pitch_family & FXFONT_FF_FIXEDPITCH) {
    desc.generic_family = blink::WebFontDescription::kGenericFamilyMonospace;
  } else if (pitch_family & FXFONT_FF_ROMAN) {
    desc.generic_family = blink::WebFontDescription::kGenericFamilySerif;
  } else {
    desc.generic_family = blink::WebFontDescription::kGenericFamilyStandard;
  }

  // Map from the standard PDF fonts to TrueType font names.
  bool found_substitution = false;
  for (const auto& substitution : kPdfFontSubstitutions) {
    if (face == substitution.pdf_name) {
      desc.family = blink::WebString::FromUtf8(substitution.face);
      if (substitution.bold) {
        desc.weight = blink::WebFontDescription::kWeightBold;
      }
      if (substitution.italic) {
        desc.italic = true;
      }
      found_substitution = true;
      break;
    }
  }

  if (!found_substitution) {
    // Convert to UTF-8 and make sure it is valid.
    std::string face_utf8;
    if (base::IsStringUTF8(face)) {
      face_utf8 = face;
    } else {
      std::string encoding;
      if (base::DetectEncoding(face, &encoding)) {
        // ConvertToUtf8AndNormalize() clears `face_utf8` on failure.
        base::ConvertToUtf8AndNormalize(face, encoding, &face_utf8);
      }
    }

    if (face_utf8.empty()) {
      return std::nullopt;
    }

    desc.family = blink::WebString::FromUtf8(face_utf8);
    desc.weight = WeightToBlinkWeight(weight);
    desc.italic = italic > 0;
  }

  return desc;
}

}  // namespace chrome_pdf
