mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Rendering PDFs using PDF.js (client portal) (#3589)
* Wip for testing * Rendering PDFs using pdf.js
This commit is contained in:
parent
b162fde79e
commit
a87ae37ce1
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/js/clients/shared/pdf.js
vendored
Normal file
2
public/js/clients/shared/pdf.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
public/js/clients/shared/pdf.js.LICENSE.txt
Normal file
9
public/js/clients/shared/pdf.js.LICENSE.txt
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
22
public/js/vendor/pdf.js/pdf.min.js
vendored
Normal file
22
public/js/vendor/pdf.js/pdf.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
22
public/js/vendor/pdf.js/pdf.worker.min.js
vendored
Normal file
22
public/js/vendor/pdf.js/pdf.worker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,11 +1,12 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=8b49701583f407403ddf",
|
||||
"/css/app.css": "/css/app.css?id=0cc15fdcb4d64dfeda0e",
|
||||
"/css/app.css": "/css/app.css?id=422c8d1b7e15aa054cd7",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=caec43815d9a13168a38",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=af49e24958be5fc00c92",
|
||||
"/js/clients/payment_methods/authorize-stripe-card.js": "/js/clients/payment_methods/authorize-stripe-card.js?id=f4c45f0da9868d840799",
|
||||
"/js/clients/payments/process.js": "/js/clients/payments/process.js?id=bb91cb611d1bdab40973",
|
||||
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=f7c6bfdbf9cfc3efdf0b",
|
||||
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=fcb23f15eebc3bff639d",
|
||||
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=5e05bbd189e1f70e0c27",
|
||||
"/js/setup/setup.js": "/js/setup/setup.js?id=d30808ba9e2c9f908ce5"
|
||||
}
|
||||
|
78
resources/js/clients/shared/pdf.js
vendored
Normal file
78
resources/js/clients/shared/pdf.js
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
class PDF {
|
||||
constructor(url, canvas) {
|
||||
this.url = url;
|
||||
this.canvas = canvas;
|
||||
this.context = canvas.getContext("2d");
|
||||
this.currentPage = 1;
|
||||
this.maxPages = 1;
|
||||
}
|
||||
|
||||
handlePreviousPage() {
|
||||
if (this.currentPage == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentPage -= 1;
|
||||
|
||||
this.handle();
|
||||
}
|
||||
|
||||
handleNextPage() {
|
||||
if (this.currentPage == this.maxPages) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentPage += 1;
|
||||
|
||||
this.handle();
|
||||
}
|
||||
|
||||
prepare() {
|
||||
let previousPageButton = document.getElementById(
|
||||
"previous-page-button"
|
||||
);
|
||||
|
||||
let nextPageButton = document.getElementById("next-page-button");
|
||||
|
||||
previousPageButton.addEventListener("click", () =>
|
||||
this.handlePreviousPage()
|
||||
);
|
||||
|
||||
nextPageButton.addEventListener("click", () => this.handleNextPage());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async handle() {
|
||||
let pdf = await pdfjsLib.getDocument(this.url).promise;
|
||||
|
||||
let page = await pdf.getPage(this.currentPage);
|
||||
|
||||
this.maxPages = pdf.numPages;
|
||||
|
||||
let viewport = await page.getViewport({ scale: 1 });
|
||||
|
||||
this.canvas.height = viewport.height;
|
||||
this.canvas.width = viewport.width;
|
||||
|
||||
page.render({
|
||||
canvasContext: this.context,
|
||||
viewport
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const url = document.querySelector("meta[name='pdf-url'").content;
|
||||
const canvas = document.getElementById("pdf-placeholder");
|
||||
|
||||
new PDF(url, canvas).prepare().handle();
|
@ -3191,4 +3191,6 @@ return [
|
||||
'minumum_php_version' => 'Minimum PHP version',
|
||||
'satisfy_requirements' => 'Make sure all requirements are satisfied.',
|
||||
'oops_issues' => 'Oops, something doesn\'t look right!',
|
||||
|
||||
'open_in_new_tab' => 'Open in new tab',
|
||||
];
|
||||
|
@ -1,6 +1,11 @@
|
||||
@extends('portal.ninja2020.layout.app')
|
||||
@section('meta_title', ctrans('texts.view_invoice'))
|
||||
|
||||
@push('head')
|
||||
<meta name="pdf-url" content="{{ asset($invoice->pdf_url()) }}">
|
||||
<script src="{{ asset('js/vendor/pdf.js/pdf.min.js') }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('header')
|
||||
{{ Breadcrumbs::render('invoices.show', $invoice) }}
|
||||
@endsection
|
||||
@ -37,7 +42,43 @@
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<embed src="{{ asset($invoice->pdf_url()) }}#toolbar=1&navpanes=1&scrollbar=1" type="application/pdf" width="100%"
|
||||
height="1180px"/>
|
||||
<div class="flex items-center justify-between">
|
||||
<section class="flex items-center">
|
||||
<button class="input-label" id="previous-page-button">
|
||||
<svg class="w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="input-label" id="next-page-button">
|
||||
<svg class="w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</button>
|
||||
</section>
|
||||
<div x-data="{ open: false }" @keydown.escape="open = false" @click.away="open = false" class="relative inline-block text-left">
|
||||
<div>
|
||||
<button @click="open = !open" class="flex items-center text-gray-400 hover:text-gray-600 focus:outline-none focus:text-gray-600">
|
||||
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="open" x-transition:enter="transition ease-out duration-100" x-transition:enter-start="transform opacity-0 scale-95" x-transition:enter-end="transform opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95" class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg">
|
||||
<div class="rounded-md bg-white shadow-xs">
|
||||
<div class="py-1">
|
||||
<a target="_blank" href="{{ asset($invoice->pdf_url()) }}" class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900">{{ ctrans('texts.open_in_new_tab') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<canvas id="pdf-placeholder" class="shadow rounded-lg bg-white mt-4 p-4"></canvas>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('footer')
|
||||
<script src="{{ asset('js/clients/shared/pdf.js') }}"></script>
|
||||
@endsection
|
||||
|
@ -1,6 +1,11 @@
|
||||
@extends('portal.ninja2020.layout.app')
|
||||
@section('meta_title', ctrans('texts.view_quote'))
|
||||
|
||||
@push('head')
|
||||
<meta name="pdf-url" content="{{ asset($quote->pdf_file_path()) }}">
|
||||
<script src="{{ asset('js/vendor/pdf.js/pdf.min.js') }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('header')
|
||||
{{ Breadcrumbs::render('quotes.show', $quote) }}
|
||||
@endsection
|
||||
@ -37,8 +42,43 @@
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<embed src="{{ asset($quote->pdf_file_path()) }}#toolbar=1&navpanes=1&scrollbar=1" type="application/pdf"
|
||||
width="100%"
|
||||
height="1180px"/>
|
||||
<div class="flex items-center justify-between">
|
||||
<section class="flex items-center">
|
||||
<button class="input-label" id="previous-page-button">
|
||||
<svg class="w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="input-label" id="next-page-button">
|
||||
<svg class="w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</button>
|
||||
</section>
|
||||
<div x-data="{ open: false }" @keydown.escape="open = false" @click.away="open = false" class="relative inline-block text-left">
|
||||
<div>
|
||||
<button @click="open = !open" class="flex items-center text-gray-400 hover:text-gray-600 focus:outline-none focus:text-gray-600">
|
||||
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="open" x-transition:enter="transition ease-out duration-100" x-transition:enter-start="transform opacity-0 scale-95" x-transition:enter-end="transform opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95" class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg">
|
||||
<div class="rounded-md bg-white shadow-xs">
|
||||
<div class="py-1">
|
||||
<a target="_blank" href="{{ asset($invoice->pdf_url()) }}" class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900">{{ ctrans('texts.open_in_new_tab') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<canvas id="pdf-placeholder" class="shadow rounded-lg bg-white mt-4 p-4"></canvas>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('footer')
|
||||
<script src="{{ asset('js/clients/shared/pdf.js') }}"></script>
|
||||
@endsection
|
||||
|
6
webpack.mix.js
vendored
6
webpack.mix.js
vendored
@ -31,6 +31,10 @@ mix.js("resources/js/app.js", "public/js")
|
||||
.js(
|
||||
"resources/js/setup/setup.js",
|
||||
"public/js/setup/setup.js"
|
||||
)
|
||||
.js(
|
||||
"resources/js/clients/shared/pdf.js",
|
||||
"public/js/clients/shared/pdf.js"
|
||||
);
|
||||
|
||||
mix.sass("resources/sass/app.scss", "public/css")
|
||||
@ -44,4 +48,4 @@ mix.sass("resources/sass/app.scss", "public/css")
|
||||
});
|
||||
|
||||
mix.version();
|
||||
mix.disableNotifications();
|
||||
mix.disableNotifications();
|
||||
|
Loading…
x
Reference in New Issue
Block a user