چگونه یک نمایشگر PDF جاوااسکریپت بسازیم؟

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 19 بهمن 1397
دسته بندی ها : جاوا اسکریپت

«قالب سند قابل حمل» (Portable Document Format) یا به اختصار PDF، برای به اشتراک گذاری اسنادی که شامل مقدار زیادی متن و تصاویر قالب‌بندی شده هستند، مناسب می‌باشد؛ به خصوص اگر این اسناد قرار است چاپ شده، یا این که به صورت آفلاین خوانده شوند. با این که اکثر مرورگرهای مدرن می‌توانند فایل‌های PDF را نمایش دهند، اما آن‌ها این کار را با استفاده از یک نمایشگر PDF که در یک تب یا پنجره مستقل اجرا می‌شود، انجام می‌دهند و کاربر را مجبور می‌کنند تا وبسایت شما را ترک کند.

PDF.js یک کتابخانه JavaScript متن باز است که شما را قادر می‌سازد تا فایل‌های PDF را مستقیما داخل صفحات وب خود parse کرده، و رندر کنید. در این آموزش، من نحوه استفاده از آن برای ساخت یک نمایشگر PDF‌ جاوااسکریپت را از ابتدا به شما توضیح خواهم داد.

۱. ساخت یک رابط کاربری

بیایید با ساخت یک صفحه جدید و اضافه کردن کد قالب HTML 5 معمولی شروع کنیم.

<!doctype html>
 
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My PDF Viewer</title>
</head>
<body>
     
</body>
</html>

سپس داخل تگ <body>، یک عنصر <div> بسازید که به عنوان یک محفظه برای نمایشگر PDF‌ ما عمل می‌کند.

<div id="my_pdf_viewer">
     
</div>

در قلب نمایشگر PDF جاوااسکریپت، یک عنصر <canvas> مربوط به HTML 5 قرار خواهد داشت. ما صفحات فایل‌های PDF خود را داخل آن رندر خواهیم کرد. پس این کد را به داخل عنصر <div> اضافه کنید:

<div id="canvas_container">

    <canvas id="pdf_renderer"></canvas>

</div>

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

<div id="navigation_controls">
    <button id="go_previous">Previous</button>
    <input id="current_page" value="1" type="number"/>
    <button id="go_next">Next</button>
</div>

برای پشتیبانی از عملیات‌های بزرگنمایی، دو دکمه دیگر به رابط مورد نظر اضافه کنید: یکی برای بزرگ‌تر کردن و یکی برای کوچک‌تر کردن.

<div id="zoom_controls">  
    <button id="zoom_in">+</button>
    <button id="zoom_out">-</button>
</div>

۲. دریافت PDF.js

حال که رابط کاربری نمایشگر PDF جاوااسکریپت ما آماده است، بیایید PDF.js را به صفحه وب خود اضافه کنیم. از آنجایی که آخرین نسخه کتابخانه مورد نظر بر روی CDNJS در دسترس است، ما می‌توانیم با اضافه کردن این خطوط در انتهای صفحه وب خود به سادگی این کار را انجام دهیم:

<script

    src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.min.js">

</script>

اگر استفاده از یک کپی محلی از کتابخانه را ترجیح می‌دهید، می‌توانید آن را از مخزن pdfjs-dist بر روی گیت‌هاب دانلود کنید.

۳. بارگذاری یک فایل PDF

قبل از این که شروع به بارگذاری یک فایل PDF نماییم، بیایید یک آبجکت JavaScript ساده بسازیم، تا state نمایشگر PDF خود را در آن ذخیره کنیم. ما داخل این آبجکت، سه آیتم خواهیم داشت: یک ارجاع به خود فایل PDF، شاخص صفحه فعلی، و سطح بزرگنمایی فعلی.

<script>
    var myState = {
        pdf: null,
        currentPage: 1,
        zoom: 1
    }
 
    // more code here
</script>

در اینجا ما می‌توانیم فایل PDF خود را با فراخوانی متد getDocument() مربوط به آبجکت pdfjsLib بارگذاری کنیم، که به صورت ناهمگام اجرا می‌شود.

pdfjsLib.getDocument('./my_document.pdf').then((pdf) => {
    // more code here
});

دقت کنید که متد getDocument() برای بارگذاری فایل PDF، به صورت داخلی از یک آبجکت XMLHttpRequest استفاده می‌کند. این یعنی فایل مورد نظر باید یا بر روی وب سرور شما، و یا بر روی یک سرور که درخواست‌های میان منبعی را ممکن می‌سازد، حاضر باشد.

اگر شما یک فایل PDF مناسب ندارید، می‌توانید فایلی که من استفاده می‌کنم را در این لینک بیابید.

پس از این که فایل PDF با موفقیت بارگذاری شد، می‌توانیم ویژگی pdf مربوط به آبجکت state خود را بروزرسانی کنیم.

