libpdfviewer: update to PdfViewer 19
This commit is contained in:
commit
7e01d2b224
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,3 +6,5 @@ keystore.properties
|
|||||||
*.keystore
|
*.keystore
|
||||||
/.idea
|
/.idea
|
||||||
/releases
|
/releases
|
||||||
|
/node_modules
|
||||||
|
/app/src/*/assets/viewer
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright © 2017-2023 GrapheneOS
|
Copyright © 2017-2024 GrapheneOS
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
|
||||||
@ -38,14 +39,13 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileSdk = 33
|
compileSdk = 34
|
||||||
buildToolsVersion = "33.0.2"
|
buildToolsVersion = "34.0.0"
|
||||||
|
|
||||||
namespace = "app.grapheneos.pdfviewer"
|
namespace = "app.grapheneos.pdfviewer"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 33
|
|
||||||
resourceConfigurations.add("en")
|
resourceConfigurations.add("en")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,18 +70,37 @@ android {
|
|||||||
buildConfig = true
|
buildConfig = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility(JavaVersion.VERSION_17)
|
|
||||||
targetCompatibility(JavaVersion.VERSION_17)
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = JavaVersion.VERSION_17.toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
implementation("com.google.android.material:material:1.9.0")
|
implementation("androidx.core:core:1.13.1")
|
||||||
|
implementation("com.google.android.material:material:1.12.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCommand(command: String, winExt: String = "cmd"): String {
|
||||||
|
return if (Os.isFamily(Os.FAMILY_WINDOWS)) "$command.$winExt" else command
|
||||||
|
}
|
||||||
|
|
||||||
|
val npmSetup = tasks.register("npmSetup", Exec::class) {
|
||||||
|
workingDir = projectDir
|
||||||
|
commandLine(getCommand("npm"), "ci", "--ignore-scripts")
|
||||||
|
}
|
||||||
|
|
||||||
|
val processStatic = tasks.register("processStatic", Exec::class) {
|
||||||
|
workingDir = projectDir.parentFile
|
||||||
|
dependsOn(npmSetup)
|
||||||
|
commandLine(getCommand("node", "exe"), "process_static.js")
|
||||||
|
}
|
||||||
|
|
||||||
|
val cleanStatic = tasks.register("cleanStatic", Delete::class) {
|
||||||
|
delete("src/main/assets/viewer", "src/debug/assets/viewer")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.preBuild {
|
||||||
|
dependsOn(processStatic)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.clean {
|
||||||
|
dependsOn(cleanStatic)
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
../../../pdfjs-dist/build/pdf.min.js
|
|
1
app/src/main/assets/pdf.worker.js
vendored
1
app/src/main/assets/pdf.worker.js
vendored
@ -1 +0,0 @@
|
|||||||
../../../pdfjs-dist/build/pdf.worker.min.js
|
|
@ -268,7 +268,7 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
|
|||||||
return new WebResourceResponse("application/pdf", null, mInputStream);
|
return new WebResourceResponse("application/pdf", null, mInputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/viewer.html".equals(path)) {
|
if ("/viewer/index.html".equals(path)) {
|
||||||
final WebResourceResponse response = fromAsset("text/html", path);
|
final WebResourceResponse response = fromAsset("text/html", path);
|
||||||
HashMap<String, String> headers = new HashMap<>();
|
HashMap<String, String> headers = new HashMap<>();
|
||||||
headers.put("Content-Security-Policy", CONTENT_SECURITY_POLICY);
|
headers.put("Content-Security-Policy", CONTENT_SECURITY_POLICY);
|
||||||
@ -278,11 +278,11 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/viewer.css".equals(path)) {
|
if ("/viewer/main.css".equals(path)) {
|
||||||
return fromAsset("text/css", path);
|
return fromAsset("text/css", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/viewer.js".equals(path) || "/pdf.js".equals(path) || "/pdf.worker.js".equals(path)) {
|
if ("/viewer/js/index.js".equals(path) || "/viewer/js/worker.js".equals(path)) {
|
||||||
return fromAsset("application/javascript", path);
|
return fromAsset("application/javascript", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,7 +428,7 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
|
|||||||
this.fileSize = fileSize;
|
this.fileSize = fileSize;
|
||||||
showSystemUi();
|
showSystemUi();
|
||||||
activity.invalidateOptionsMenu();
|
activity.invalidateOptionsMenu();
|
||||||
binding.webview.loadUrl("https://localhost/viewer.html");
|
binding.webview.loadUrl("https://localhost/viewer/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadPdfWithPassword(final String password) {
|
public void loadPdfWithPassword(final String password) {
|
||||||
|
@ -155,11 +155,11 @@ public class Utils {
|
|||||||
throw new ParseException("Invalid UTC offset minutes", position);
|
throw new ParseException("Invalid UTC offset minutes", position);
|
||||||
}
|
}
|
||||||
position += 2;
|
position += 2;
|
||||||
}
|
|
||||||
|
|
||||||
// Apostrophe shall succeed mm
|
// Apostrophe shall succeed mm
|
||||||
if (date.charAt(position) != '\'') {
|
if (date.charAt(position) != '\'') {
|
||||||
throw new ParseException("Expected apostrophe", position);
|
throw new ParseException("Expected apostrophe", position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") apply false
|
id("com.android.application") version "8.4.0" apply false
|
||||||
id("org.jetbrains.kotlin.android") apply false
|
id("org.jetbrains.kotlin.android") version "1.9.24" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@ -12,5 +12,5 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("clean", Delete::class) {
|
tasks.register("clean", Delete::class) {
|
||||||
delete(rootProject.buildDir)
|
delete(rootProject.layout.buildDirectory)
|
||||||
}
|
}
|
||||||
|
33
eslint.config.js
Normal file
33
eslint.config.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
channel: "readonly",
|
||||||
|
...globals.browser
|
||||||
|
},
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: "module"
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
indent: ["error", 4],
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
quotes: ["error", "double"],
|
||||||
|
semi: ["error", "always"],
|
||||||
|
"no-unused-vars": ["error", {caughtErrors: "none"}],
|
||||||
|
"no-var": ["error"]
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"app/build/",
|
||||||
|
"app/src/*/assets/viewer",
|
||||||
|
"build/",
|
||||||
|
"releases/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
File diff suppressed because it is too large
Load Diff
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=38f66cd6eef217b4c35855bb11ea4e9fbc53594ccccb5fb82dfd317ef8c2c5a3
|
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
17
gradlew
vendored
17
gradlew
vendored
@ -83,7 +83,8 @@ done
|
|||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@ -201,11 +202,11 @@ fi
|
|||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command:
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# and any embedded shellness will be escaped.
|
||||||
# double quotes to make sure that they get re-expanded; and
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
2115
package-lock.json
generated
Normal file
2115
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
8
package.json
Normal file
8
package.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"type": "module",
|
||||||
|
"devDependencies": {
|
||||||
|
"esbuild": "^0.21.1",
|
||||||
|
"eslint": "^9.2.0",
|
||||||
|
"pdfjs-dist": "^4.2.67"
|
||||||
|
}
|
||||||
|
}
|
107
process_static.js
Normal file
107
process_static.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import esbuild from "esbuild";
|
||||||
|
import { spawn } from "node:child_process";
|
||||||
|
import fs from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef ProcessOptions
|
||||||
|
* @property {string} rootDir
|
||||||
|
* @property {string[]} entryPoints
|
||||||
|
* @property {string} outDir
|
||||||
|
* @property {boolean} production
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function processStatic() {
|
||||||
|
const rootDir = "viewer";
|
||||||
|
const outDir = "app/src/main/assets/viewer";
|
||||||
|
const outDirDebug = "app/src/debug/assets/viewer";
|
||||||
|
|
||||||
|
await commandLine(getCommand("node_modules/.bin/eslint"), ".");
|
||||||
|
|
||||||
|
await processScripts({
|
||||||
|
rootDir,
|
||||||
|
entryPoints: ["js/index.js", "js/worker.js"],
|
||||||
|
outDir,
|
||||||
|
production: true,
|
||||||
|
});
|
||||||
|
await processScripts({
|
||||||
|
rootDir,
|
||||||
|
entryPoints: ["js/index.js", "js/worker.js"],
|
||||||
|
outDir: outDirDebug,
|
||||||
|
production: false,
|
||||||
|
});
|
||||||
|
await processStyles({
|
||||||
|
rootDir,
|
||||||
|
entryPoints: ["main.css"],
|
||||||
|
outDir,
|
||||||
|
production: true,
|
||||||
|
});
|
||||||
|
await processHtml({
|
||||||
|
rootDir,
|
||||||
|
entryPoints: ["index.html"],
|
||||||
|
outDir,
|
||||||
|
production: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} command
|
||||||
|
* @param {string} winExt
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getCommand(command, winExt = "cmd") {
|
||||||
|
return path.resolve(globalThis.process.platform === "win32" ? `${command}.${winExt}` : command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} command
|
||||||
|
* @param {...string} args
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
function commandLine(command, ...args) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const subprocess = spawn(command, args, { shell: false, stdio: "inherit" });
|
||||||
|
subprocess.on("close", (code) => code === 0 ? resolve() : reject());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ProcessOptions} options
|
||||||
|
*/
|
||||||
|
async function processScripts(options) {
|
||||||
|
const entryPoints = options.entryPoints.map((filepath) => path.join(options.rootDir, filepath));
|
||||||
|
await esbuild.build({
|
||||||
|
entryPoints,
|
||||||
|
bundle: true,
|
||||||
|
format: "esm",
|
||||||
|
platform: "browser",
|
||||||
|
target: "es2022",
|
||||||
|
outdir: path.join(options.outDir, "js"),
|
||||||
|
minify: options.production,
|
||||||
|
sourcemap: options.production ? false : "inline",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ProcessOptions} options
|
||||||
|
*/
|
||||||
|
async function processStyles(options) {
|
||||||
|
const entryPoints = options.entryPoints.map((filepath) => path.join(options.rootDir, filepath));
|
||||||
|
await esbuild.build({
|
||||||
|
entryPoints,
|
||||||
|
bundle: true,
|
||||||
|
outdir: options.outDir,
|
||||||
|
minify: options.production,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ProcessOptions} options
|
||||||
|
*/
|
||||||
|
async function processHtml(options) {
|
||||||
|
for (const entryPoint of options.entryPoints) {
|
||||||
|
await fs.copyFile(path.join(options.rootDir, entryPoint), path.join(options.outDir, entryPoint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await processStatic();
|
56
viewer/css/pdf_viewer.css
Normal file
56
viewer/css/pdf_viewer.css
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
canvas {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #c0c0c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
--scale-factor: 1;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container canvas,
|
||||||
|
#container .textLayer {
|
||||||
|
/* overlay child elements on top of each other */
|
||||||
|
grid-row-start: 1;
|
||||||
|
grid-column-start: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-main-rotation="90"] {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-main-rotation="180"] {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-main-rotation="270"] {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hiddenCanvasElement {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
@ -3,39 +3,6 @@
|
|||||||
--text-layer-foreground: transparent;
|
--text-layer-foreground: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body, canvas {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #c0c0c0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#container {
|
|
||||||
--scale-factor: 1;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#container canvas, #container .textLayer {
|
|
||||||
/* overlay child elements on top of each other */
|
|
||||||
grid-row-start: 1;
|
|
||||||
grid-column-start: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer {
|
.textLayer {
|
||||||
text-align: initial;
|
text-align: initial;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -99,13 +66,3 @@ canvas {
|
|||||||
.textLayer .endOfContent.active {
|
.textLayer .endOfContent.active {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-main-rotation="90"] {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
[data-main-rotation="180"] {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
[data-main-rotation="270"] {
|
|
||||||
transform: rotate(270deg);
|
|
||||||
}
|
|
@ -3,14 +3,13 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, user-scalable=no" />
|
||||||
<title>PDF</title>
|
<title>PDF</title>
|
||||||
<link rel="stylesheet" href="viewer.css" />
|
<link rel="stylesheet" href="/viewer/main.css">
|
||||||
<script src="pdf.js"></script>
|
<script type="module" src="/viewer/js/index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<canvas id="content"></canvas>
|
<canvas id="content"></canvas>
|
||||||
<div id="text" class="textLayer"></div>
|
<div id="text" class="textLayer"></div>
|
||||||
</div>
|
</div>
|
||||||
<script src="viewer.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -1,6 +1,11 @@
|
|||||||
"use strict";
|
import {
|
||||||
|
GlobalWorkerOptions,
|
||||||
|
PasswordResponses,
|
||||||
|
getDocument,
|
||||||
|
renderTextLayer,
|
||||||
|
} from "pdfjs-dist";
|
||||||
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = "/pdf.worker.js";
|
GlobalWorkerOptions.workerSrc = "/viewer/js/worker.js";
|
||||||
|
|
||||||
let pdfDoc = null;
|
let pdfDoc = null;
|
||||||
let pageRendering = false;
|
let pageRendering = false;
|
||||||
@ -139,7 +144,7 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newCanvas = document.createElement("canvas");
|
const newCanvas = document.createElement("canvas");
|
||||||
const ratio = window.devicePixelRatio;
|
const ratio = globalThis.devicePixelRatio;
|
||||||
newCanvas.height = viewport.height * ratio;
|
newCanvas.height = viewport.height * ratio;
|
||||||
newCanvas.width = viewport.width * ratio;
|
newCanvas.width = viewport.width * ratio;
|
||||||
newCanvas.style.height = viewport.height + "px";
|
newCanvas.style.height = viewport.height + "px";
|
||||||
@ -166,7 +171,7 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) {
|
|||||||
render();
|
render();
|
||||||
|
|
||||||
const newTextLayerDiv = textLayerDiv.cloneNode();
|
const newTextLayerDiv = textLayerDiv.cloneNode();
|
||||||
task = pdfjsLib.renderTextLayer({
|
task = renderTextLayer({
|
||||||
textContentSource: page.streamTextContent(),
|
textContentSource: page.streamTextContent(),
|
||||||
container: newTextLayerDiv,
|
container: newTextLayerDiv,
|
||||||
viewport: viewport
|
viewport: viewport
|
||||||
@ -215,7 +220,7 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRenderPage(zoom) {
|
globalThis.onRenderPage = function (zoom) {
|
||||||
if (pageRendering) {
|
if (pageRendering) {
|
||||||
if (newPageNumber === channel.getPage() && newZoomRatio === channel.getZoomRatio() &&
|
if (newPageNumber === channel.getPage() && newZoomRatio === channel.getZoomRatio() &&
|
||||||
orientationDegrees === channel.getDocumentOrientationDegrees()) {
|
orientationDegrees === channel.getDocumentOrientationDegrees()) {
|
||||||
@ -232,13 +237,13 @@ function onRenderPage(zoom) {
|
|||||||
} else {
|
} else {
|
||||||
renderPage(channel.getPage(), zoom, false);
|
renderPage(channel.getPage(), zoom, false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function isTextSelected() {
|
globalThis.isTextSelected = function () {
|
||||||
return window.getSelection().toString() !== "";
|
return globalThis.getSelection().toString() !== "";
|
||||||
}
|
};
|
||||||
|
|
||||||
function toggleTextLayerVisibility() {
|
globalThis.toggleTextLayerVisibility = function () {
|
||||||
let textLayerForeground = "red";
|
let textLayerForeground = "red";
|
||||||
let textLayerOpacity = 1;
|
let textLayerOpacity = 1;
|
||||||
if (isTextLayerVisible) {
|
if (isTextLayerVisible) {
|
||||||
@ -248,15 +253,15 @@ function toggleTextLayerVisibility() {
|
|||||||
document.documentElement.style.setProperty("--text-layer-foreground", textLayerForeground);
|
document.documentElement.style.setProperty("--text-layer-foreground", textLayerForeground);
|
||||||
document.documentElement.style.setProperty("--text-layer-opacity", textLayerOpacity.toString());
|
document.documentElement.style.setProperty("--text-layer-opacity", textLayerOpacity.toString());
|
||||||
isTextLayerVisible = !isTextLayerVisible;
|
isTextLayerVisible = !isTextLayerVisible;
|
||||||
}
|
};
|
||||||
|
|
||||||
function loadDocument() {
|
globalThis.loadDocument = function () {
|
||||||
const pdfPassword = channel.getPassword();
|
const pdfPassword = channel.getPassword();
|
||||||
const loadingTask = pdfjsLib.getDocument({ url: "https://localhost/placeholder.pdf", password: pdfPassword });
|
const loadingTask = getDocument({ url: "https://localhost/placeholder.pdf", password: pdfPassword });
|
||||||
loadingTask.onPassword = (_, error) => {
|
loadingTask.onPassword = (_, error) => {
|
||||||
if (error === pdfjsLib.PasswordResponses.NEED_PASSWORD) {
|
if (error === PasswordResponses.NEED_PASSWORD) {
|
||||||
channel.showPasswordPrompt();
|
channel.showPasswordPrompt();
|
||||||
} else if (error === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
|
} else if (error === PasswordResponses.INCORRECT_PASSWORD) {
|
||||||
channel.invalidPassword();
|
channel.invalidPassword();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -274,8 +279,8 @@ function loadDocument() {
|
|||||||
}, function (reason) {
|
}, function (reason) {
|
||||||
console.error(reason.name + ": " + reason.message);
|
console.error(reason.name + ": " + reason.message);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
window.onresize = () => {
|
globalThis.onresize = () => {
|
||||||
setLayerTransform(canvas.clientWidth, canvas.clientHeight, textLayerDiv);
|
setLayerTransform(canvas.clientWidth, canvas.clientHeight, textLayerDiv);
|
||||||
};
|
};
|
1
viewer/js/worker.js
Normal file
1
viewer/js/worker.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
import "pdfjs-dist/build/pdf.worker.mjs";
|
2
viewer/main.css
Normal file
2
viewer/main.css
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@import url(css/pdf_viewer.css);
|
||||||
|
@import url(css/text_layer.css);
|
Loading…
Reference in New Issue
Block a user