مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
در حین کار با برنامه‌های وب، چشم‌پوشی از جاوا اسکریپت عملا ممکن نیست؛ هرچند با Blazor، امکان انجام کارهایی را یافته‌ایم که پیشتر با MVC و یا Razor pages میسر نبودند، اما هیچگاه به تنهایی نمی‌تواند جایگزین کامل جاوا اسکریپت، در تولید برنامه‌های وب باشد. بنابراین ضروری است که نحوه‌ی یکپارچگی جاوا اسکریپت را با برنامه‌های مبتنی بر Blazor، بررسی کنیم.


ایجاد کامپوننت جدید BlazorJS

برای بررسی نحوه‌ی تعامل جاوا اسکریپت و Blazor، در ابتدا کامپوننت جدید Pages\LearnBlazor\BlazorJS.razor را ایجاد کرده:
@page "/BlazorJS"

<h3>BlazorJS</h3>

@code
{
}
و همچنین مدخل منوی آن‌را نیز بر اساس مسیریابی ابتدای فایل این کامپوننت، به فایل Shared\NavMenu.razor اضافه می‌کنیم:
<li class="nav-item px-3">
    <NavLink class="nav-link" href="BlazorJS">
        <span class="oi oi-list-rich" aria-hidden="true"></span> BlazorJS
    </NavLink>
</li>


روش فراخوانی کدهای جاوا اسکریپتی از طریق کدهای سی‌شارپ Blazor

فرض کنید می‌خواهیم در حین کلیک بر روی دکمه‌ای مانند دکمه‌ی حذف، ابتدا تائیدیه‌ای را توسط تابع confirm جاوا اسکریپتی، از کاربر اخذ کنیم. روش انجام چنین کاری در برنامه‌های مبتنی بر Blazor به صورت زیر است:
@page "/BlazorJS"

@inject IJSRuntime JsRuntime

<h3>BlazorJS</h3>

<div class="row">
    <button class="btn btn-secondary" @onclick="TestConfirmBox">Test Confirm Button</button>
</div>
<div class="row">
    @if (ConfirmResult)
    {
        <p>Confirmation has been made!</p>
    }
    else
    {
        <p>Confirmation Pending!</p>
    }
</div>

@code {
    string ConfirmMessage = "Are you sure you want to click?";
    bool ConfirmResult;

    async Task TestConfirmBox()
    {
        ConfirmResult = await JsRuntime.InvokeAsync<bool>("confirm", ConfirmMessage);
    }
}
توضیحات:
- در اینجا می‌خواهیم تابع استاندارد confirm جاوا اسکریپتی را از طریق کدهای سی‌شارپ، با کلیک بر روی دکمه‌ی Test Confirm Button، فراخوانی کنیم. به همین جهت onclick@ این دکمه، به متد TestConfirmBox کدهای UI سی‌شارپ این کامپوننت، متصل شده‌است.
- برای دسترسی به توابع جاوا اسکریپتی، نیاز است سرویس توکار IJSRuntime را به کدهای کامپوننت تزریق کنیم که روش انجام آن‌را توسط دایرکتیو inject@ مشاهده می‌کنید. برای دسترسی به این سرویس توکار، نیاز به تنظیمات ابتدایی خاصی نیست و اینکار پیشتر انجام شده‌است.
- سرویس JsRuntime تزریق شده، دو متد مهم InvokeVoidAsync و InvokeAsync را جهت فراخوانی توابع جاوا اسکریپتی به همراه دارد. اگر تابعی، خروجی غیر void داشته باشد، باید از متد InvokeAsync استفاده کرد. برای مثال خروجی تابع استاندارد confirm، از نوع boolean است. بنابراین نوع این خروجی را به صورت یک آرگومان جنریک متد InvokeAsync مشخص کرده‌ایم.
- اولین پارامتر متد InvokeAsync، نام رشته‌ای تابع جاوا اسکریپتی است که قرار است صدا زده شود. پارامترهای اختیاری بعدی که به صورت params object?[]? args تعریف شده‌اند، لیست نامحدود آرگومان‌های ورودی این متد هستند.
- فیلد ConfirmMessage، پیامی را جهت اخذ تائید، تعریف می‌کند که به عنوان پارامتر متد confirm، توسط JsRuntime.InvokeAsync فراخوانی خواهد شد.
- فیلد ConfirmResult، نتیجه‌ی فراخوانی متد confirm جاوا اسکریپتی را به همراه دارد.
- در اینجا روش عکس العمل نشان دادن به خروجی دریافتی از متد جاوااسکریپتی را نیز مشاهده می‌کنید. پس از پایان متد TestConfirmBox که یک متد رویدادگران است، همانطور که در مطلب بررسی «چرخه‌ی حیات کامپوننت‌ها» نیز بررسی کردیم، متد StateHasChanged، در پشت صحنه فراخوانی می‌شود که سبب رندر مجدد UI خواهد شد. بنابراین در حین رندر مجدد UI، بر اساس مقدار جدید ConfirmResult دریافت شده‌ی از کاربر، با تشکیل یک if/else@، می‌توان به نتیجه‌ی تائید یا عدم تائید کاربر، واکنش نشان داد. با این توضیحات در اولین بار نمایش کامپوننت جاری چون مقدار ConfirmResult مساوی false است، پیام زیر را مشاهده می‌کنیم:


اما در ادامه با کلیک بر روی دکمه و تائید پیام ظاهر شده، عبارت زیر ظاهر می‌شود:



روش افزودن یک کتابخانه‌ی خارجی جاوا اسکریپتی به پروژه‌های Blazor

فرض کنید می‌خواهیم پیام‌های برنامه را توسط کتابخانه‌ی معروف جاوا اسکریپتی Toastr نمایش دهیم؛ با این دمو.
مرحله‌ی اول کار با این کتابخانه، دریافت فایل‌های CSS و JS آن است. برای این منظور قصد داریم از برنامه‌ی مدیریت بسته‌های LibMan استفاده کنیم:
dotnet tool install -g Microsoft.Web.LibraryManager.Cli
libman init
libman install bootstrap --provider unpkg --destination wwwroot/lib/bootstrap
libman install jquery --provider unpkg --destination wwwroot/lib/jquery
libman install toastr --provider unpkg --destination wwwroot/lib/toastr
بنابراین خط فرمان را در ریشه‌ی پروژه گشوده و پنج دستور فوق را اجرا می‌کنیم. دستور اول، ابزار خط فرمان LibMan را نصب می‌کند. دستور دوم، یک فایل libman.json خالی را در این پوشه ایجاد می‌کند و سه دستور بعدی، جی‌کوئری، بوت استرپ و toastr را دریافت و در پوشه‌ی wwwroot/lib قرار می‌دهند. Toastr برای اجرا، نیاز به jQuery نیز دارد.
البته تعاریف مداخل آن‌ها به فایل libman.json نیز اضافه می‌شوند. مزیت آن، اجرای دستور libman restore برای بازیابی و نصب مجدد تمام بسته‌های ذکر شده‌ی در فایل libman.json است.

