feat: implement PDF handling to prepare for in-IDE statement viewing

This commit is contained in:
Alexander Klee 2024-05-15 20:10:22 +02:00
parent 217979e9d7
commit 09355188b1
4 changed files with 191 additions and 0 deletions

View File

@ -0,0 +1,68 @@
package io.gitlab.jfronny.sdom.ui
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.fileEditor.FileEditorLocation
import com.intellij.openapi.fileEditor.FileEditorState
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.UserDataHolderBase
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.jcef.JBCefBrowser
import com.intellij.ui.jcef.JBCefBrowserBuilder
import com.intellij.ui.jcef.JBCefClient
import io.gitlab.jfronny.sdom.util.pdfBootstrap
import org.jetbrains.annotations.Nls
import java.beans.PropertyChangeListener
import javax.swing.JComponent
class SDBrowserViewer(private val file: VirtualFile, cefClient: JBCefClient) : UserDataHolderBase(), FileEditor {
private val browser: JBCefBrowser = JBCefBrowserBuilder().setClient(cefClient)
.setEnableOpenDevToolsMenuItem(false)
.build()
init {
browser.loadHTML(pdfBootstrap(file.contentsToByteArray()))
}
private val component = browser.component
override fun getComponent(): JComponent {
return component
}
override fun getPreferredFocusedComponent(): JComponent? {
return component
}
override fun getName(): @Nls(capitalization = Nls.Capitalization.Title) String {
return "S-dom Integrated Statement Viewer"
}
override fun setState(fileEditorState: FileEditorState) {
}
override fun isModified(): Boolean {
return false
}
override fun isValid(): Boolean {
return file.isValid
}
override fun addPropertyChangeListener(propertyChangeListener: PropertyChangeListener) {
}
override fun removePropertyChangeListener(propertyChangeListener: PropertyChangeListener) {
}
override fun getCurrentLocation(): FileEditorLocation? {
return null
}
override fun dispose() {
Disposer.dispose(this)
}
override fun getFile(): VirtualFile? {
return this.file
}
}

View File

@ -0,0 +1,41 @@
package io.gitlab.jfronny.sdom.ui
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.fileEditor.FileEditorPolicy
import com.intellij.openapi.fileEditor.FileEditorProvider
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.LightVirtualFileBase
import com.intellij.ui.jcef.JBCefApp
import org.jetbrains.annotations.NonNls
class SDBrowserViewerProvider : FileEditorProvider, DumbAware {
private val ourCefClient = JBCefApp.getInstance().createClient()
init {
Disposer.register(ApplicationManager.getApplication(), ourCefClient)
}
override fun accept(project: Project, virtualFile: VirtualFile): Boolean {
return virtualFile is LightVirtualFileBase && virtualFile.extension == "pdf"
}
override fun createEditor(project: Project, virtualFile: VirtualFile): FileEditor {
return SDBrowserViewer(virtualFile, ourCefClient)
}
override fun getEditorTypeId(): @NonNls String {
return EDITOR_TYPE_ID
}
override fun getPolicy(): FileEditorPolicy {
return FileEditorPolicy.HIDE_DEFAULT_EDITOR
}
companion object {
private const val EDITOR_TYPE_ID = "SdomBrowserViewer"
}
}

View File

@ -0,0 +1,81 @@
package io.gitlab.jfronny.sdom.util
import com.intellij.ui.jcef.JBCefScrollbarsHelper
import org.intellij.lang.annotations.Language
import java.util.Base64
@Language("HTML")
fun pdfBootstrap(pdf: ByteArray) = """
<!DOCTYPE html>
<html>
<body>
<script src="https://mozilla.github.io/pdf.js/build/pdf.mjs" type="module"></script>
<link rel='stylesheet' type='text/css' href='https://mozilla.github.io/pdf.js/web/viewer.css'>
<style type='text/css'>
${JBCefScrollbarsHelper.getOverlayScrollbarStyle()}
</style>
<script type="module">
const pdfData = atob('${Base64.getEncoder().encodeToString(pdf)}');
// Loaded via <script> tag, create shortcut to access PDF.js exports.
const { pdfjsLib } = globalThis;
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://mozilla.github.io/pdf.js/build/pdf.worker.mjs';
// Using DocumentInitParameters object to load binary data.
const loadingTask = pdfjsLib.getDocument({data: pdfData});
loadingTask.promise.then(function(pdf) {
console.log('PDF loaded');
const body = document.getElementById('our-body');
for (let i = 1; i <= pdf.numPages; i++) {
pdf.getPage(i).then(function(page) {
console.log('Page loaded');
const scale = 1.5;
const viewport = page.getViewport({scale: scale});
// Prepare canvas using PDF page dimensions
const canvas = document.createElement('canvas');
const textLayer = document.createElement('div');
textLayer.className = "textLayer"
body.appendChild(canvas)
body.appendChild(textLayer)
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
const renderContext = {
canvasContext: context,
viewport: viewport
};
const renderTask = page.render(renderContext);
renderTask.promise.then(function () {
return page.getTextContent()
}).then(function (textContent) {
pdfjsLib.renderTextLayer({
textContentSource: textContent,
container: textLayer,
viewport: viewport,
textDivs: []
});
textLayer.style.left = canvas.offsetLeft + 'px';
textLayer.style.top = canvas.offsetTop + 'px';
textLayer.style.height = canvas.offsetHeight + 'px';
textLayer.style.width = canvas.offsetWidth + 'px';
});
});
}
}, function (reason) {
// PDF loading error
console.error(reason);
});
</script>
<div id="our-body" style="width: 100%" />
</body>
</html>
""".trimIndent()

View File

@ -25,6 +25,7 @@
<!-- Extension points defined by the plugin.
Read more: https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html -->
<extensions defaultExtensionNs="com.intellij">
<fileEditorProvider implementation="io.gitlab.jfronny.sdom.ui.SDBrowserViewerProvider"/>
<toolWindow factoryClass="io.gitlab.jfronny.sdom.toolwindow.SDToolWindowFactory"
id="S-DOM" anchor="bottom" canCloseContents="true" icon="io.gitlab.jfronny.sdom.icons.SDIcons.ToolWindow" />
<notificationGroup id="sdom.notifications" displayType="BALLOON" />