<commit_before>#r "Newtonsoft.Json"
#r "System.Drawing"

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

static double RoundScore(double score) => Math.Round(score * 100);

static void NormalizeScores(Scores scores)
{
    scores.Anger = RoundScore(scores.Anger);
    scores.Happiness = RoundScore(scores.Happiness);
    scores.Neutral = RoundScore(scores.Neutral);
    scores.Sadness = RoundScore(scores.Sadness);
    scores.Surprise = RoundScore(scores.Surprise);
}

static Image MergeCardImage(Image card, byte[] imageBytes, Tuple<string, string> personInfo, double score)
{
    using (MemoryStream faceImageStream = new MemoryStream(imageBytes)) 
    {
        const int topLeftFaceX   = 85;
        const int topLeftFaceY   = 187;
        const int faceRect       = 648;
        const int nameTextX      = 56;
        const int nameTextY      = 60;
        const int titleTextY     = 108;
        const int nameWidth      = 430;
        const int scoreX         = 654;
        const int scoreY         = 70;
        const int scoreWidth     = 117;
        const int nameFontSize   = 34;
        const int titleFontSize  = 26;

        using (Image faceImage = Image.FromStream(faceImageStream, true)) 
        {
            using (Graphics g = Graphics.FromImage(card)) 
            {
                g.DrawImage(faceImage, topLeftFaceX, topLeftFaceY, faceRect, faceRect);
                RenderText(g, nameFontSize, nameTextX, nameTextY, nameWidth, personInfo.Item1);
                RenderText(g, titleFontSize, nameTextX + 4, titleTextY, nameWidth, personInfo.Item2); // second line seems to need some left padding

                RenderScore(g, scoreX, scoreY, scoreWidth, score.ToString());
            }

            return card;
        }
    }
}

// save with higher quality than the default, to avoid jpeg artifacts on the text and numbers
static void SaveAsJpeg(Image image, Stream outputStream)
{
    var jpgEncoder = GetEncoder(ImageFormat.Jpeg);
    var qualityEncoder = System.Drawing.Imaging.Encoder.Quality;
    var encoderParams = new EncoderParameters(1);
    encoderParams.Param[0] = new EncoderParameter(qualityEncoder, 90L);

    image.Save(outputStream, jpgEncoder, encoderParams);
}

static ImageCodecInfo GetEncoder(ImageFormat format)
{
    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
    foreach (ImageCodecInfo codec in codecs)
    {
        if (codec.FormatID == format.Guid) {
          return codec;
        }
    }
    return null;
}

static void RenderText(Graphics graphics, int fontSize, int xPos, int yPos, int width, string text)
{
    var brush = new SolidBrush(Color.Black);
    var font = new Font("Microsoft Sans Serif", fontSize, FontStyle.Bold);
    SizeF size;

    do {
        font = new Font("Microsoft Sans Serif", fontSize--, FontStyle.Bold);
        size = graphics.MeasureString(text, font);
    }
    while (size.Width > width);

    graphics.DrawString(text, font, brush, xPos, yPos);
}

static void RenderScore(Graphics graphics, int xPos, int yPos, int width, string score)
{
  var brush = new SolidBrush(Color.Black);
  var fontSize = 38;
  var font = new Font("Microsoft Sans Serif", fontSize, FontStyle.Bold);
  SizeF size = graphics.MeasureString(score, font);

  graphics.DrawString(score, font, brush, width - size.Width + xPos, yPos);
}

static string GetFullImagePath(string filename)
{
    var appPath = Path.Combine(Environment.GetEnvironmentVariable("HOME"), "site", "wwwroot", "CardGenerator", "assets");
    return Path.Combine(appPath, filename);
}

static Tuple<string, string> GetNameAndTitle(string filename)
{
    string[] words = filename.Split('-');
    return Tuple.Create(words[0], words[1]);
}

public class Face
{
    public FaceRectangle FaceRectangle { get; set; }
    public Scores Scores { get; set; }
}<commit_msg>Add env variable for project root<commit_after>#r "Newtonsoft.Json"
#r "System.Drawing"

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

