<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Olufemi Oyedepo</title>
    <description>The latest articles on DEV Community by Olufemi Oyedepo (@olufemi_oyedepo).</description>
    <link>https://dev.to/olufemi_oyedepo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F59652%2F0e3489db-aefd-4fbd-9118-075d030b7786.jpg</url>
      <title>DEV Community: Olufemi Oyedepo</title>
      <link>https://dev.to/olufemi_oyedepo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/olufemi_oyedepo"/>
    <language>en</language>
    <item>
      <title>PDF Generation using QuestPDF in ASP.NET Core — Part 1</title>
      <dc:creator>Olufemi Oyedepo</dc:creator>
      <pubDate>Sat, 04 May 2024 13:18:49 +0000</pubDate>
      <link>https://dev.to/olufemi_oyedepo/pdf-generation-using-questpdf-in-aspnet-core-part-1-2a34</link>
      <guid>https://dev.to/olufemi_oyedepo/pdf-generation-using-questpdf-in-aspnet-core-part-1-2a34</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
Report generation in an enterprise setting provides insights into key performance indicators, tracks progress toward goals, and helps stakeholders make informed decisions. The most common report formats available are in PDF, MS-Word, PPTX, Excel, etc.&lt;/p&gt;

&lt;p&gt;Of course, there are a bunch of PDF generation tools/libraries for the .NET platform out there, each with its pros &amp;amp; cons (the most notable for me has always been the licensing cost, as they are usually on the high side).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Note on Alternatives&lt;/strong&gt;&lt;br&gt;
Since licensing costs can be a concern (as I mentioned), it's worth noting that &lt;a href="https://ironpdf.com/competitors/questpdf-vs-ironpdf/" rel="noopener noreferrer"&gt;IronPDF offers a different approach&lt;/a&gt; that might suit some projects better. While QuestPDF uses a fluent API requiring you to build documents programmatically, IronPDF lets you design in HTML/CSS and &lt;a href="https://ironpdf.com/tutorials/html-to-pdf/" rel="noopener noreferrer"&gt;converts directly to PDF&lt;/a&gt;. This means you can leverage existing web design skills without learning a new API.&lt;br&gt;
For example, that same investment advice document could be created with IronPDF using familiar HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var renderer = new ChromePdfRenderer();
var html = $@"
    &amp;lt;h3&amp;gt;INVESTMENT ADVICE FOR {investmentType} - REF: {refNumber}&amp;lt;/h3&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Principal:&amp;lt;/strong&amp;gt; ${principal:N2}&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Expected Interest:&amp;lt;/strong&amp;gt; ${interest:N2}&amp;lt;/p&amp;gt;
    &amp;lt;!-- Your existing HTML template --&amp;gt;
";
var pdf = renderer.RenderHtmlAsPdf(html);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IronPDF's Chrome-based rendering ensures perfect CSS support, handles JavaScript, and creates text-selectable PDFs. It's already compatible with .NET 10 (coming November 2025) alongside current versions.&lt;/p&gt;

&lt;p&gt;While it's a commercial product, the pricing is transparent and includes features like digital signatures, form filling, and PDF merging out of the box. That said, QuestPDF is an excellent open-source choice if you prefer the fluent API approach and community licensing works for your project. Let's continue with the QuestPDF implementation...&lt;/p&gt;