پس از دریافت بسته‌های سمت کلاینت آن، مداخل مرتبط را به فایل Pages\_Host.cshtml برنامه‌ی Blazor Server اضافه خواهیم کرد (و یا در فایل wwwroot/index.html برنامه‌های Blazor WASM).
<head>
    <base href="~/" />
    <link rel="stylesheet" href="lib/toastr/build/toastr.min.css" />

</head>
<body>
 
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/toastr/build/toastr.min.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>
مدخل فایل css آن‌را در قسمت head و فایل js آن‌را پیش از بسته شدن تگ body تعریف می‌کنیم. در اینجا نیازی به ذکر پوشه‌ی آغازین wwwroot نیست؛ چون base href تعریف شده، به این پوشه اشاره می‌کند.

یک نکته: می‌توان فایل csproj برنامه را به صورت زیر تغییر داد تا کار اجرای دستور libman restore را قبل از build، به صورت خودکار انجام دهد:
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <Target Name="DebugEnsureLibManEnv" BeforeTargets="BeforeBuild" Condition=" '$(Configuration)' == 'Debug' ">
    <!-- Ensure libman is installed -->
    <Exec Command="libman --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    </Exec>
    <Error Condition="'$(ErrorCode)' != '0'" Text="libman is required to build and run this project. To continue, please run `dotnet tool install -g Microsoft.Web.LibraryManager.Cli`, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'libman'. This may take several minutes..." />
    <Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="libman restore" />
  </Target>
</Project>


روش فراخوانی یک کتابخانه‌ی خارجی جاوا اسکریپتی در پروژه‌های Blazor

پس از افزودن فایل‌های سمت کلاینت toastr و تعریف مداخل آن در فایل Pages\_Host.cshtml برنامه‌ی Blazor Server جاری، اکنون می‌خواهیم از این کتابخانه استفاده کنیم. یک روش کار با این نوع کتابخانه‌های عمومی و سراسری به صورت زیر است:
- ابتدا فایل خالی جدید wwwroot\js\common.js را ایجاد می‌کنیم.
- سپس تابع عمومی و سراسری ShowToastr را بر اساس امکانات کتابخانه‌ی toastr و مستندات آن، به صورت زیر ایجاد می‌کنیم:
window.ShowToastr = (type, message) => {
  // Toastr don't work with Bootstrap 4.2
  toastr.options.toastClass = "toastr"; // https://github.com/CodeSeven/toastr/issues/599

  if (type === "success") {
    toastr.success(message, "Operation Successful", { timeOut: 20000 });
  }
  if (type === "error") {
    toastr.error(message, "Operation Failed", { timeOut: 20000 });
  }
};
چون تابع ShowToastr به شیء window انتساب داده شده‌است، در سراسر برنامه‌ی جاری قابل دسترسی است.
سطر اول آن هم برای رفع عدم تداخل با بوت استرپ 4x اضافه شده‌است. بوت استرپ 4x به همراه کلاس‌های CSS مشابهی است که نیاز است با تنظیم toastClass به مقداری دیگر، این تداخل را برطرف کرد.

- در ادامه مدخل تعریف فایل wwwroot\js\common.js را به انتهای تگ body فایل Pages\_Host.cshtml اضافه می‌کنیم:
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/toastr/build/toastr.min.js"></script>
    <script src="js/common.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>

در آخر برای آزمایش آن به کامپوننت Pages\LearnBlazor\BlazorJS.razor مراجعه کرده و تابع سراسری ShowToastr را دقیقا مانند روشی که در مورد تابع confirm بکار بردیم، توسط سرویس JsRuntime، فراخوانی می‌کنیم:
@page "/BlazorJS"

@inject IJSRuntime JsRuntime


<div class="row">
    <button class="btn btn-success" @onclick="@(()=>TestSuccess("Success Message"))">Test Toastr Success</button>
    <button class="btn btn-danger" @onclick="@(()=>TestFailure("Error Message"))">Test Toastr Failure</button>
</div>

@code {
    async Task TestSuccess(string message)
    {
        await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message);
    }

    async Task TestFailure(string message)
    {
        await JsRuntime.InvokeVoidAsync("ShowToastr", "error", message);
    }
}
در اینجا دو دکمه، جهت فراخوانی متد ShowToastr با پارامترهای مختلفی تعریف شده‌اند. چون تابع ShowToastr خروجی ندارد، به همین جهت اینبار از متد InvokeVoidAsync استفاده کرده‌ایم. پارامتر اول آن، نام متد ShowToastr است. پارامتر‌های دوم و سوم آن با آرگومان‌های (type, message) تعریف شده‌ی تابع ShowToastr تطابق دارند. به علاوه در این مثال، روش ارسال پارامترها را نیز در onlick@ توسط arrow functions مشاهده می‌کنید.



کاهش کدهای تکراری فراخوانی متدهای جاوا اسکریپتی با تعریف متدهای الحاقی

می‌توان جهت کاهش تکرار کدهای استفاده از تابع ShowToastr، متدهای الحاقی زیر را برای سرویس IJSRuntime تهیه کرد:
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace BlazorServerSample.Utils
{
    public static class JSRuntimeExtensions
    {
        public static ValueTask ToastrSuccess(this IJSRuntime JSRuntime, string message)
        {
            return JSRuntime.InvokeVoidAsync("ShowToastr", "success", message);
        }

        public static ValueTask ToastrError(this IJSRuntime JSRuntime, string message)
        {
            return JSRuntime.InvokeVoidAsync("ShowToastr", "error", message);
        }
    }
}
و سپس فضای نام آن‌را به فایل Imports.razor_ معرفی نمود تا در تمام کامپوننت‌های برنامه قابل استفاده شوند.
@using BlazorServerSample.Utils
به این ترتیب به فراخوانی‌های ساده شده‌ی زیر خواهیم رسید:
async Task TestSuccess(string message)
{
   //await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message);
   await JsRuntime.ToastrSuccess(message);
}


فراخوانی یک متد عمومی واقع در کامپوننت فرزند از طریق کامپوننت والد

فرض کنید در کامپوننت فرزند Pages\LearnBlazor\LearnBlazor‍Components\ChildComponent.razor که در قسمت‌های قبل آن‌را تکمیل کردیم، متد عمومی زیر تعریف شده‌است:
@inject IJSRuntime JsRuntime


@code {
    // ...

    public async Task TestSuccess(string message)
    {
        await JsRuntime.ToastrSuccess(message);
    }
}
اکنون اگر بخواهیم این متد عمومی را از طریق کامپوننت والد یا دربرگیرنده‌ی آن فراخوانی کنیم، نیاز است از مفهوم جدیدی به نام ref استفاده کرد. برای این منظور به کامپوننت Pages\LearnBlazor\ParentComponent.razor مراجعه کرده و تغییرات زیر را اعمال می‌کنیم:
@page "/ParentComponent"

<ChildComponent
    OnClickBtnMethod="ShowMessage"
    @ref="ChildComp"
    Title="This title is passed as a parameter from the Parent Component">
    <ChildContent>
        A `Render Fragment` from the parent!
    </ChildContent>
    <DangerChildContent>
        A danger content from the parent!
    </DangerChildContent>
</ChildComponent>

