diff --git a/src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj b/src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj index fd5f4897..d7c1e685 100644 --- a/src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj +++ b/src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj @@ -1,7 +1,7 @@  - 10.0.6 + 10.0.7 diff --git a/src/components/BootstrapBlazor.PdfReader/Locales/en.json b/src/components/BootstrapBlazor.PdfReader/Locales/en.json index 2208ad32..da66418c 100644 --- a/src/components/BootstrapBlazor.PdfReader/Locales/en.json +++ b/src/components/BootstrapBlazor.PdfReader/Locales/en.json @@ -12,6 +12,7 @@ "Print": "Print", "TwoPageView": "Two pages on view", "PresentationMode": "Presentation mode", - "DocumentProperty": "Document properties" + "DocumentProperty": "Document properties", + "CloseButtonText": "Close" } } diff --git a/src/components/BootstrapBlazor.PdfReader/Locales/zh.json b/src/components/BootstrapBlazor.PdfReader/Locales/zh.json index bb62b23f..d2b90f80 100644 --- a/src/components/BootstrapBlazor.PdfReader/Locales/zh.json +++ b/src/components/BootstrapBlazor.PdfReader/Locales/zh.json @@ -12,6 +12,7 @@ "Print": "打印", "TwoPageView": "双页视图", "PresentationMode": "演示模式", - "DocumentProperty": "文档属性" + "DocumentProperty": "文档属性", + "CloseButtonText": "关闭" } } diff --git a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor index 6de2cc4b..268b3657 100644 --- a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor +++ b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor @@ -74,7 +74,9 @@ } @if (ShowTwoPagesOneView || ShowPresentationMode) { - +
+
+
} +
+
+
+
Document properties
+
+
File name:
+
+
+
+
File size:
+
+
+
+
+
+
+
Title:
+
+
+
+
Author:
+
+
+
+
Subject:
+
+
+
+
Keywords:
+
+
+
+
Created:
+
+
+
+
Modified:
+
+
+
+
Application:
+
+
+
+
+
+
+
PDF producer:
+
+
+
+
PDF version:
+
+
+
+
Page count:
+
+
+
+
Page size:
+
+
+
+
+
+
+
Fast web view:
+
No
+
+
+ +
+
+
diff --git a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.css b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.css index 7a9b3cd0..ff2479c8 100644 --- a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.css +++ b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.css @@ -209,3 +209,56 @@ background-color: #000; inset: 0; } + +.bb-view-pdf-info { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 5; +} + + .bb-view-pdf-info:not(.show) { + display: none; + } + +.bb-view-pdf-backdrop { + position: absolute; + inset: 0; + background-color: #000; + opacity: 0.6; + z-index: 1; +} + +.bb-view-pdf-dialog { + background-color: #fff; + padding: 1rem; + z-index: 5; + border-radius: var(--bs-border-radius); +} + +.bb-view-pdf-dialog-item { + display: flex; + flex-wrap: nowrap; + width: 344px; +} + + .bb-view-pdf-dialog-item:not(:last-child) { + margin-block-start: .5rem; + } + +.bb-view-pdf-dialog-label { + width: 108px; + white-space: nowrap; + overflow: hidden; +} + +.bb-view-pdf-dialog-close { + text-align: right; + padding-block-start: 1rem; +} + + .bb-view-pdf-dialog-close .btn { + --bs-btn-padding-x: 1rem; + } diff --git a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js index 66951070..5af82679 100644 --- a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js +++ b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js @@ -5,7 +5,7 @@ import Data from '../BootstrapBlazor/modules/data.js'; import EventHandler from '../BootstrapBlazor/modules/event-handler.js'; if (pdfjsLib != null) { - pdfjsLib.GlobalWorkerOptions.workerSrc = './pdf.worker.min.mjs'; + pdfjsLib.GlobalWorkerOptions.workerSrc = '/_content/BootstrapBlazor.PdfReader/lib/pdf.worker.min.mjs'; } export async function init(id, invoke, options) { @@ -101,9 +101,120 @@ const loadPdf = async (el, invoke, options) => { const pdfDocument = await loadingTask.promise; pdfViewer.setDocument(pdfDocument); + pdfDocument.getMetadata().then(metadata => { + loadMetadata(el, pdfViewer, metadata); + }); + return pdfViewer; } +const loadMetadata = (el, pdfViewer, metadata) => { + console.log(metadata); + + const filename = el.querySelector('.bb-view-pdf-dialog-filename'); + const docTitle = el.querySelector('.bb-view-subject'); + filename.textContent = docTitle.textContent; + + const filesize = el.querySelector('.bb-view-pdf-dialog-filesize'); + filesize.textContent = getFilesize(metadata); + + const title = el.querySelector('.bb-view-pdf-dialog-title'); + const author = el.querySelector('.bb-view-pdf-dialog-author'); + const subject = el.querySelector('.bb-view-pdf-dialog-subject'); + const keywords = el.querySelector('.bb-view-pdf-dialog-keywords'); + const created = el.querySelector('.bb-view-pdf-dialog-created'); + created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString(); + + const modified = el.querySelector('.bb-view-pdf-dialog-modified'); + + const application = el.querySelector('.bb-view-pdf-dialog-application'); + application.textContent = metadata.info.Creator; + + const producer = el.querySelector('.bb-view-pdf-dialog-producer'); + producer.textContent = metadata.info.Producer; + + const version = el.querySelector('.bb-view-pdf-dialog-version'); + version.textContent = metadata.info.PDFFormatVersion; + + const count = el.querySelector('.bb-view-pdf-dialog-count'); + count.textContent = pdfViewer.pagesCount; + + const size = el.querySelector('.bb-view-pdf-dialog-size'); + pdfViewer.pdfDocument.getPage(pdfViewer.currentPageNumber).then(page => { + const viewport = page.getViewport({ scale: 1 }); + size.textContent = `${(viewport.width / 72).toFixed(2)} * ${(viewport.height / 72).toFixed(2)} in (portrait)`; + }); + + const webview = el.querySelector('.bb-view-pdf-dialog-webview'); +} + +function parsePdfDate(pdfDateString) { + if (!pdfDateString || typeof pdfDateString !== 'string') { + return null; + } + + let dateStr = pdfDateString.startsWith('D:') ? pdfDateString.substring(2) : pdfDateString; + + const pdfDateRegex = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})([Zz+-])(\d{2})'?(\d{2})'?$/; + const match = dateStr.match(pdfDateRegex); + + if (!match) { + return null; + } + + const [, year, month, day, hours, minutes, seconds, timezoneSign, timezoneHours, timezoneMinutes] = match; + + const date = new Date( + parseInt(year), + parseInt(month) - 1, + parseInt(day), + parseInt(hours), + parseInt(minutes), + parseInt(seconds) + ); + + if (timezoneSign === 'Z' || timezoneSign === 'z') { + const utcTime = Date.UTC( + parseInt(year), + parseInt(month) - 1, + parseInt(day), + parseInt(hours), + parseInt(minutes), + parseInt(seconds) + ); + date.setTime(utcTime); + } + else if (timezoneSign === '+' || timezoneSign === '-') { + const offsetHours = parseInt(timezoneHours); + const offsetMinutes = parseInt(timezoneMinutes || 0); + const totalOffsetMinutes = offsetHours * 60 + offsetMinutes; + + if (timezoneSign === '+') { + date.setMinutes(date.getMinutes() - totalOffsetMinutes); + } + else { + date.setMinutes(date.getMinutes() + totalOffsetMinutes); + } + } + return date; +} + +const getFilesize = metadata => { + const length = metadata.contentLength; + if (length < 1024) { + return `${Math.round(length)}B`; + } + else if (length < 1024 * 1024) { + return `${Math.round(length / 1024)}KB`; + } + else if (length < 1024 * 1024 * 1024) { + return `${length / 1024 / 1024}MB`; + } + else if (length < 1024 * 1024 * 1024 * 1024) { + return `${length / 1024 / 1024 / 1024}GB`; + } +} + const setObserver = el => { const observer = new ResizeObserver(entries => { relayoutToolbar(el); @@ -325,6 +436,20 @@ const addToolbarEventHandlers = (el, pdfViewer, invoke, options) => { // el.requestFullscreen(); //} }); + EventHandler.on(toolbar, "click", ".dropdown-item-doc", e => { + const dialog = el.querySelector(".bb-view-pdf-info"); + if (dialog) { + dialog.classList.add("show"); + } + }); + + const closeButton = el.querySelector(".btn-close-doc"); + EventHandler.on(closeButton, 'click', e => { + const dialog = el.querySelector(".bb-view-pdf-info"); + if (dialog) { + dialog.classList.remove("show"); + } + }); } const resetToolbarView = (el, pdfViewer) => { @@ -402,7 +527,7 @@ const updateScaleValue = (el, value) => { const scaleEl = el.querySelector(".bb-view-scale-input"); const scale = value * 100; - scaleEl.value = `${Math.round(scale, 0)}%`; + scaleEl.value = `${Math.round(scale)}%`; if (scale === 25) { minus.classList.add("disabled"); @@ -422,7 +547,7 @@ const updateScale = (pdfViewer, button, rate) => { } const scale = pdfViewer.currentScale; - const current = Math.round(parseFloat(scale * 100), 0); + const current = Math.round(parseFloat(scale * 100)); const step = [25, 33, 50, 67, 75, 80, 90, 100, 110, 125, 150, 175, 200, 250, 300, 400, 500]; const findValues = step.filter(s => rate > 0 ? current < s : current > s); if (findValues.length === 0) { @@ -462,7 +587,7 @@ const printPdf = url => { } iframe = document.createElement("iframe"); - iframe.classList = "bb-view-print-iframe"; + iframe.classList.add("bb-view-print-iframe"); iframe.style.position = "fixed"; iframe.style.right = "100%"; iframe.style.bottom = "100%"; @@ -506,5 +631,10 @@ export function dispose(id) { if (iframe) { iframe.remove(); } + + const closeButton = el.querySelector(".btn-close-doc"); + if (closeButton) { + EventHandler.off(closeButton, "click"); + } } }