static double RoundScore(double score) => Math.Round(score * 100);

static void NormalizeScores(Scores scores)
{
    scores.Anger = RoundScore(scores.Anger);
    scores.Happiness = RoundScore(scores.Happiness);
    scores.Neutral = RoundScore(scores.Neutral);
    scores.Sadness = RoundScore(scores.Sadness);
    scores.Surprise = RoundScore(scores.Surprise);
}

static Image MergeCardImage(Image card, byte[] imageBytes, Tuple<string, string> personInfo, double score)
{
    using (MemoryStream faceImageStream = new MemoryStream(imageBytes)) 
    {
        const int topLeftFaceX   = 85;
        const int topLeftFaceY   = 187;
        const int faceRect       = 648;
        const int nameTextX      = 56;
        const int nameTextY      = 60;
        const int titleTextY     = 108;
        const int nameWidth      = 430;
        const int scoreX         = 654;
        const int scoreY         = 70;
        const int scoreWidth     = 117;
        const int nameFontSize   = 34;
        const int titleFontSize  = 26;

        using (Image faceImage = Image.FromStream(faceImageStream, true)) 
        {
            using (Graphics g = Graphics.FromImage(card)) 
            {
                g.DrawImage(faceImage, topLeftFaceX, topLeftFaceY, faceRect, faceRect);
                RenderText(g, nameFontSize, nameTextX, nameTextY, nameWidth, personInfo.Item1);
                RenderText(g, titleFontSize, nameTextX + 4, titleTextY, nameWidth, personInfo.Item2); // second line seems to need some left padding

                RenderScore(g, scoreX, scoreY, scoreWidth, score.ToString());
            }

            return card;
        }
    }
}

// save with higher quality than the default, to avoid jpeg artifacts on the text and numbers
static void SaveAsJpeg(Image image, Stream outputStream)
{
    var jpgEncoder = GetEncoder(ImageFormat.Jpeg);
    var qualityEncoder = System.Drawing.Imaging.Encoder.Quality;
    var encoderParams = new EncoderParameters(1);
    encoderParams.Param[0] = new EncoderParameter(qualityEncoder, 90L);

    image.Save(outputStream, jpgEncoder, encoderParams);
}

static ImageCodecInfo GetEncoder(ImageFormat format)
{
    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
    foreach (ImageCodecInfo codec in codecs)
    {
        if (codec.FormatID == format.Guid) {
          return codec;
        }
    }
    return null;
}

static void RenderText(Graphics graphics, int fontSize, int xPos, int yPos, int width, string text)
{
    var brush = new SolidBrush(Color.Black);
    var font = new Font("Microsoft Sans Serif", fontSize, FontStyle.Bold);
    SizeF size;

    do {
        font = new Font("Microsoft Sans Serif", fontSize--, FontStyle.Bold);
        size = graphics.MeasureString(text, font);
    }
    while (size.Width > width);

    graphics.DrawString(text, font, brush, xPos, yPos);
}

static void RenderScore(Graphics graphics, int xPos, int yPos, int width, string score)
{
  var brush = new SolidBrush(Color.Black);
  var fontSize = 38;
  var font = new Font("Microsoft Sans Serif", fontSize, FontStyle.Bold);
  SizeF size = graphics.MeasureString(score, font);

  graphics.DrawString(score, font, brush, width - size.Width + xPos, yPos);
}

static string GetFullImagePath(string filename)
{
    var appPath = Path.Combine(Environment.GetEnvironmentVariable("ROOT"), "CardGenerator", "assets");
    return Path.Combine(appPath, filename);
}

static Tuple<string, string> GetNameAndTitle(string filename)
{
    string[] words = filename.Split('-');
    return Tuple.Create(words[0], words[1]);
}

public class Face
{
    public FaceRectangle FaceRectangle { get; set; }
    public Scores Scores { get; set; }
}
{'fraction_non_alphanumeric': 0.09985734664764621, 'fraction_numerical': 0.011412268188302425, 'mean_word_length': 3.1196581196581197, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5870700', 'n_tokens_mistral': 2401, 'n_tokens_neox': 2253, 'n_words': 641}