<div class="row">
    <button class="btn btn-success" @onclick="@(()=>ChildComp.TestSuccess("Done!"))">Show Alert</button>
</div>


@code {
    ChildComponent ChildComp;
    // ...
}
با استفاده از ref@ که به فیلد ChildComp انتساب داده شده‌است، می‌توان ارجاعی از کامپوننت فرزند را (وهله‌ای از کلاس مرتبط با آن‌را) در کامپوننت جاری بدست آورد و سپس از آن جهت فراخوانی متدهای عمومی کامپوننت فرزند استفاده کرد.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-11.zip
مطالب
تهیه‌ی کارت با فرمت PDF با استفاده از کتابخانه iTextSharp
فرض کنید که می‌خواهید برای کاربری پس از ثبت اطلاعاتش در سایت، کارتی به فرمت PDF صادر کنید تا آن را دریافت و سپس چاپ کند. حتما از این دست موارد زیاد مشاهده کرده اید؛ مانند دریافت کارت ورود به جلسات امتحانی، کارت ورود به همایش‌ها و کنسرت‌های موسیقی و ...

برای تهیه فایل PDF، به غیر از کتابخانه‌های گزارش گیری تجاری، می‌توان از کتابخانه‌ی iTextSharp که گزینه‌ای سورس باز، با کیفیت و محبوب است، استفاده کرد. متاسفانه این کتابخانه دارای محیط گرافیکی طراحی گزارش نیست و کار با آن فقط از طریق کدنویسی میسر است که صد البته انعطاف پذیری و پویایی قابل توجهی را برای تهیه‌ی گزارش نسبت به ابزار‌های طراحی گرافیکی، در اختیار برنامه نویس قرار می‌دهد. البته می‌توان از برنامه‌ی Open Office برای طراحی قالب گزارش نیز استفاده کرد، اما من پس از استفاده، به کیفیت و انعطاف پذیری و امکانات مورد نظرم نتوانستم دست یابم و تصمیم گرفتم برای تهیه‌ی کارت، مستقیما با iTextSharp کد نویسی انجام دهم.

در این مقاله به نحوه‌ی تهیه یک کارت به فرمت PDF با استفاده از کتابخانه iTextSharp خواهیم پرداخت که این کتابخانه به فناوری خاصی گره نخورده است و در تمامی برنامه‌های ASP.NET ، WPF، Windows Form و در کل هر کجا که دات نت فریمورک در دسترس است، قابل استفاده می‌باشد.

فرض کنید کارتی به شکل زیر می‌خواهیم بسازیم (تمامی تصاویر از سطح اینترنت جمع آوری شده‌اند):


طبق این عکس، طرح بندی کارت مورد نظر به این شکل است که در بالای کارت باید آرم سازمان، عنوان در وسط و عکس فرد در سمت چپ باشد و در ادامه نام و مشخصات فرد درج شده باشند. همچنین عکسی به صورت شفاف، تمام پس زمینه کارت را پوشانده است.

 فرقی نمی‌کند که تکنولوژی مورد استفاده شما چیست، برای سادگی کار این مثال را با یک Console Application آغاز کنید. برای نصب iTextSharp نیز فرمان زیر را در کنسول NuGet وارد کنید:
Install-Package iTextSharp


شروع کار با iTextSharp

معمولا برای کار با iText یک سری روال تکراری از قبیل انتخاب نام فایل نهایی، تعریف فونت، سایز کاغذ، حاشیه بندی و ... را باید طی کنید که کدهای آن را در ذیل مشاهده می‌کنید:

            var fileStream = new FileStream("card.pdf", FileMode.Create, FileAccess.Write, FileShare.None);

            var docFont = GetFont();

            var pageSize = PageSize.A6.Rotate(); // سایز کارت را اینجا باید مشخص کرد

            var doc = new Document(pageSize);

            doc.SetMargins(18f, 18f, 15f, 2f);

            var pdfWriter = PdfWriter.GetInstance(doc, fileStream);

            doc.Open();
-  در اینجا سایز کارت، بر روی کاغذ A6 در حالت افقی قرار داده شده است. بدیهی است که مطابق نیاز خودتان می‌توانید این سایز را تغییر دهید.
-  تابع GetFont یک تابع کمکی است که در سورس نهایی ارائه شده است و نکته تعریف فونت در iTextSharp  در آن رعایت شده است.
 - بقیه موارد نیز جزء الزامات کار با این کتابخانه است.


برای درج عکس به صورت شفاف در پس زمینه کارت باید از کد زیر استفاده کرد:

            //  درج لوگوی مسابقات به صورت شفاف در پس زمینه
            var canvas = pdfWriter.DirectContentUnder;
            var logoImg = Image.GetInstance(competitionImagePath);
            logoImg.SetAbsolutePosition(0, 0);
            logoImg.ScaleAbsolute(pageSize);
            var graphicsState = new PdfGState { FillOpacity = 0.2F };
            canvas.SetGState(graphicsState);
            canvas.AddImage(logoImg);
 

چیدمان و طرح بندی بندی عناصر در iTextSharp

برای طراحی کارت یا کلا کار طراحی، باید با نحوه‌ی قرار دادن و طرح بندی عناصر مثل تصاویر و نوشته‌ها و ابزارهای مورد نیاز برای این کار، آشنا شوید. خوشبختانه در iText برای این کار ابزارهای خوبی وجود دارد.

حتما با تگ Table در HTML آشنایی دارید. در سال‌های دور، حتی کل صفحه‌ی وب را به وسیله‌ی Table ساختار دهی می‌کردند. در iTextSharp نیز کلاسی به نام PdfPTable در دسترس است که می‌توان از آن به عنوان قالبی برای قرار دادن عناصر، در صفحه استفاده کرد. این Table همانند هر جدولی دارای یک سری سطر و ستون است که می‌توانیم عناصر مورد نظرمان مثل تصویر و نوشته و... را در آن قرار دهیم. 

همانطور که از تگ Table در HTML می‌توان برای رسم جدول و قرار دادن عناصر در سطر و ستون‌های آن استفاده کرد، در iText نیز می‌توان از کلاس PdfPTable برای ترسیم جدول و از متد AddCell آن برای افزودن سلول به آن استفاده کرد. کار با کلاس PdfPTable نیز ساده است. کافی هست به هنگام ساخت نمونه‌ای از آن، در سازنده‌اش تعداد ستون‌های جدول را ذکر کنید. سپس با استفاده از متد AddCell آن، پارامتری از جنس PdfPCell برای آن ارسال کنید تا به جدول، سلول جدیدی اضافه شود و در صورتیکه تعداد سلول‌های جدید، از تعداد ستون‌های تعریف شده بیشتر شود، iText به صورت خودکار سلول‌های اضافی را به ردیف جدیدی منتقل می‌کند. البته برای افزودن سطر و ستون، روش‌های دیگری نیز هست؛ ولی گویا  روش مرجح همین روش است.  کلاس PdfPCell که نقش سلول‌های جدول را بازی می‌کند، نیز می‌تواند شامل متن، تصویر و یا حتی یک جدول تودرتو باشد.
  