myState.pdf = pdf;

در آخرهم بیایید یک فراخوانی به یک تابع به نام render() را اضافه کنیم، تا نمایشگر PDF ما به صورت خودکار اولین صفحه فایل PDF را رندر کند. ما تابع مورد نظر را در قدم بعدی تعریف خواهیم کرد.

render();

۴. رندر کردن یک صفحه

ما با فراخوانی متد getPage() مربوط به آبجکت pdf و منتقل کردن یک شماره صفحه به آن، یک ارجاع به هر صفحه‌ای داخل فایل PDF ایجاد کنیم. فعلا، بیایید ویژگی currentPage آبجکت state خود را به آن منتقل کنیم. این متد یک promise را بر می‌گرداند؛ پس ما به یک تابع callback‌ برای مدیریت نتیجه آن نیاز خواهیم داشت.

بر این اساس، یک تابع جدید به نام render() بسازید که شامل این کد باشد:

function render() {
    myState.pdf.getPage(myState.currentPage).then((page) => {
 
        // more code here
 
    });
}

برای این که یک صفحه را رندر کنیم، باید متد render() مربوط به آبجکت page که در callback‌ در دسترس است را فراخوانی کنیم. این متد انتظار یک زمینه دو بعدی از بوم ما، و یک آبجکت PageViewport را  به عنوان آرگومان دارد، که ما می‌توانیم مورد دوم را با استفاده از متد getViewport() به دست بیاوریم. از آنجایی که متد getViewport() انتظار سطح بزرگنمایی مطلوب را به عنوان آرگومان خود دارد، ما باید ویژگی zoom مربوط به آبجکت state خود را به آن منتقل کنیم.

var canvas = document.getElementById("pdf_renderer");

var ctx = canvas.getContext('2d');

var viewport = page.getViewport(myState.zoom);

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

canvas.width = viewport.width;

canvas.height = viewport.height;

در اینجا ما می‌توانیم ادامه داده، و صفحه را رندر کنیم.

page.render({
    canvasContext: ctx,
    viewport: viewport
});

اگر سعی کنید که صفحه را در یک مرورگر باز کنید، باید بتوانید اولین صفحه فایل PDF را ببینید.

شاید متوجه شده باشید که اندازه نمایشگر PDF ما در حال حاضر به اندازه صفحه‌ای که رندر می‌شود، و سطح بزرگنمایی بستگی دارد. از آنجایی که ما نمی‌خواهیم طرح صفحه وبمان در حالتی که کاربر با نمایشگر PDF در تعامل است،‌ تحت تاثیر قرار بگیر، این مسئله ایده‌آل نیست.

برای بر طرف کردن این مشکل، تنها کاری که باید انجام دهیم این است که یک عرض و ارتفاع ثابت به عنصر <div> که بوم ما را کپسوله‌سازی می‌کند بدهیم، و ویژگی overflow آن در CSS را برابر با auto قرار دهیم. این ویژگی در هنگام ضرورت، یک اسکرول بار به عنصر <div>‌ مورد نظر اضافه می‌کند، و کاربران را قادر می‌سازد تا هم به صورت افقی و هم به صورت عمودی اسکرول کنند.

این کد را داخل تگ <head> صفحه وب مورد نظر اضافه کنید:

<style>
    #canvas_container {
        width: 800px;
        height: 450px;
        overflow: auto;
    }
</style>

البته که شما می‌توانید آزادانه عرض و ارتفاع را تغییر داده، یا حتی از کوئری‌های رسانه‌ای استفاده کنید تا عنصر <div> را مجبور کنید که با نیازمندی‌های شما تطابق داشته باشد.

شما می‌توانید به صورت انتخابی این قوانین CSS را هم شامل کنید، تا عنصر <div> به صورت متمایزتر دیده شود:

#canvas_container {

    background: #333;

    text-align: center;

    border: solid 3px;

}

حال اگر صفحه وب را مجددا بارگذاری کنید، باید چیزی به مانند این تصویر را در صفحه خود ببینید:

۵. تغییر صفحه فعلی

در حال حاضر نمایشگر PDF جاوااسکریپت ما می‌تواند فقط صفحه اول هر فایل PDF‌ که به آن داده شده است را نمایش دهد. برای این که کاربران بتوانند صفحه‌ای که در حال رندر شدن است را تغییر دهند، ما باید listener رویداد کلیک را به دکمه‌های go_previous و go_next که پیش‌تر ساختیم، اضافه کنیم.

ما باید داخل listener‌ مربوط به دکمه go_previous، ویژگی currentPage آبجکت state خود را کاهش داده، و تضمین کنیم که پایین‌تر از ۱ نمی‌آید. پس از آن، ما می‌توانیم به سادگی تابع render() را دوباره فراخوانی کنیم، تا صفحه جدید را رندر کند.