&lt;p&gt;By the end of this article, you should be able to generate a dummy investment advice PDF document shown in the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0p6yeafbm9ah6n854fsc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0p6yeafbm9ah6n854fsc.webp" alt="QuestPDF C# .Net Core Sample report" width="669" height="888"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is QuestPDF?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/QuestPDF/QuestPDF" rel="noopener noreferrer"&gt;QuestPDF&lt;/a&gt; is an open-source .NET library for PDF document generation. It uses a fluent API approach to compose together many simple elements to create complex documents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of QuestPDF:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fluent API&lt;/li&gt;
&lt;li&gt;Very easy to use: Decent knowledge of C# is all that’s required.&lt;/li&gt;
&lt;li&gt;Intuive documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons of QuestPDF:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting used to the fluent API specifications &amp;amp; helper methods.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Let’s get started&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1.&lt;/strong&gt; Create an asp.net core web API project and install the QuestPDF NuGet package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package QuestPDF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Create a folder in the API project and name it “Assets”, create a JSON file inside the Assets folder, and call it “customer-investment-data.json”. Paste the following snippet into the JSON file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Customer": {
    "FullName": "John Doe",
    "Address": "23 Aaron Hawkins Avenue, Charleston, Texas"
  },
  "DealInfo": {
    "Type": "Fixed Deposit",
    "Issuer": "Morgan Stanley",
    "Currency": "USD",
    "ReferenceNumber": "REF-1234567890",
    "TransactionDate": "Thursday, 2 April 2024",
    "StartDate": "Thursday, 2 May 2024",
    "EndDate": "Monday, 27 January 2025",
    "Duration": "270",
    "NetRate": "17.88",
    "Principal": 450000,
    "ExpectedInterest": 70000,
    "NetMaturityValue": 850000
  },
  "CompanyLogoUrl": "idBGtJQnXa/idINtDZZob.jpeg"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Create model classes to deserialize the content of the JSON file: Create a folder and call it “Models” inside the API project, and create 4 model classes with the properties as stated below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Customer
{
    public string FullName { get; set; }
    public string Address { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class DealInfo
{
    public string Type { get; set; }
    public string Issuer { get; set; }
    public string Currency { get; set; }
    public string ReferenceNumber { get; set; }
    public string TransactionDate { get; set; }
    public string StartDate { get; set; }
    public string EndDate { get; set; }
    public string Duration { get; set; }
    public string NetRate { get; set; }
    public int Principal { get; set; }
    public int ExpectedInterest { get; set; }
    public int NetMaturityValue { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class PdfReportFileInfo
{
    public byte[] ByteArray { get; set; }
    public string MimeType { get; set; }
    public string FileName { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class InvestmentTradeInfo
{
    public Customer Customer { get; set; }
    public DealInfo DealInfo { get; set; }
    public string CompanyLogoUrl { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Create a service class (Services/QuestPdfService.cs) to handle the PDF generation business logic with the content below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.Mvc;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using QuestPDF_Demo.Models;
using System.Net;
using System.Text.Json;

public static class QuestPdfService
{
    public static async Task&amp;lt;PdfReportFileInfo&amp;gt; GenerateSample1Pdf()
    {
        // this is just a proof of concept, please feel free to refactor / make the code asynchronous if db/http calls are involved

        try
        {
            // read investment advice data from json file &amp;amp; deserialize into a C# object
            string customerInvestmentData = File.ReadAllText(@"./Assets/customer-investment-data.json");
            var investmentTradeInfo = JsonSerializer.Deserialize&amp;lt;InvestmentTradeInfo&amp;gt;(customerInvestmentData);
            var imageStream = await GetCompanyLogoUrlImage2(investmentTradeInfo.CompanyLogoUrl);

            Document document = Document.Create(container =&amp;gt;
            {
                container.Page(page =&amp;gt;
                {
                    page.Size(PageSizes.A4);
                    page.Margin(2, Unit.Centimetre);
                    page.PageColor(Colors.White);
                    page.DefaultTextStyle(x =&amp;gt; x.FontSize(11).FontFamily("Arial", "Calibri", "Tahoma"));
                    page.Header().Width(1, Unit.Inch).Image(imageStream);

                    page.Content().Column(x =&amp;gt;
                    {
                        x.Item().PaddingVertical((float)0.5, Unit.Centimetre).Text(investmentTradeInfo.DealInfo.TransactionDate);
                        x.Item().Text(investmentTradeInfo.Customer.FullName).FontSize(15).Bold();
                        x.Item().PaddingBottom(1, Unit.Centimetre).Text(investmentTradeInfo.Customer.Address).FontSize(13);

                        x.Item().PaddingBottom((float)0.3, Unit.Centimetre).Text("Dear Sir/Ma,");

                        x.Item().AlignCenter()
                            .Text($"INVESTMENT ADVICE FOR {investmentTradeInfo.DealInfo.Type} - {investmentTradeInfo.DealInfo.Issuer} - REF NO: {investmentTradeInfo.DealInfo.ReferenceNumber}".ToUpper())
                            .FontSize(13)
                            .SemiBold()
                            .Underline();

                        x.Item().PaddingTop((float)0.5, Unit.Centimetre).Text("Please refer to the details of the investment below: ");

                        x.Item().PaddingTop((float)0.5, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Issuer: ").SemiBold();
                            row.RelativeItem().AlignRight().Text($"{investmentTradeInfo.DealInfo.Issuer}".ToUpper());
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Investment Type: ").SemiBold();
                            row.RelativeItem().AlignRight().Text($"{investmentTradeInfo.DealInfo.Type}".ToUpper());
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Currency: ").SemiBold();
                            row.RelativeItem().AlignRight().Text($"{investmentTradeInfo.DealInfo.Currency}".ToUpper());
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Start Date: ").SemiBold();
                            row.RelativeItem().AlignRight().Text(investmentTradeInfo.DealInfo.StartDate);
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("End Date: ").SemiBold();
                            row.RelativeItem().AlignRight().Text(investmentTradeInfo.DealInfo.EndDate);
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Duration (days): ").SemiBold();
                            row.RelativeItem().AlignRight().Text(investmentTradeInfo.DealInfo.Duration);
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Net Rate (% per annum): ").SemiBold();
                            row.RelativeItem().AlignRight().Text(investmentTradeInfo.DealInfo.NetRate);
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Principal: ").SemiBold();
                            row.RelativeItem().AlignRight().Text($"${investmentTradeInfo.DealInfo.Principal:N2}");
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Expected Interest: ").SemiBold();
                            row.RelativeItem().AlignRight().Text($"${investmentTradeInfo.DealInfo.ExpectedInterest:N2}");
                        });

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.RelativeItem().Text("Net Maturity Value: ").SemiBold();
                            row.RelativeItem().AlignRight().Text($"${investmentTradeInfo.DealInfo.NetMaturityValue:N2}");
                        });


                        x.Item().PaddingTop((float)0.8, Unit.Centimetre).Text("Terms &amp;amp; conditions: ").SemiBold();

                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.Spacing(5);
                            row.AutoItem().PaddingLeft(10).Text("•");
                            row.RelativeItem().Text("This investment is offered under the Jane Doe Investment Securities Management Service.");
                        });

                        x.Item().PaddingTop((float)0.5, Unit.Centimetre).Text("We thank you for your valued patronage and continued interest in Jane Doe Investment Securities Limited.");


                        x.Item().PaddingTop(1, Unit.Centimetre).Text("Warm regards,");
                        x.Item().PaddingTop((float)0.3, Unit.Centimetre).Row(row =&amp;gt;
                        {
                            row.Spacing(5);
                            row.AutoItem().Text("For: ").NormalWeight();
                            row.RelativeItem().Text("Jane Doe Investment Securities Limited");
                        });

                    });



                    page.Footer()
                        .AlignCenter()
                        .Text(x =&amp;gt;
                        {
                            x.Span("THIS IS A SYSTEM GENERATED MAIL, PLEASE DO NOT REPLY TO THIS EMAIL.").FontSize(9);
                        });
                });
            });
            byte[] pdfBytes = document.GeneratePdf();

            return new PdfReportFileInfo() { 
                ByteArray = pdfBytes, 
                FileName = $"Investment_Advice_{investmentTradeInfo.Customer.FullName}_{investmentTradeInfo.DealInfo.ReferenceNumber}.pdf",
                MimeType = "application/pdf"
            };
        }
        catch (Exception ex)
        {
            throw ex;
        }

    }

    private static async Task&amp;lt;Image&amp;gt; GetCompanyLogoUrlImage2(string imagePath)
    {
        using var client = new HttpClient();
        client.BaseAddress = new Uri("https://asset.brandfetch.io/");
        client.DefaultRequestHeaders.Accept.Clear();
        var imageStream = await client.GetStreamAsync(imagePath);
        return Image.FromStream(imageStream);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; Set the license type: Insert the snippet below into the &lt;strong&gt;Progam.cs&lt;/strong&gt; file. You may want to read more about licensing &lt;a href="https://www.questpdf.com/license/configuration.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;QuestPDF.Settings.License = LicenseType.Community;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; Create a controller, name it QuestPdfController, and add the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Route("api/[controller]")]
[ApiController]
public class QuestPdfController : ControllerBase
{
    [HttpGet]
    [Route("sample1")]
    public async Task&amp;lt;ActionResult&amp;gt; GenerateSample1Pdf()
    {
        var pdfReportInfo = await QuestPdfService.GenerateSample1Pdf();
        return File(pdfReportInfo.ByteArray, pdfReportInfo.MimeType, pdfReportInfo.FileName);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that all is set, your folder structure should look like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta5x4x2p0dsj2epu85p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta5x4x2p0dsj2epu85p4.png" alt="QuestPdf sample report 1" width="308" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7.&lt;/strong&gt; Run the web API project and you should see the image below, call the /sample1 endpoint by clicking on the “Execute” button, that action should return a file that can be downloaded. Click on “Download file” as indicated below and you should have the dummy investment advice report in PDF.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyo2q5xf58rspt38usvh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyo2q5xf58rspt38usvh.png" alt="QuestPDF-Sample-report-api-endpoint" width="720" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it for the first part of this series, kindly drop me a follow so as not to miss subsequent posts.&lt;br&gt;
The source code for this example can be found &lt;a href="https://github.com/olufemioyedepo/QuestPDF-Demo" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  .netcore #pdf #report #questpdf #ironpdf #csharp
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Datetime Filter in Vue 3 using Moment.js</title>
      <dc:creator>Olufemi Oyedepo</dc:creator>
      <pubDate>Thu, 13 May 2021 19:03:15 +0000</pubDate>
      <link>https://dev.to/olufemi_oyedepo/datetime-filter-in-vue-3-using-moment-js-3678</link>
      <guid>https://dev.to/olufemi_oyedepo/datetime-filter-in-vue-3-using-moment-js-3678</guid>
      <description>&lt;h1&gt;
  
  
  Datetime Filter in Vue 3 using Moment.js
&lt;/h1&gt;

&lt;p&gt;It turns out that Filters have been removed 😏 in Vue 3 &lt;a href="https://v3.vuejs.org/guide/migration/filters.html"&gt;Official docs link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So that practically makes it a bit hard to format date/datetime in Vue 3. According to the official docs, the use of global filters is now encouraged but in my opinion, I'm not too sure the use of global filters would solve the problem at hand.&lt;/p&gt;

&lt;p&gt;So, I looked around but couldn't find much examples but I was able to eventually come up with something with the help of this famous library [Moment.js] 🕗 (&lt;a href="https://momentjs.com/"&gt;https://momentjs.com/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Let's dive straight into it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install moment js from npm &lt;code&gt;npm install moment --save&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In your component
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import moment from 'moment'
export default {
...
  created: function () {
    this.moment = moment;
  },
  setup() {
   let todaysDate = new Date();
  }
...
}

&amp;lt;div&amp;gt;
   {{ moment(todaysDate).format("ddd MMM DD, YYYY [at] HH:mm a") }}
   &amp;lt;!-- As of the time of writing, this gives ==&amp;gt; Thu May 13, 2021 at 19:42 pm --&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that's it 😉. Please feel free to change the format to suit your use case.&lt;br&gt;
Suggestions/comments are welcome. Thanks 🙏 🙏 🙏 &lt;/p&gt;

</description>
      <category>vue3</category>
      <category>vue</category>
      <category>momentjs</category>
      <category>datefilter</category>
    </item>
  </channel>
</rss>
