Changed PDF file preview behavior (#3144)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -6,17 +6,20 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.9.6",
|
||||
"@types/pdfjs-dist": "^2.10.378",
|
||||
"kbar": "^0.1.0-beta.39",
|
||||
"local-storage-fallback": "^4.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.4.3",
|
||||
"mds": "https://github.com/minio/mds.git#v0.13.3",
|
||||
"pdfjs-dist": "3.11.174",
|
||||
"react": "^18.1.0",
|
||||
"react-component-export-image": "^1.0.6",
|
||||
"react-copy-to-clipboard": "^5.0.2",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-markdown": "8.0.7",
|
||||
"react-pdf": "7.5.1",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "6.20.1",
|
||||
"react-virtualized": "^9.22.5",
|
||||
|
||||
61779
portal-ui/public/scripts/pdf.worker.min.js
vendored
Normal file
61779
portal-ui/public/scripts/pdf.worker.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -950,11 +950,16 @@ const ListObjects = () => {
|
||||
bucketName={bucketName}
|
||||
/>
|
||||
)}
|
||||
{previewOpen && (
|
||||
{previewOpen && selectedPreview && (
|
||||
<PreviewFileModal
|
||||
open={previewOpen}
|
||||
bucketName={bucketName}
|
||||
object={selectedPreview}
|
||||
actualInfo={{
|
||||
name: selectedPreview.name || "",
|
||||
last_modified: "",
|
||||
version_id: selectedPreview.version_id || "",
|
||||
size: selectedPreview.size || 0,
|
||||
}}
|
||||
onClosePreview={closePreviewWindow}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -570,13 +570,7 @@ const ObjectDetailPanel = ({
|
||||
<PreviewFileModal
|
||||
open={previewOpen}
|
||||
bucketName={bucketName}
|
||||
object={{
|
||||
name: actualInfo.name || "",
|
||||
version_id: actualInfo.version_id || "null",
|
||||
size: actualInfo.size || 0,
|
||||
content_type: "",
|
||||
last_modified: actualInfo.last_modified || "",
|
||||
}}
|
||||
actualInfo={actualInfo}
|
||||
onClosePreview={() => {
|
||||
setPreviewOpen(false);
|
||||
}}
|
||||
|
||||
@@ -330,7 +330,7 @@ const VersionsNavigator = ({
|
||||
<PreviewFileModal
|
||||
open={previewOpen}
|
||||
bucketName={bucketName}
|
||||
object={{
|
||||
actualInfo={{
|
||||
name: actualInfo.name || "",
|
||||
version_id:
|
||||
objectToShare && objectToShare.version_id
|
||||
|
||||
@@ -17,28 +17,33 @@
|
||||
import React, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
import { ProgressBar, Grid, Box, InformativeMessage } from "mds";
|
||||
import get from "lodash/get";
|
||||
import { BucketObjectItem } from "../ListObjects/types";
|
||||
import { AllowedPreviews, previewObjectType } from "../utils";
|
||||
import { encodeURLString } from "../../../../../../common/utils";
|
||||
import { api } from "../../../../../../api";
|
||||
import PreviewPDF from "./PreviewPDF";
|
||||
import { downloadObject } from "../../../../ObjectBrowser/utils";
|
||||
import { useAppDispatch } from "../../../../../../store";
|
||||
import { BucketObject } from "../../../../../../api/consoleApi";
|
||||
|
||||
interface IPreviewFileProps {
|
||||
bucketName: string;
|
||||
object: BucketObjectItem | null;
|
||||
actualInfo: BucketObject;
|
||||
isFullscreen?: boolean;
|
||||
}
|
||||
|
||||
const PreviewFile = ({
|
||||
bucketName,
|
||||
object,
|
||||
actualInfo,
|
||||
isFullscreen = false,
|
||||
}: IPreviewFileProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
const [metaData, setMetaData] = useState<any>(null);
|
||||
const [isMetaDataLoaded, setIsMetaDataLoaded] = useState(false);
|
||||
|
||||
const objectName = object?.name || "";
|
||||
const objectName = actualInfo?.name || "";
|
||||
|
||||
const fetchMetadata = useCallback(() => {
|
||||
if (!isMetaDataLoaded) {
|
||||
@@ -71,12 +76,12 @@ const PreviewFile = ({
|
||||
|
||||
let path = "";
|
||||
|
||||
if (object) {
|
||||
const encodedPath = encodeURLString(object.name);
|
||||
if (actualInfo) {
|
||||
const encodedPath = encodeURLString(actualInfo.name || "");
|
||||
let basename = document.baseURI.replace(window.location.origin, "");
|
||||
path = `${window.location.origin}${basename}api/v1/buckets/${bucketName}/objects/download?preview=true&prefix=${encodedPath}`;
|
||||
if (object.version_id) {
|
||||
path = path.concat(`&version_id=${object.version_id}`);
|
||||
if (actualInfo.version_id) {
|
||||
path = path.concat(`&version_id=${actualInfo.version_id}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +179,18 @@ const PreviewFile = ({
|
||||
onLoad={iframeLoaded}
|
||||
/>
|
||||
)}
|
||||
{objectType === "pdf" && (
|
||||
<Fragment>
|
||||
<PreviewPDF
|
||||
path={path}
|
||||
onLoad={iframeLoaded}
|
||||
loading={loading}
|
||||
downloadFile={() =>
|
||||
downloadObject(dispatch, bucketName, path, actualInfo)
|
||||
}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{objectType === "none" && (
|
||||
<div>
|
||||
<InformativeMessage
|
||||
@@ -188,7 +205,8 @@ const PreviewFile = ({
|
||||
{objectType !== "none" &&
|
||||
objectType !== "video" &&
|
||||
objectType !== "audio" &&
|
||||
objectType !== "image" && (
|
||||
objectType !== "image" &&
|
||||
objectType !== "pdf" && (
|
||||
<div className={`iframeBase ${loading ? "iframeHidden" : ""}`}>
|
||||
<iframe
|
||||
src={path}
|
||||
|
||||
@@ -17,32 +17,32 @@
|
||||
import React, { Fragment } from "react";
|
||||
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
|
||||
import PreviewFileContent from "./PreviewFileContent";
|
||||
import { BucketObjectItem } from "../ListObjects/types";
|
||||
import { ObjectPreviewIcon } from "mds";
|
||||
import { BucketObject } from "../../../../../../api/consoleApi";
|
||||
|
||||
interface IPreviewFileProps {
|
||||
open: boolean;
|
||||
bucketName: string;
|
||||
object: BucketObjectItem | null;
|
||||
actualInfo: BucketObject;
|
||||
onClosePreview: () => void;
|
||||
}
|
||||
|
||||
const PreviewFileModal = ({
|
||||
open,
|
||||
bucketName,
|
||||
object,
|
||||
actualInfo,
|
||||
onClosePreview,
|
||||
}: IPreviewFileProps) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
title={`Preview - ${object?.name}`}
|
||||
title={`Preview - ${actualInfo?.name}`}
|
||||
onClose={onClosePreview}
|
||||
wideLimit={false}
|
||||
titleIcon={<ObjectPreviewIcon />}
|
||||
>
|
||||
<PreviewFileContent bucketName={bucketName} object={object} />
|
||||
<PreviewFileContent bucketName={bucketName} actualInfo={actualInfo} />
|
||||
</ModalWrapper>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useState } from "react";
|
||||
import { Document, Page, pdfjs } from "react-pdf";
|
||||
import { Box, Button, InformativeMessage } from "mds";
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = "./scripts/pdf.worker.min.js";
|
||||
|
||||
interface IPreviewPDFProps {
|
||||
path: string;
|
||||
loading: boolean;
|
||||
onLoad: () => void;
|
||||
downloadFile: () => void;
|
||||
}
|
||||
|
||||
const PreviewPDF = ({
|
||||
path,
|
||||
loading,
|
||||
onLoad,
|
||||
downloadFile,
|
||||
}: IPreviewPDFProps) => {
|
||||
const [errorState, setErrorState] = useState<boolean>(false);
|
||||
const [totalPages, setTotalPages] = useState<number>(0);
|
||||
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderPages = totalPages > 5 ? 5 : totalPages;
|
||||
const arrayCreate = Array.from(Array(renderPages).keys());
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{errorState && totalPages === 0 && (
|
||||
<InformativeMessage
|
||||
variant={"error"}
|
||||
title={"Error"}
|
||||
message={
|
||||
<Fragment>
|
||||
File preview couldn't be displayed, Please try Download instead.
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginTop: 12,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
id={"download-preview"}
|
||||
onClick={downloadFile}
|
||||
variant={"callAction"}
|
||||
>
|
||||
Download File
|
||||
</Button>
|
||||
</Box>
|
||||
</Fragment>
|
||||
}
|
||||
sx={{ marginBottom: 10 }}
|
||||
/>
|
||||
)}
|
||||
{!loading && !errorState && (
|
||||
<InformativeMessage
|
||||
variant={"warning"}
|
||||
title={"File Preview"}
|
||||
message={
|
||||
<Fragment>
|
||||
This is a File Preview for the first {arrayCreate.length} pages of
|
||||
the document, if you wish to work with the full document please
|
||||
download instead.
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginTop: 12,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
id={"download-preview"}
|
||||
onClick={downloadFile}
|
||||
variant={"callAction"}
|
||||
>
|
||||
Download File
|
||||
</Button>
|
||||
</Box>
|
||||
</Fragment>
|
||||
}
|
||||
sx={{ marginBottom: 10 }}
|
||||
/>
|
||||
)}
|
||||
{!errorState && (
|
||||
<Box
|
||||
sx={{
|
||||
overflowY: "auto",
|
||||
"& .react-pdf__Page__canvas": {
|
||||
margin: "0 auto",
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Document
|
||||
file={path}
|
||||
onLoadSuccess={({ _pdfInfo }) => {
|
||||
setTotalPages(_pdfInfo.numPages || 0);
|
||||
setErrorState(false);
|
||||
onLoad();
|
||||
}}
|
||||
onLoadError={(error) => {
|
||||
setErrorState(true);
|
||||
onLoad();
|
||||
console.error(error);
|
||||
}}
|
||||
>
|
||||
{arrayCreate.map((item) => (
|
||||
<Page
|
||||
pageNumber={item + 1}
|
||||
key={`render-page-${item}`}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
renderForms={false}
|
||||
/>
|
||||
))}
|
||||
</Document>
|
||||
</Box>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default PreviewPDF;
|
||||
@@ -191,7 +191,13 @@ class BrowserDownload {
|
||||
}
|
||||
}
|
||||
|
||||
export type AllowedPreviews = "image" | "text" | "audio" | "video" | "none";
|
||||
export type AllowedPreviews =
|
||||
| "image"
|
||||
| "pdf"
|
||||
| "text"
|
||||
| "audio"
|
||||
| "video"
|
||||
| "none";
|
||||
export const contentTypePreview = (contentType: string): AllowedPreviews => {
|
||||
if (contentType) {
|
||||
const mimeObjectType = (contentType || "").toLowerCase();
|
||||
@@ -199,6 +205,9 @@ export const contentTypePreview = (contentType: string): AllowedPreviews => {
|
||||
if (mimeObjectType.includes("image")) {
|
||||
return "image";
|
||||
}
|
||||
if (mimeObjectType.includes("pdf")) {
|
||||
return "pdf";
|
||||
}
|
||||
if (mimeObjectType.includes("text")) {
|
||||
return "text";
|
||||
}
|
||||
@@ -231,7 +240,8 @@ export const extensionPreview = (fileName: string): AllowedPreviews => {
|
||||
"png",
|
||||
"heic",
|
||||
];
|
||||
const textExtensions = ["pdf"];
|
||||
const textExtensions = ["txt"];
|
||||
const pdfExtensions = ["pdf"];
|
||||
const audioExtensions = ["wav", "mp3", "alac", "aiff", "dsd", "pcm"];
|
||||
const videoExtensions = [
|
||||
"mp4",
|
||||
@@ -258,6 +268,10 @@ export const extensionPreview = (fileName: string): AllowedPreviews => {
|
||||
return "image";
|
||||
}
|
||||
|
||||
if (pdfExtensions.includes(fileExtension)) {
|
||||
return "pdf";
|
||||
}
|
||||
|
||||
if (textExtensions.includes(fileExtension)) {
|
||||
return "text";
|
||||
}
|
||||
|
||||
108
portal-ui/tests/permissions-7/filePreview.ts
Normal file
108
portal-ui/tests/permissions-7/filePreview.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import * as roles from "../utils/roles";
|
||||
import { Selector } from "testcafe";
|
||||
import * as functions from "../utils/functions";
|
||||
import { namedTestBucketBrowseButtonFor } from "../utils/functions";
|
||||
|
||||
fixture("Test Preview page in Console").page("http://localhost:9090/");
|
||||
|
||||
const bucketName = "preview";
|
||||
export const file = Selector(".ReactVirtualized__Table__rowColumn").withText(
|
||||
"internode.png",
|
||||
);
|
||||
export const fileScript = Selector(
|
||||
".ReactVirtualized__Table__rowColumn",
|
||||
).withText("filescript.pdf");
|
||||
|
||||
export const pdfFile = Selector(".ReactVirtualized__Table__rowColumn").withText(
|
||||
"file1.pdf",
|
||||
);
|
||||
|
||||
const bucketNameAction = namedTestBucketBrowseButtonFor(bucketName);
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
await functions.setUpNamedBucket(t, bucketName);
|
||||
await functions.uploadNamedObjectToBucket(
|
||||
t,
|
||||
bucketName,
|
||||
"internode.png",
|
||||
"portal-ui/tests/uploads/internode.png",
|
||||
);
|
||||
})("File can be previewed", async (t) => {
|
||||
await t
|
||||
.useRole(roles.admin)
|
||||
.navigateTo(`http://localhost:9090/browser`)
|
||||
.click(bucketNameAction)
|
||||
.click(file)
|
||||
.click(Selector(".objectActions button").withText("Preview"))
|
||||
.expect(Selector(".dialogContent > div > img").exists)
|
||||
.ok();
|
||||
})
|
||||
.after(async (t) => {
|
||||
await functions.cleanUpNamedBucketAndUploads(t, bucketName);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
await functions.setUpNamedBucket(t, bucketName);
|
||||
await functions.uploadNamedObjectToBucket(
|
||||
t,
|
||||
bucketName,
|
||||
"file1.pdf",
|
||||
"portal-ui/tests/uploads/file1.pdf",
|
||||
);
|
||||
})("PDF File can be previewed", async (t) => {
|
||||
await t
|
||||
.useRole(roles.admin)
|
||||
.navigateTo(`http://localhost:9090/browser`)
|
||||
.click(bucketNameAction)
|
||||
.click(pdfFile)
|
||||
.click(Selector(".objectActions button").withText("Preview"))
|
||||
.expect(Selector(".react-pdf__Page__canvas").exists)
|
||||
.ok();
|
||||
})
|
||||
.after(async (t) => {
|
||||
await functions.cleanUpNamedBucketAndUploads(t, bucketName);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
await functions.setUpNamedBucket(t, bucketName);
|
||||
await functions.uploadNamedObjectToBucket(
|
||||
t,
|
||||
bucketName,
|
||||
"filescript.pdf",
|
||||
"portal-ui/tests/uploads/filescript.pdf",
|
||||
);
|
||||
})("PDF with Alert doesn't execute script", async (t) => {
|
||||
await t
|
||||
.useRole(roles.admin)
|
||||
.navigateTo(`http://localhost:9090/browser`)
|
||||
.click(bucketNameAction)
|
||||
.click(fileScript)
|
||||
.click(Selector(".objectActions button").withText("Preview"))
|
||||
.setNativeDialogHandler(() => false);
|
||||
|
||||
const history = await t.getNativeDialogHistory();
|
||||
|
||||
await t.expect(history.length).eql(0);
|
||||
})
|
||||
.after(async (t) => {
|
||||
await functions.cleanUpNamedBucketAndUploads(t, bucketName);
|
||||
});
|
||||
BIN
portal-ui/tests/uploads/file1.pdf
Normal file
BIN
portal-ui/tests/uploads/file1.pdf
Normal file
Binary file not shown.
254
portal-ui/tests/uploads/filescript.pdf
Normal file
254
portal-ui/tests/uploads/filescript.pdf
Normal file
@@ -0,0 +1,254 @@
|
||||
%PDF-1.3
|
||||
%<25>߬<EFBFBD>
|
||||
3 0 obj
|
||||
<</Type /Page
|
||||
/Parent 1 0 R
|
||||
/Resources 2 0 R
|
||||
/MediaBox [0 0 595.2799999999999727 841.8899999999999864]
|
||||
/Contents 4 0 R
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Length 126
|
||||
>>
|
||||
stream
|
||||
0.5670000000000001 w
|
||||
0 G
|
||||
BT
|
||||
/F1 16 Tf
|
||||
18.3999999999999986 TL
|
||||
0 g
|
||||
56.6929133858267775 785.1970866141732586 Td
|
||||
(Some text) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
1 0 obj
|
||||
<</Type /Pages
|
||||
/Kids [3 0 R ]
|
||||
/Count 1
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Helvetica
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Helvetica-Bold
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Helvetica-Oblique
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Helvetica-BoldOblique
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Courier
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Courier-Bold
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
11 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Courier-Oblique
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
12 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Courier-BoldOblique
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
13 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Times-Roman
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
14 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Times-Bold
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
15 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Times-Italic
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
16 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Times-BoldItalic
|
||||
/Subtype /Type1
|
||||
/Encoding /WinAnsiEncoding
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
17 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /ZapfDingbats
|
||||
/Subtype /Type1
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
18 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/BaseFont /Symbol
|
||||
/Subtype /Type1
|
||||
/FirstChar 32
|
||||
/LastChar 255
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
|
||||
/Font <<
|
||||
/F1 5 0 R
|
||||
/F2 6 0 R
|
||||
/F3 7 0 R
|
||||
/F4 8 0 R
|
||||
/F5 9 0 R
|
||||
/F6 10 0 R
|
||||
/F7 11 0 R
|
||||
/F8 12 0 R
|
||||
/F9 13 0 R
|
||||
/F10 14 0 R
|
||||
/F11 15 0 R
|
||||
/F12 16 0 R
|
||||
/F13 17 0 R
|
||||
/F14 18 0 R
|
||||
>>
|
||||
/XObject <<
|
||||
>>
|
||||
>>
|
||||
endobj
|
||||
19 0 obj
|
||||
<<
|
||||
/Names [(EmbeddedJS) 20 0 R]
|
||||
>>
|
||||
endobj
|
||||
20 0 obj
|
||||
<<
|
||||
/S /JavaScript
|
||||
/JS (app.alert(1);)
|
||||
>>
|
||||
endobj
|
||||
21 0 obj
|
||||
<<
|
||||
/Producer (jsPDF 2.5.1)
|
||||
/CreationDate (D:20231120150708-06'00')
|
||||
>>
|
||||
endobj
|
||||
22 0 obj
|
||||
<<
|
||||
/Type /Catalog
|
||||
/Pages 1 0 R
|
||||
/OpenAction [3 0 R /FitH null]
|
||||
/PageLayout /OneColumn
|
||||
/Names <</JavaScript 19 0 R>>
|
||||
>>
|
||||
endobj
|
||||
xref
|
||||
0 23
|
||||
0000000000 65535 f
|
||||
0000000329 00000 n
|
||||
0000002146 00000 n
|
||||
0000000015 00000 n
|
||||
0000000152 00000 n
|
||||
0000000386 00000 n
|
||||
0000000511 00000 n
|
||||
0000000641 00000 n
|
||||
0000000774 00000 n
|
||||
0000000911 00000 n
|
||||
0000001034 00000 n
|
||||
0000001163 00000 n
|
||||
0000001295 00000 n
|
||||
0000001431 00000 n
|
||||
0000001559 00000 n
|
||||
0000001686 00000 n
|
||||
0000001815 00000 n
|
||||
0000001948 00000 n
|
||||
0000002050 00000 n
|
||||
0000002394 00000 n
|
||||
0000002445 00000 n
|
||||
0000002502 00000 n
|
||||
0000002588 00000 n
|
||||
trailer
|
||||
<<
|
||||
/Size 23
|
||||
/Root 22 0 R
|
||||
/Info 21 0 R
|
||||
/ID [ <B6B01304F38DFFEC893A6499A048ED16> <B6B01304F38DFFEC893A6499A048ED16> ]
|
||||
>>
|
||||
startxref
|
||||
2722
|
||||
%%EOF
|
||||
BIN
portal-ui/tests/uploads/internode.png
Normal file
BIN
portal-ui/tests/uploads/internode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user