به علاوه، ما باید مقدار فیلد متن current_page را بروزرسانی کنیم، تا شماره صفحه جدید را نمایش دهد. کد زیر نحوه انجام این کار را به ما نشان می‌دهد:

document.getElementById('go_previous')
        .addEventListener('click', (e) => {
            if(myState.pdf == null
               || myState.currentPage == 1) return;
            myState.currentPage -= 1;
            document.getElementById("current_page")
                    .value = myState.currentPage;
            render();
        });

به طور مشابه، ما باید در داخل listener مربوط به دکمه go_next مقدار ویژگی currentPage را افزایش دهیم، و تضمین کنیم که فراتر از شماره صفحه‌ای که در فایل PDF حاضر است نمی‌رود. این کار می‌تواند با استفاده از ویژگی numPages مربوط به آبجکت _pdfInfo تعیین شود.

document.getElementById('go_next')
        .addEventListener('click', (e) => {
            if(myState.pdf == null
               || myState.currentPage > myState.pdf
                                               ._pdfInfo.numPages) 
               return;
         
            myState.currentPage += 1;
            document.getElementById("current_page")
                    .value = myState.currentPage;
            render();
        });

در آخر، ما باید یک listener‌ رویداد فشردن دکمه به فیلد متن current_page اضافه کنیم، تا کاربران بتوانند به سادگی و با تایپ کردن شماره صفحه و فشردن کلید Enter، مستقیما به هر صفحه‌ای که می‌خواهند بروند. ما باید در داخل listener رویداد، مطمئن شویم شماره‌ای که کاربر وارد کرده است، هم بزرگ‌تر از صفر باشد و هم کمتر از، یا مساوی با ویژگی numPages.

document.getElementById('current_page')
        .addEventListener('keypress', (e) => {
            if(myState.pdf == null) return;
         
            // Get key code
            var code = (e.keyCode ? e.keyCode : e.which);
         
            // If key code matches that of the Enter key
            if(code == 13) {
                var desiredPage = 
                        document.getElementById('current_page')
                                .valueAsNumber;
                                 
                if(desiredPage >= 1 
                   && desiredPage <= myState.pdf
                                            ._pdfInfo.numPages) {
                        myState.currentPage = desiredPage;
                        document.getElementById("current_page")
                                .value = desiredPage;
                        render();
                }
            }
        });

حال نمایشگر PDF ما می‌‌تواند هر صفحه‌ای از فایل PDF مورد نظر را نشان دهد.

۶. تغییر سطح بزرگنمایی

از آنجایی که تابع render در هنگام رندر کردن صفحه، از پیش در حال استفاده از ویژگی zoom‌ مربوط به آبجکت state می‌باشد، تنظیم کردن سطح بزرگنمایی به سادگی افزایش یا کاهش ویژگی و فراخوانی تابع است.

بیایید در داخل listener رویداد کلیک دکمه zoom_in، ویژگی zoom را به مقدار 0.5 افزایش دهیم.

document.getElementById('zoom_in')
        .addEventListener('click', (e) => {
            if(myState.pdf == null) return;
            myState.zoom += 0.5;
            render();
        });

و بیایید در داخل listener رویداد کلیک مربوط به دکمه zoom_out، ویژگی zoom را به مقدار 0.5 کاهش دهیم.

document.getElementById('zoom_out')
        .addEventListener('click', (e) => {
            if(myState.pdf == null) return;
            myState.zoom -= 0.5;
            render();
        });

شما می‌توانید مرزهای بالا و پایین را هم به ویژگی zoom اضافه کنید، اما این کار ضروری نیست.

نمایشگر PDF ما آماده است. اگر باز هم صفحه وب را مجددا بارگذاری کنید، باید بتوانید تمام صفحات حاضر در فایل PDF‌ را ببینید و همچنین به سمت داخل و خارج بزرگنمایی کنید.

نتیجه گیری

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

منبع

مقالات پیشنهادی

دکتر استارتاپ (مهدی علیپور) چگونه کار می کند

این مطلب به درخواست وبسایت راکت تهیه شده ، از من خواسته شده تا در مورد روتین های روزمره ، سبک کاری ، فضای کارم و ابزارهایی که استفاده میکنم براتون توض...

خالق لاراول چگونه کار می‌کند؟

اخیرا افرادی را دیدم که وضعیت / روند کاری خود را به اشتراک می‌گذارند. پس من هم تصمیم گرفتم که همین کار را انجام دهم.

چگونه در JavaScript، یک افزونه ساده برای کروم ‌ بسازیم؟

امروز، نحوه ساخت افزونه کروم را در Vanilla JavaScript به شما نشان خواهم داد. Vanilla JavaScript در واقع JavaScript خالص، بدون هیچ‌گونه فریم‌وورک اضافی...

ساخت کامل اپلیکیشن های جاوااسکریپتی به راحتی با CxJS

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