در ادامه کدهای جدول بالایی کارت شامل لوگوی دانشگاه، عنوان مسابقات و عکس فرد را مشاهده می‌کنید:
            // جدولی که برای چیدمان عناصر ارم دانشگاه و عنوان و عکس شخص استفاده می‌شود
            var topTable = new PdfPTable(3)
            {
                WidthPercentage = 100,
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                ExtendLastRow = false,
            };

            var universityLogoImage = Image.GetInstance(universityLogoPath);

            universityLogoImage.ScaleAbsolute(70, 100);

            topTable.AddCell(new PdfPCell(universityLogoImage)
            {
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            topTable.AddCell(new PdfPCell(new Phrase("کارت مسابقات دانشگاه آزاد اسلامی", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_CENTER,
                Border = 0,
            });

            var userImage = Image.GetInstance(userModel.ImagePath);
            userImage.Border = Rectangle.TOP_BORDER | Rectangle.RIGHT_BORDER | Rectangle.BOTTOM_BORDER | Rectangle.LEFT_BORDER;
            userImage.BorderWidth = 1f;
            userImage.BorderColor = new BaseColor(204, 204, 204); // gray color
            userImage.ScaleAbsolute(70, 100);

            topTable.AddCell(new PdfPCell(userImage)
            {
                HorizontalAlignment = 2,
                Border = 0
            });

            int[] topTableColumnsWidth = { 10, 25, 10 };

            topTable.SetWidths(topTableColumnsWidth);

            doc.Add(topTable);
-  در ابتدا یک جدول سه ستونه تعریف شده است. تعداد ستون‌ها در هنگام نمونه سازی از کلاس PdfPTable، در سازنده‌ی آن ذکر شده است.
-  در iText برای کار با تصاویر، باید از کلاس Image و متد GetInstance فراهم شده توسط خود کتابخانه استفاده کرد. سپس این تصویر را باید به عنوان پارامتر به سلول جدول ارسال کرد.
-  به کمک متد AddCell، می‌توان به جدول، سلول اضافه کرد و به صورت خودکار سلول‌های جدیدی که از تعداد ستون‌ها بیشتر می‌شوند، به سطر جدید منتقل می‌شوند.
-  اگر می‌خواهید در سلولی متنی نمایش دهید، از کلاس Phrase و تعیین صریح فونت آن استفاده کنید؛ چرا که در غیر این صورت ممکن است متون فارسی نمایش داده نشود.
-  در انتها هم جدول مورد نظر را باید به شی doc از جنس کلاس Document تعریف شده اضافه کرد.
 
بدیهی هست که اطلاعات شخص مثل نام، نام خانوادگی و ... را نیز باید در یک جدول چهار ستونه قرار داد و نکته‌ی خاص اضافه‌تری ندارد. 

حال اگر بیاییم این تکه کدها را کنار هم قرار بدهیم به کدی قابل اجرا خواهیم رسید.
ابتدا کلاسی را که در برگیرنده‌ی اطلاعات فرد است، تعریف می‌کنیم:
    public class UserModel
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string StudentNumber { get; set; }
        public string NationalCode { get; set; }
        public string UniversityName { get; set; }
        public string ImagePath { get; set; }
    }
 
- سپس کلاس CardReport را که اصل و اساس بحث ما بود، تعریف می‌کنیم.
using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Font = iTextSharp.text.Font;
using Image = iTextSharp.text.Image;
using Rectangle = iTextSharp.text.Rectangle;

namespace ITextSharpCardSample
{
    public class CardReport
    {
        public static void Generate(UserModel userModel, string competitionImagePath, string universityLogoPath)
        {
            var fileStream = new FileStream("card.pdf", FileMode.Create, FileAccess.Write, FileShare.None);

            var docFont = GetFont();

            var pageSize = PageSize.A6.Rotate(); // سایز کارت را اینجا باید مشخص کرد

            var doc = new Document(pageSize);

            doc.SetMargins(18f, 18f, 15f, 2f);

            var pdfWriter = PdfWriter.GetInstance(doc, fileStream);

            doc.Open();

            //  درج لوگوی مسابقات به صورت شفاف در پس زمینه
            var canvas = pdfWriter.DirectContentUnder;
            var logoImg = Image.GetInstance(competitionImagePath);
            logoImg.SetAbsolutePosition(0, 0);
            logoImg.ScaleAbsolute(pageSize);
            var graphicsState = new PdfGState { FillOpacity = 0.2F };
            canvas.SetGState(graphicsState);
            canvas.AddImage(logoImg);


            // جدولی که برای چیدمان عناصر ارم دانشگاه و عنوان و عکس شخص استفاده می‌شود
            var topTable = new PdfPTable(3)
            {
                WidthPercentage = 100,
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                ExtendLastRow = false,
            };

            var universityLogoImage = Image.GetInstance(universityLogoPath);

            universityLogoImage.ScaleAbsolute(70, 100);

            topTable.AddCell(new PdfPCell(universityLogoImage)
            {
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            topTable.AddCell(new PdfPCell(new Phrase("کارت مسابقات دانشگاه آزاد اسلامی", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_CENTER,
                Border = 0,
            });

            var userImage = Image.GetInstance(userModel.ImagePath);
            userImage.Border = Rectangle.TOP_BORDER | Rectangle.RIGHT_BORDER | Rectangle.BOTTOM_BORDER | Rectangle.LEFT_BORDER;
            userImage.BorderWidth = 1f;
            userImage.BorderColor = new BaseColor(204, 204, 204); // gray color
            userImage.ScaleAbsolute(70, 100);

            topTable.AddCell(new PdfPCell(userImage)
            {
                HorizontalAlignment = 2,
                Border = 0
            });

            int[] topTableColumnsWidth = { 10, 25, 10 };

            topTable.SetWidths(topTableColumnsWidth);

            doc.Add(topTable);


            // جدول مشخصات شرکت کننده مثل نام و نام خانوادگی
            var infoTable = new PdfPTable(4)
            {
                WidthPercentage = 100,
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                ExtendLastRow = false,
                SpacingBefore = 15,
            };

            infoTable.AddCell(new PdfPCell(new Phrase("نام:", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0,
                PaddingBottom = 15
            });

            infoTable.AddCell(new PdfPCell(new Phrase(userModel.FirstName, docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            infoTable.AddCell(new PdfPCell(new Phrase("نام خانوادگی:", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            infoTable.AddCell(new PdfPCell(new Phrase(userModel.LastName, docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            infoTable.AddCell(new PdfPCell(new Phrase("شماره\nدانشجویی:", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0,
                PaddingBottom = 15
            });

            infoTable.AddCell(new PdfPCell(new Phrase(userModel.StudentNumber, docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            infoTable.AddCell(new PdfPCell(new Phrase("کد ملی:", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            infoTable.AddCell(new PdfPCell(new Phrase(userModel.NationalCode, docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            infoTable.AddCell(new PdfPCell(new Phrase("واحد دانشگاهی:", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            infoTable.AddCell(new PdfPCell(new Phrase(userModel.UniversityName, docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });


            // دو سلول بعدی صرفا جهت تکمیل شدن یک ردیف است تا عملکرد صحیح خود را داشته باشد
            infoTable.AddCell(new PdfPCell(new Phrase("", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });

            infoTable.AddCell(new PdfPCell(new Phrase("", docFont))
            {
                RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                HorizontalAlignment = Element.ALIGN_LEFT,
                Border = 0
            });


            int[] infoTableColumnsWidth = { 20, 15, 20, 15 };

            infoTable.SetWidths(infoTableColumnsWidth);

            doc.Add(infoTable);

            doc.Close();
        }

        private static Font GetFont()
        {
            const string fontName = "Iranian Sans";

            if (FontFactory.IsRegistered(fontName))
                return FontFactory.GetFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

            var fontPath = "Fonts/irsans.ttf"; // مسیر فونت

            FontFactory.Register(fontPath);

            return FontFactory.GetFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        }

    }

}
نکته: حتما به تعریف فونت در پوشه‌ی Fonts و عکس‌ها در پوشه Images توجه فرمایید.

و در انتها نحوه‌ی استفاده از کلاس CardReport در یک برنامه‌ی Console: 
    class Program
    {
        static void Main(string[] args)
        {
            var userModel = new UserModel
            {
                FirstName = "علی",
                LastName = "احمدی",
                NationalCode = "1234567890",
                StudentNumber = "23242342",
                UniversityName = "آزاد",
                ImagePath = "Images/avatar.jpg"
            };

            CardReport.Generate(userModel, "Images/competition_logo.jpg", "Images/university_logo.png");

            System.Diagnostics.Process.Start("card.pdf");

        }
    }
     
اشتراک‌ها
چگونه بفهمیم فایل انتخابی واقعا عکسه یا نه ؟

موقعی که از کاربر یک عکس رو دریافت میکنیم نیازه که بررسی شه که آیا این فایل واقعا عکسه یا نه.

برای کنترل این موضوع Attribute‌های اعتبارسنجی بهترین انتخاب هستن و با یکبار افزودن میتوان از آنها در هر جایی استفاده کرد.

سمت سرور اینکار به این صورت هست ^

اما سمت کلاینت حتی اگه بیایم فرمت فایل رو هم بررسی کنیم بازهم جوابگو نیست و میشه به راحتی فرمت یک فایل ZIP رو به PNG تغییر داد و ارسالش کرد و عملا اعتبارسنجی سمت کلاینت کار خاصی رو انجام نمیده.

در این پروژه Attribute اعتبارسنجی برای بررسی فایل که آیا واقعا عکس هست یا نه نوشته شده، هم سمت کلاینت و هم سمت سرور.

نحوه انجام کار: برای سمت کلاینت عکسی که کاربر وارد میکنه رو در داخل تگ IMG نمایش میده و اگه عکس به درستی لود بشه رخداد OnLoad تگ IMG فراخوانی میشه و اعتبارسنجی پاس میشه.

تگ IMG به کاربر نمایش داده نمیشه و به صورت display: none هستش.

از این Attribute اعتبارسنجی در کنار Attribute‌های کنترل حجم فایل هم میشه استفاده کرد که قبل از اینکه بخوایم بررسی کنیم که آیا فایل واقعا عکسه یا نه ابتدا حجم فایل مورد بررسی قرار بگیره.

چگونه بفهمیم فایل انتخابی واقعا عکسه یا نه ؟
بازخوردهای پروژه‌ها
عدم امکان آپلود تصویر
سلام
هنگام آپلود تصویر امکان آپلود تصویر وجود ندارد وقتی هم ویرایش میکنم که این امکان اضافه بشه بصورت زیر
<input type="file" name="files" required />
و در کنترلر 
public virtual ActionResult AddPost(AddPostModel postModel, HttpPostedFileBase files)
در هر صورت به files  مقدار Null داده میشه
دلیل چیست؟ کجا باید اصلاح بشه؟
مرسی
نظرات مطالب
مقدمه‌ای بر یادگیری ماشین در #C با استفاده از ML.NET
سلام ممنون بابت توضیحات خوبتون
توی مستندات خود مایکروسافت (اگر اشتباه نکنم) مثالی ارائه شده که در اون از یک فایل به عنوان دیتابیس استفاده شده و در ستون اول عکسی از یک گل، و در ستون دوم تعداد برگ‌های گل نوشته شده و با آموزش دادن برنامه میتونه تعداد برگ‌های هر گلی رو شناسایی کنه.
حالا سوال من این هست ایا این امکان وجود داره که ما به جای عکس گل، عکس کپچا و به جای تعداد برگ، عدد و حروف (که داخل کپچا نوشته شده) رو وارد کنیم؟ در واقع میخوام به کمک ML کدهای کپچا رو بصورت خودکار شناسایی و در فیلد پر کنم.
نظرات مطالب
OpenCVSharp #17
 خیلی ممنون؛ من خودم چند ماه قبل داشتم یه سیستم تشخیص و شناسایی چهره با کمک این سایت مینوشتم... که خوب متاسفانه نتیجه خوبی نگرفتم به این خاطر که بعضی چهره‌ها رو به اشتباه تشخیص می‌داد... البته تعداد گروههای عکس هایی که من Train کردم زیاد بود شاید به این خاطر اشتباه تشخیص می‌داد... فک کنم در حدود 50 گروه بود که در هر گروه 10 عکس برای Train وجود داشت.
به نظر شما بهترین Library برای تشخیص و شناسایی چهره چیه؟ و اینکه این درست است که اگر تعداد گروههای Train زیاد باشه احتمال تشخیص صحیح کم میشه یا اینکه من یه جای کار اشتباه کردم؟ 
مطالب
شروع کار با webpack - قسمت سوم
در مطلب قبلی با فایل‌های پیکربندی وبپک، وب سرور وبپک، لودر‌ها و ... آشنا شدیم .


استفاده از preLoader‌ها در وبپک 
پیش‌تر با Loader‌ها آشنا شدیم و دلیل استفاده‌ی از آنها نیز ذکر و Loader تایپ اسکریپت را نیز نصب کرده و با استفاده از آن فایل‌های پروژه را ترنسپایل کردیم. اما ممکن است که همه‌ی کارها در استفاده از یک Loader خلاصه نشوند. ممکن است بخواهید از یک ابزار Linting مانند jsHint  قبل از اجرای Loader ها بهره ببرید و این دقیقا کاری است که به preLoader‌ها سپرده می‌شود. به عنوان مثال پیش لودر jsHint را نصب خواهیم کرد. اضافه کردن preLoader‌ها در فایل پیکربندی وبپک تفاوتی با Loader‌ها نخواهد داشت و دارای همان قسمت‌هایی است که برای Loader‌ها تعریف کردیم.
پیش از هر کاری ابتدا jsHint را نصب کرده و سپس loader آن را نیز نصب می‌کنیم. دستورات مورد نیاز، در ادامه آورده شده اند:
npm install -D jsHint jsHint-loader
سپس در ادامه به فایل پیکربندی وبپک مراجعه کرده و قسمت preLoader را به آن اضافه می‌کنیم:
module.exports = {
    entry:['./shared.js','./main.ts']
    ,output:{
        filename:'bundle.js'
    }
    ,watch :true
    ,module:{
        preLoaders:[
            {
                test:/\.js$/
                ,exclude:/node_modules/
                ,loader:'jshint-loader'
            }
        ],
        loaders:[
            {
                test:/\.ts$/
                ,exclude:/node_modules/
                ,loader:'ts-loader'
            }
        ]
    }
    
}

حال وب سرور وبپک را اجرا می‌کنیم. در خط فرمان نتیجه‌ی اجرا شدن jsHint در تصویر قابل مشاهده است که از دو فایل پروژه ایراد گرفته است:


همچنین برای تکمیل قابل ذکر است که وبپک دارای postLoaders نیز می‌باشد که پس از Loader‌های اصلی اجرا می‌شوند.
لیست کاملی از Loader‌ها را می‌توانید در اینجا مشاهده کنید .تمامی لودرهای وبپک

Minify کردن باندل‌ها با استفاده از وبپک

در صورتی که باندل ساخته شده تا به اینجای کار را باز کرده باشید، مشاهده کرده‌اید که باندل ساخته شده Minify شده نیست. ساده‌ترین روش جهت Minify کردن باندل ساخته شده در هنگام فراخوانی وبپک با استفاده از یک پرچم در خط فرمان می‌باشد. دستور مورد نیاز در ادامه آورده شده است.
// در حالتی که به صورت محلی وبپک نصب شده است
npm run webpack -- -p
// درحالتی که وبپک به صورت سراسری اجرا می‌شود
webpack -p
حال در صورتی که به باندل ساخته شده مراجعه کنید، باندل در حالت Minify شده قرار دارد.

اضافه کردن فایل پیکربندی مخصوص بیلد‌های اصلی پروژه

قطعا شما نیز در حین توسعه‌ی پروژه از دستورات لاگ یا اندازه گیری زمان اجرای یک قطعه کد و ... استفاده می‌کنید و حضور این کد‌ها در باندل نهایی دلیلی ندارد. یک راهکار این است که کدهایی را که فقط جهت توسعه‌ی پروژه و دیباگ بودند و سودی در نتیجه‌ی نهایی ندارند، به صورت دستی پاک کنیم و در صورتی که حجم این طور دستورات بالا باشند، قطعا کار جالبی نخواهد بود و همچنین حذف کلی این دستورات نیز در ادامه برای برگشت به پروژه ممکن است مشکل زا باشد.
راهکاری که با وبپک می‌توان پیش گرفت این است که از یک Loader جهت بیلد‌های اصلی استفاده کرده و مثلن تمامی کامنت‌ها و دستورات لاگ کننده و ... را از بیلد نهایی حذف کنیم. جهت اینکار یک فایل پیکربندی مخصوص را به بیلدهای اصلی به پروژه اضافه می‌کنیم و اسم فایل را webpack.prod.config.js می‌گذاریم.
قدم بعدی نصب یک loader می‌باشد که وظیفه‌ی حذف مواردی را دارد که برای آن مشخص خواهیم کرد. این لودر strip-loader نام دارد و با دستور زیر آن را در پروژه اضافه می‌کنیم.
npm install -D strip-loader
سپس وارد فایل پیکربندی که فقط جهت تولید باندل‌های اصلی پروژه ایجاد کرده‌ایم (webpack.prod.config.js) می‌شویم و کد‌های زیر را وارد می‌کنیم.
// webpack.prod.config.js
 //تنظیمات قبلی را می‌خوانیم
var devConfig = require("./webpack.config.js");
// لودری که وظیفه‌ی حذف کردن دارد را وارد می‌کنیم
var stripLoader = require("strip-loader");

// مانند قبل یک آبجکت با موارد مورد نظر برای لودر می‌سازیم
var stripLoaderConfig = {
    test:[/\.js$/,/\.ts$/],
    exclude :/node_modules/
    ,loader:stripLoader.loader("console.log")
}

// اضافه کردن به لیست لودرهای قبلی
devConfig.module.loaders.push(stripLoaderConfig);

// و در آخر اکسپورت کردن تنظیمات جدید وقبلی
module.exports = devConfig;
در توضیح کد‌های بالا در خط اول ابتدا فایل پیکربندی را که در توسعه‌ی عادی پروژه استفاده می‌شد، می‌خوانیم و در آبجکتی با نام devConfig ذخیره می‌کنیم. مزیت اینکار این می‌باشد که از تعریف تنظیمات تکراری و مورد نیاز جلوگیری می‌کند (نکته : اگر بخاطر داشته باشید در مطلب قبلی ذکر شد که فایل‌های پیکربندی در فرمت commonjs می‌باشند و در نتیجه امکان وارد کردن آنها با استفاده از تابع require امکان پذیر است).
خط بعدی نیز loader ی که وظیفه‌ی حذف موارد مورد نظر ما را دارد وارد می‌کنیم و در ادامه یک آبجکت را با تعاریفی که قبلن از لودر‌ها داشتیم می‌سازیم. تنها نکته در قسمت تعریف اسم لودر می‌باشد.
loader:stripLoader.loader("console.log")
در کد بالا مواردی را که مورد نیاز است از سورس اصلی در سورس نهایی حذف شود، به لودر معرفی می‌کنیم. در اینجا تمامی دستوراتی که شامل console.log می‌شوند از بیلد نهایی باندل حذف خواهند شد.
تنها مرحله‌ی باقی مانده، فراخوانی وبپک با استفاده از فایل پیکربندی جدید می‌باشد. برای اینکار با استفاده از پرچم config محل فایل جدید پیکربندی را به وبپک معرفی می‌کنیم تا از فایل پیکربندی پیش فرض قبلی استفاده نکند.
// در حالتی که وبپک به صورت محلی نصب شده است
npm run webpack -- --config webpack.prod.config.js -p
// در حالتی که وبپک به صورت سراسری نصب شده باشد
webpack --config webpack.prod.config.js -p
پس از اجرای این دستور و باز کردن صفحه‌ی index.html خواهید دید که پیغامی در کنسول مرورگر ظاهر نخواهد شد و دستورات console.log همگی حذف شده‌اند.
توجه داشته باشید که اگر برای ساخت باندل از وبپک استفاده کرده‌اید، برای میزبانی فایل‌های پروژه از وب سرور وبپک استفاده نکنید و از وب سروری دیگر (مانند http-server یا IIS و ...) استفاده کنید؛ چرا که باندل توسط وب سرور وبپک دوباره ساخته می‌شود و تغییرات از بین می‌روند. در صورتی که می‌خواهید از وب سرور وبپک برای میزبانی بیلد نهایی پروژه نیز استفاده کنید، فایل پیکربندی بیلد نهایی را نیز به وب سرور وبپک با استفاده از دستور زیر معرفی کنید:
// زمانی که وبپک به صورت محلی در پروژه نصب شده است
npm run webpackserver -- --config webpack.prod.config.js  -p
//در حالتی که وبپک به صورت گلوبال ( سراسری ) نصب می‌باشد
webpack-dev-server --config webpack.prod.config.js  -p

مدیریت فایل و فولدرها با استفاده وبپک
تا به اینجای کار اگر به ساختار چینش فایل‌ها در پروژه دقت کنید خواهید دید که همگی فایل‌ها در مسیر اصلی پروژه قرار دارند و این روش مناسبی برای مدیریت فایل‌ها و فولدرها در پروژه‌های واقعی و بزرگ نیست. در ادامه قصد داریم این مسئله را حل کرده و ساختار مشخصی را برای محل قرارگیری فایل‌های پروژه با کمک وبپک ایجاد کنیم.
در اولین قدم فولدری را برای اسکریپت‌ها با نام js ایجاد کرده و اسکریپت‌ها را به این فولدر انتقال می‌دهیم.
در قدم دوم برای فایل‌های استاتیک پروژه مانند صفحات html و ... فولدر دیگری را با نام assets ایجاد می‌کنیم و این گونه فایل‌ها را در آن قرار خواهیم داد.
تا اینجای کار در صورتی که وبپک را اجرا کنید، مسیرهای جدید را پیدا نخواهد کرد و دچار خطا خواهد شد. پس به فایل پیکربندی ( webpack.config.js ) مراجعه کرده و وبپک را از ساختار جدید پروژه خبردار می‌کنیم.
// new webpack.config.js file
//ماژول توکار نود جی اس
var path = require("path");

module.exports = {
    // مشخص کردن زمینه برای فایل‌های ورودی
    context:path.resolve("js"),
    entry:['./shared.js','./main.ts']
    ,output:{
       // مشخص کردن محل قرارگیری باندل ساخته شده
        path:path.resolve("build/js"),
      // درخواست از سمت چه مسیری برای باندل خواهد آمد ؟
        publicPath:"assets/js",
        filename:'bundle.js'
    }
    ,
    devServer:{
        //راهنمایی برای وب سرور جهت اینکه فایل‌ها را از چه محلی سرو کند
        contentBase:"assets"
    }

    ,watch :true
    ,module:{
        
        loaders:[
            {
                test:/\.ts$/
                ,exclude:/node_modules/
                ,loader:'ts-loader'
            }
        ]
    }
    
}
در خط اول فایل پیکربندی جدید، ماژول توکار path از نود جی اس را وارد می‌کنیم و سپس کلید جدیدی را به تنظیمات وبپک با نام context اضافه می‌کنیم که زمینه‌ی فایل‌های ورودی را مشخص خواهد کرد. قبلا ذکر شد که فولدری با نام js را ساخته و اسکریپت‌ها را در آن قرار می‌دهیم. پس با کمک ماژول path این مسیر را به وبپک معرفی می‌کنیم.
context:path.resolve("js")
تغییر بعدی را در تنظیمات برای ساخت باندل داریم که مشخص کردن مسیر قرارگیری جدید باندل می‌باشد که با کلید جدیدی با نام path، مسیر قرارگیری باندل پس از ساخته شدن را به وبپک اطلاع می‌دهیم و در ادامه کلید دیگری با نام publicPath اضافه شده که راهنمایی برای وب سرور وبپک می‌باشد تا با استفاده از آن درخواست‌هایی که به مسیر مشخص شده می‌آیند، از مسیری که در کلید path نامیده شده سرو شوند.
// تغییرات در شی output
// این کلید جدید مسیر قرار گیری جدید باندل را به وبپک اطلاع می‌دهد
path:path.resolve("build/js"),
//  راهنما برای وب سرور وبپک جهت میزبانی مسیر زیر از کلید بالا
publicPath:"assets/js",
آخرین تغییر در فایل پیکربندی، مربوط به اضافه شدن آبجکت جدید devServer می‌باشد که در آن کلیدی اضافه شده که مسیر اصلی فایل‌های میزبانی شده را اعلام می‌کند. به طور مثال فایل html اصلی پروژه را بالاتر اشاره کردیم که در این مسیر قرار می‌دهیم.
در نهایت وارد فایل index.html می‌شویم و مسیر جدید باندل را به آن معرفی میکنیم.
//index.html

<html>
    <head>
        first part of webpack tut!
    </head>
    <body>
        <h1>webpack is awesome !</h1>
        <script src="assets/js/bundle.js"></script>
    </body>
</html>
قابل مشاهده است که مسیر باندل ذکر شده در اینجا وجود خارجی ندارد و وبپک آن را با کمک تنظیماتش، به صورت پویا پیدا خواهد کرد. در تصویر زیر سعی بر روشن‌تر شدن این مسئله شده است.


حال با اجرا کردن وب سرور وبپک می‌توان مشاهده کرد که مسیرهای جدید، بدون مشکل توسط وبپک پیدا شده و فایل‌ها سرو می‌شوند. قابل توجه است که این نوع چینش پروژه قابل تغییر و شخصی سازی برای پروژه‌های گوناگون می‌باشد.


ساخت فایل‌های سورس مپ  (source map)


ساده‌ترین راه جهت ساخت فایل‌های سورس مپ با استفاده از یک پرچم در هنگام فراخوانی وبپک به صورت زیر می‌باشد.

// فعال کردن ساخت سورس مپ‌ها 
npm run webpack -- -d 
// یا  در هنگام نصب گلوبال
webpack -d
// جهت استفاده به همراه وب سرور
npm run webpackserver -- -d
// یا به صورت نصب گلوبال
webpack-dev-server -d
راه دوم با استفاده از انجام تغییرات در فایل پیکربندی وبپک می‌باشد؛ به این صورت که کلیدی را به این فایل اضافه می‌کنیم.
// webpack.config.js
// کلید جدید اضافه شده در فایل پیکربندی
devtool:"#source-map"
حال در هنگام فراخوانی وبپک فایل سورس مپ نیز ساخته خواهد شد و احتیاجی به استفاده از پرچم خط فرمان در هنگام فراخوانی نیست.
با اجرای وب سرور وبپک خواهید دید که سورس مپ‌ها در منوی توسعه دهنده‌ی مرورگر قابل دستیابی می‌باشند.

ساخت چندین باندل گوناگون

قصد داریم به پروژه، دو صفحه‌ی دیگر را نیز با نام‌های aboutme و contact اضافه کنیم. هر یک از این صفحات اسکریپت مخصوص به خود را خواهد داشت و باندل نهایی نیز شامل تمامی آنها خواهد شد. در صورتی که این خروجی مطلوب ما نباشد و به طور مثال بخواهیم مکانیزمی شبیه به lazy loading اسکریپت‌ها را داشته باشیم و فقط زمانی اسکریپت‌ها بارگذاری شوند که به آنها احتیاج باشد، برای انجام این کار با وبپک به صورت زیر عمل خواهیم کرد.
دو صفحه‌ی html جدید را با عناوین ذکر شده‌ی بالا به پوشه‌ی assets اضافه می‌کنیم و برای هریک نیز اسکریپتی با همان نام خواهیم ساخت و در پوشه‌ی js قرار می‌دهیم.
محتوای صفحات بدین شکل می‌باشد.
// index.html file
<html>
<head>

    <title>
        third part of webpack tut!
    </title>
</head>

<body>
    <nav>
        <a href="aboutme.html">about me</a>
        <a href="contact.html">contact</a>
    </nav>
    <h1>webpack is awesome !</h1>
    <script src="assets/js/shared.js"></script>
    <script src="assets/js/index.js"></script>
</body>

</html>

// aboutme.html file
<html>

<head>

    <title>
        about me page !
    </title>
</head>

<body>
    <nav>
        <a href="index.html">index</a>
        <a href="contact.html">contact</a>
    </nav>
    <h1>webpack is awesome !</h1>
    <script src="assets/js/shared.js"></script>
    <script src="assets/js/aboutme.js"></script>
</body>

</html>

// contact.html file

<html>

<head>

    <title>
        contact me page !
    </title>
</head>

<body>
    <nav>
        <a href="index.html">index</a>
        <a href="aboutme.html">about me</a>
    </nav>
    <h1>webpack is awesome !</h1>
    <script src="assets/js/shared.js"></script>
    <script src="assets/js/contact.js"></script>
</body>

</html>
در هر یک از صفحات یک اسکریپت مخصوص آن صفحه و همچنین یک اسکریپت با نام shared.js که قبلا فرض کردیم نقش ماژولی را دارد که در سرتاسر پروژه از آن استفاده می‌شود، اضافه شده است. حال این تغییرات را در فایل پیکربندی وبپک به آن معرفی می‌کنیم.
var path = require("path");
var webpack = require("webpack");
// وارد کردن پلاگینی از وب پک برای ساخت تکه‌های مختلف اسکریپت‌ها 
// معرفی اسکریپت shared.js
var commonChunkPlugin = new webpack.optimize.CommonsChunkPlugin("shared.js");
module.exports = {
    context:path.resolve("js"),
    //entry:['./shared.js','./main.ts']
   // معرفی اسکریپت‌های جدید به وبپک
    entry:{
        index:"./main.js",
        aboutme:"./aboutme.js",
        contact:"./contact.js"
    }
    ,output:{
        path:path.resolve("build/js"),
        publicPath:"assets/js",
     //   filename:'bundle.js'
     // به جای یک باندل کلی از وبپک میخاهیم برای هر ورودی باندلی جدید بسازد
        filename:"[name].js"
    }
    // رجیستر کردن پلاگین 
    ,plugins:[commonChunkPlugin]
    ,
    devServer:{
        contentBase:"assets"
    }
    //,devtool:"#source-map"
    ,watch :true
    ,module:{...
    }
    
}
جهت انجام این کار از یک پلاگین وبپک با نام CommonsChunkPlugin کمک گرفته‌ایم که به ما کمک می‌کند اسکریپت shared.js را به عنوان یک وابستگی در تمامی صفحاتمان داشته باشیم. نحوه‌ی کار این پلاگین نیز از نامش مشخص است و نقاط Common را می‌توان با آن مشخص کرد. تغییر بعدی نیز در کلید entry می‌باشد که اسکریپت‌های جدید را با استفاده از اسمشان به وبپک معرفی کرده‌ایم و سپس در کلید output نیز به وبپک خبر داده‌ایم که برای هر ورودی، باندل جداگانه‌ی خود را بسازد. تکه کد  filename:[name].js به وبپک می‌گوید که باندل‌های جداگانه، با نام خود اسکریپت ساخته شوند و در نهایت کلید جدید plugins به وبپک پلاگین CommonsChunkPlugin را اضافه می‌کند.
حال با اجرای وبپک می‌توان دید که سه باندل ساخته شده که همگی به اسکریپت shared.js وابستگی دارند و اگر این اسکریپت را از صفحات HTML حذف کنید، با خطا رو به رو خواهید شد. این پلاگین قدرت ساخت باندل‌هایی با خاصیت مشخص کردن وابستگی‌ها و همچنین تو در تویی‌ها خاص را نیز دارد. برای مطالعه‌ی بیشتر می‌توانید به اینجا مراجعه کنید: پلاگین commonsChunk

در قسمت بعدی با استفاده از وبپک فایل‌های css، فونت‌ها و تصاویر را نیز باندل خواهیم کرد.

فایل‌های مطلب:
سورس تا قبل از قسمت ایجاد تغییرات در ساختار فایل‌های پروژه :dntwebpack-part3-beforeFileAndFolderManagment.zip
سورس برای بعد از ایجاد تغییرات در ساختار فایل‌های پروژه : dntwebpack-part3AfterFileOrganization.zip

پروژه‌ها
برنامه IRIS PDF Editor
IRIS PDF Editor، برنامه ای تهیه شده با WPF هست. این برنامه در تکمیل سیستم مدیریت محتوای IRIS هست. یکی از موارد استفاده این سیستم برای من، قرار دادن فایل‌های PDF هست. فایل‌های PDF برای قرار گرفتن روی سایت، احتیاج به حذف لینک‌های سایت‌های دیگر و افزودن فوتر به فایل و همچنین تهیه‌ی عکس از کاور فایل ،داشتند.
این عمل تکراری عموما با نرم افزار‌های تجاری انجام می‌گرفت تا این که با توجه به نیاز‌های شخصی خود آن را نوشتم.
قابلیت‌های این نرم افزار:
- حذف متن دلخواه از فایل
- قرار دان متن دلخواه به عنوان فوتر
- تهیه‌ی عکس از صفحه‌ی اول فایل

این امکانات عمده‌ی نرم افزار هست. ویرایش فایل pdf به کمک کتابخانه‌ی iTextSharp و تهیه‌ی عکس از فایل‌های PDF، به کمک کتابخانه‌ی GhostryScript که به صورت Native هست، امکان پذیر شده است.
امکان تهیه‌ی عکس از فایل PDF این پروژه می‌تواند بسیار مفید باشد. در بعضی مواقع که هنگام اعمال گزارش گیری به فرم PDF، نرم افزار خاصی برای آن تدارک دیده نشده، می‌توان گزارش مورد نظر را به عکس تبدیل کرده و سپس آن را به کاربر نمایش داد.
این نرم افزار همچنین drag and drop چندین فایل را نیز پشتیبانی می‌کند.
کار اصلی تهیه‌ی wrapper برای GhostScript، توسط Richard Moss صورت گرفته، بنده نیز آن را کمی ویرایش و اصلاح و با کتابخانه‌ی iTextSharp ترکیب کردم.
برای راه اندازی پروژه از این مقاله کمک بگیرید. 
نظرات مطالب
آپلود فایل توسط فرم‌های پویای jqGrid
«Content type مقدار بازگشتی از متد UploadFiles حتما باید text/html باشد (افزونه‌ی ارسال فایل‌ها، اینگونه کار می‌کند).»
 return Json(new { FileName = product.ImageName }, "text/html", JsonRequestBehavior.AllowGet);
توسط همان فایرباگ بررسی کنید که چه خروجی از سمت سرور به سمت کاربر ارسال شده (در برگه‌ی response آدرس درخواستی)؟ چه فرمتی دارد؟ شامل چه اطلاعاتی است؟

نظرات مطالب
نمایش یک فایل PDF پویا در یک iframe در ASP.NET
به صورت توکار لحاظ شده:
 .Generate(data =>
                {
                    fileName = HttpUtility.UrlEncode(fileName, Encoding.UTF8);
                    data.FlushInBrowser(fileName, FlushType.Inline);
                }); // creating an in-memory PDF file
FlushType.Inline آن همان مطلب جاری است.