second-state / chat-with-chatgpt

Chat with ChatGPT via GitHub issue comments.
111 stars 13 forks source link

learn and work #279

Closed fuqiangqi closed 1 year ago

fuqiangqi commented 1 year ago

learn and work

alabulei1 commented 1 year ago

OFD是一种国家标准的电子文档格式,与PDF有些许不同。虽然OFD支持将文件转换为PDF格式,但它不是 PDF 格式的子集。

因此,将 OFD 文件解析成 PDF 文件可能存在一些问题。在实际操作中,如果 OFD 文件转换失败,那么可能是以下几个原因之一:

  1. OFD 文件存在格式问题。OFD 格式相对较新,一些编辑器可能不支持它,或者在转换过程中可能会丢失信息。因此,在转换 OFD 文件之前,需要先将文件严格验证,以确保它与 OFD 规范一致且没有任何错误。

  2. PDF 文件格式不完整。如果 OFD 文件不包含某些必需的元素或信息,那么它将无法转换为 PDF 格式,或转换后的文件可能会损坏或显示错误。

  3. 转换工具的问题。OFD 文件与 PDF 文件之间的转换可能需要一些特殊的工具或库,如果使用的工具或库不稳定或存在异常,那么转换过程可能会失败或出现错误。

如果 OFD 文件无法成功转换为 PDF 文件,可以尝试以下解决方法:

  1. 使用 OFD 阅读器打开 OFD 文件进行查看,确认文件是否存在格式问题或不完整的信息。

  2. 尝试使用其他 OFD 转换工具或库转换文件,以查看是否存在某些工具特有的问题或限制。

  3. 可以将 OFD 文件导出为其他格式,如图片或文字文档,并使用其他工具进行转换。

  4. 如果 OFD 文件是由其他系统生成的,则需要参考其生成过程,查看是否有特殊的设置或环境要求。

  5. 如果您使用的是第三方 OFD 转换库,那么可以考虑更新其版本或切换到其他库,以查看是否可以解决问题。

总之,在对 OFD 文件进行转换时,需要事先对文件进行验收,并使用专门的工具进行转换。遇到问题时,需要进行深入的排查和分析,以找到合适的解决方案。

fuqiangqi commented 1 year ago

推荐一个java的转换api给我

alabulei1 commented 1 year ago

如果您需要实现在Java代码中对不同文件格式进行转换的功能,以下是一些常见的Java转换API供您参考:

  1. Apache POI:用于读写Microsoft Office格式(Word,Excel和PowerPoint)的Java API。

  2. Apache PDFBox:用于处理PDF文件的Java API。

  3. iText:用于创建和处理PDF文件的Java API。

  4. jOfficeConvert:一个可以将Microsoft Word,Excel和PowerPoint文件转换为PDF,HTML和image格式的Java API。

  5. Docmosis:用于创建和生成动态文档的Java API,支持多种文件格式,如DOC,ODT,PDF,HTML等。

以上这些API均已经被广泛应用于Java的文件转换功能中,您可以根据具体的需求来选择合适的API进行使用。

fuqiangqi commented 1 year ago

写一段代码。vuejs下载blob的pdf附件,并在浏览器新页签打开该pdf,禁止用户复制pdf内容并且禁止打印功能,包括禁止浏览器快捷键Ctrl+p 打印,来个例子吧

alabulei1 commented 1 year ago

以下是一个基于Vue.js的例子,实现在浏览器中下载并打开pdf附件,同时禁止复制和打印功能。

<template>
  <div>
    <button @click="downloadPDF">Download PDF</button>
  </div>
</template>

<script>
export default {
  methods: {
    downloadPDF() {
      const FILE_URL = "https://example.com/file.pdf";

      fetch(FILE_URL, {
          method: "GET",
          headers: {
            "Content-Type": "application/pdf"
          }
        })
        .then(response => response.blob())
        .then(blob => {
          // Create an object URL from the blob
          const objectURL = URL.createObjectURL(blob);

          // Open a new window and load the PDF into it
          const newWindow = window.open(objectURL);
          newWindow.document.title = "PDF Document";

          // Disable copying and printing of the PDF document
          newWindow.document.addEventListener("contextmenu", e => e.preventDefault());
          newWindow.document.addEventListener("keydown", e => {
            if (e.ctrlKey && e.key == "p") {
              e.preventDefault();
            }
          });
        });
    }
  }
}
</script>

在该代码中,点击Download PDF按钮后会执行downloadPDF方法。该方法使用fetch API从服务器下载PDF文件,然后将Blob对象转换为URL对象,以便可以在浏览器中加载和显示PDF文件。最后,该方法在新窗口中打开新的PDF文档,并禁用复制和打印功能,通过在新窗口事件的监听中进行判断实现。(需要注意的是,禁用复制和打印功能是防范措施,但并不能保证完全禁止。)

fuqiangqi commented 1 year ago

newWindow.document.addEventListener("contextmenu", e => e.preventDefault()); 这行代码未生效是为什么

alabulei1 commented 1 year ago

该代码的作用是在新窗口中禁用右键上下文菜单,但并不是所有浏览器都支持这个事件。在一些浏览器中,如 Google Chrome 等,该代码需要在文档加载完后才能生效。因此,你可以使用如下代码在文档加载完成后禁用右键上下文菜单:

newWindow.addEventListener("load", () => {
  newWindow.document.addEventListener("contextmenu", e => e.preventDefault());
});

这段代码使用了新窗口的load事件来监听文档是否加载完成,然后在文档加载完成后才注册右键上下文菜单的事件监听器。

需要注意的是,本身就并不能完全禁止用户复制 PDF 内容或打印 PDF,因为这些操作可以通过各种方式进行绕过。这些限制只是一种额外的保护措施。

fuqiangqi commented 1 year ago

在谷歌浏览器下还是不生效。还有别的方式吗

alabulei1 commented 1 year ago

如果 addEventListener("contextmenu", e => e.preventDefault()) 在 Chrome 下不生效,可以试试使用下面的代码:

newWindow.document.oncontextmenu = function (e) {
  e.preventDefault();
};

这段代码使用了 oncontextmenu 事件来禁用右键上下文菜单。

如果需要同时禁用快捷键操作,如 Ctrl+P 打印操作,则可以使用以下代码:

// 禁用 Ctrl+P 快捷键操作
newWindow.addEventListener("keydown", (event) => {
  if (event.ctrlKey && event.keyCode === 80) {
    event.preventDefault();
  }
});

这段代码监听了 keydown 事件来捕捉键盘如何按下,并在用户按下 Ctrl+P 快捷键时禁用浏览器的默认行为(此处即为打印操作)。需要注意的是,这些限制并不能完全禁止用户复制 PDF 内容或打印 PDF,因为这些操作可以通过各种方式进行绕过。这些限制只是一种额外的保护措施。

fuqiangqi commented 1 year ago

还是不生效 我是在vue的method模块中的函数中调用的 代码如下。分析下有没有什么问题 viewFile(filename) { viewFile(filename).then((response) => { const blob = new Blob([response], {type: 'application/pdf;charset-utf-8'}); let href = window.URL.createObjectURL(blob); const newWindow= window.open(href+'#toolbar=0');

        newWindow.document.oncontextmenu = function (e) {
         e.preventDefault();
        };

    });
}
alabulei1 commented 1 year ago

在你提供的代码中,禁用右键上下文菜单的代码是正确的,但禁用快捷键操作的代码是错误的。按照你的代码,newWindow.document 是一个 undefined ,因为 newWindow 对象本身没有 document 属性,因此你需要更改一下禁用快捷键操作的代码:

newWindow.addEventListener("keydown", (event) => {
  if (event.ctrlKey && event.keyCode === 80) {
    event.preventDefault();
  }
});

需要注意的是,PDF 防复制和防打印功能都是一种额外的保护措施,因为用户可以通过多种方式绕过这些限制。因此,这些限制不能保证完全禁止用户复制 PDF 内容或打印 PDF。

fuqiangqi commented 1 year ago

在上面的例子中document.title是可以传递过去的 但是事件监听无法生效 这是为什么

alabulei1 commented 1 year ago

事件监听无法生效的原因是你在打开新窗口之前先设置了窗口的标题,而这些事件监听器需要在文档加载完成之后才能添加。当你设置窗口标题之后,浏览器会立即开始加载 PDF 文档,此时文档并未加载完成,因此无法正常添加事件监听器。

为了解决这个问题,你可以先加载 PDF 文档,然后在文档加载完成之后再添加事件监听器。下面是一个示例代码:

const newWindow = window.open("", "_blank");
newWindow.document.write("<p>Loading PDF document...</p>");
newWindow.document.title = "PDF Document";

const xhr = new XMLHttpRequest();
xhr.open("GET", "https://example.com/file.pdf", true);
xhr.responseType = "blob";
xhr.onload = function () {
  const blob = xhr.response;
  // Create object URL from the blob
  const objectURL = URL.createObjectURL(blob);
  // Set the new window location to the object URL
  newWindow.location = objectURL;

  // Add event listeners
  newWindow.addEventListener("load", () => {
    // Disable context menu
    newWindow.document.addEventListener("contextmenu", (e) => e.preventDefault());
    // Disable print shortcut
    newWindow.addEventListener("keydown", (e) => {
      if ((e.ctrlKey || e.metaKey) && e.key === "p") {
        e.preventDefault();
      }
    });
  });
};
xhr.send();

该代码创建了一个新的空白窗口并将其赋给 newWindow 变量,然后使用 XMLHttpRequest(也可以使用 fetch) 从服务器下载 PDF 文件。当文档下载完成后,代码将 PDF 文档设置为新窗口的位置,并为文档添加事件监听器以禁用右键菜单和打印快捷键。所有的代码都是在 JavaScript 的主线程上执行的,因此不需要使用 async/await 或 Promise。

fuqiangqi commented 1 year ago

用的是谷歌浏览器自己的pdf实现 这代码也能控制吗。确定能添加document事件?

alabulei1 commented 1 year ago

如果你使用的是浏览器自带的 PDF 实现,可能无法添加到 document 对象上的事件。在这种情况下,你需要改为将事件监听器添加到 window 对象上,例如:

const newWindow = window.open("", "_blank");
newWindow.document.write("<p>Loading PDF document...</p>");
newWindow.document.title = "PDF Document";

const xhr = new XMLHttpRequest();
xhr.open("GET", "https://example.com/file.pdf", true);
xhr.responseType = "blob";
xhr.onload = function () {
  const blob = xhr.response;
  // Create object URL from the blob
  const objectURL = URL.createObjectURL(blob);
  // Set the new window location to the object URL
  newWindow.location = objectURL;

  // Add event listeners
  newWindow.addEventListener("load", () => {
    // Disable context menu
    newWindow.addEventListener("contextmenu", (e) => e.preventDefault());
    // Disable print shortcut
    newWindow.addEventListener("keydown", (e) => {
      if ((e.ctrlKey || e.metaKey) && e.key === "p") {
        e.preventDefault();
      }
    });
  });
};
xhr.send();

在这个例子中,我们将事件监听器添加到了 newWindowaddEventListener 方法中,并且将 document 改为了 window。这将确保我们可以在新窗口中禁用右键菜单和快捷键,而无需访问 document 对象。

fuqiangqi commented 1 year ago

如何使用blob的pdf文件,在谷歌浏览器自带的 PDF 预览实现查看,且不允许右键操作,

alabulei1 commented 1 year ago

你可以使用以下代码实现:

const blob = new Blob([response], { type: 'application/pdf' });
const objectUrl = URL.createObjectURL(blob);

const newWindow = window.open(objectUrl, 'PDF Viewer');
newWindow.document.addEventListener("contextmenu", (e) => {
  e.preventDefault();
});

// 禁用 Ctrl+P 快捷键操作
newWindow.addEventListener("keydown", (event) => {
  if (event.ctrlKey && event.keyCode === 80) {
    event.preventDefault();
  }
});

这段代码首先创建了一个 Blob 对象,然后使用 URL.createObjectURL() 方法创建一个 URL,将其设置为新窗口的 location 属性。然后通过向 newWindow.document 添加一个 contextmenu 事件监听器来禁用 PDF 预览中的右键菜单,使用 newWindow.addEventListener() 方法来禁用打印快捷键。

需要注意的是,PDF 防复制和防打印功能都是一种额外的保护措施,因为用户可以通过多种方式绕过这些限制。因此,这些限制不能保证完全禁止用户复制 PDF 内容或打印 PDF。

fuqiangqi commented 1 year ago

如何使用blob的pdf文件,在谷歌浏览器自带的 PDF 预览实现查看,且不允许右键操作,

alabulei1 commented 1 year ago

你可以使用以下代码实现:

const blob = new Blob([response], { type: 'application/pdf' });
const objectUrl = URL.createObjectURL(blob);

const newWindow = window.open(objectUrl, 'PDF Viewer');
newWindow.document.addEventListener("contextmenu", (e) => {
  e.preventDefault();
});

// 禁用 Ctrl+P 快捷键操作
newWindow.addEventListener("keydown", (event) => {
  if (event.ctrlKey && event.keyCode === 80) {
    event.preventDefault();
  }
});

这段代码首先创建了一个 Blob 对象,然后使用 URL.createObjectURL() 方法创建一个 URL,将其设置为新窗口的 location 属性。然后通过向 newWindow.document 添加一个 contextmenu 事件监听器来禁用 PDF 预览中的右键菜单,使用 newWindow.addEventListener() 方法来禁用打印快捷键。

需要注意的是,PDF 防复制和防打印功能都是一种额外的保护措施,因为用户可以通过多种方式绕过这些限制。因此,这些限制不能保证完全禁止用户复制 PDF 内容或打印 PDF。

fuqiangqi commented 1 year ago

给我一个可以直接允许的完整代码例子。我用浏览器跑下看

alabulei1 commented 1 year ago

下面是一个完整的例子:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>PDF Viewer</title>
    <script>
      function viewPDF(response) {
        const blob = new Blob([response], {type: 'application/pdf'});
        const objectUrl = URL.createObjectURL(blob);
        const newWindow = window.open(objectUrl, '_blank', 'fullscreen=yes');

        newWindow.document.addEventListener('contextmenu', function(e) {
          e.preventDefault();
        });

        newWindow.addEventListener('keydown', function(e) {
          if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
            e.preventDefault();
          }
        });
      }

      function loadPDF() {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', 'https://example.com/sample.pdf', true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function () {
          if (this.readyState === this.DONE && this.status === 200) {
            const response = this.response;
            viewPDF(response);
          }
        };
        xhr.send(null);
      }
    </script>
  </head>
  <body onload="loadPDF()">
    <h1>PDF Viewer</h1>
  </body>
</html>

这段代码创建了一个浮动窗口,并在窗口中打开 PDF 文件。PDF 文件被加载到 Blob 对象中,通过创建 URL 并将 URL 设置为新窗口的位置来进行显示。在新窗口中,添加了 contextmenu 事件监听器以禁用右键菜单,以及一个 keydown 监听器以禁用打印快捷键。你可以将 PDF 文件的地址替换为你自己的 PDF 文件地址。

fuqiangqi commented 1 year ago

给我一个pdf.js的例子。vue实现在新窗口打开pdf,并且pdf不能被打印和下载复制

alabulei1 commented 1 year ago

下面是一个使用 PDF.js 实现在新窗口中打开 PDF 并禁用打印和文本复制的 Vue 示例:

<template>
  <div>
    <button @click="viewPDF()">View PDF</button>
  </div>
</template>

<script>
import pdfjsLib from 'pdfjs-dist';

export default {
  methods: {
    viewPDF() {
      const newWindow = window.open('', '_blank');
      const pdfUrl = 'https://example.com/sample.pdf';

      pdfjsLib.getDocument(pdfUrl).promise.then(function(pdfDoc) {
        pdfDoc.getPage(1).then(function(page) {
          const canvas = document.createElement('canvas');
          const viewport = page.getViewport({ scale: 1 });
          const canvasContext = canvas.getContext('2d');
          canvas.height = viewport.height;
          canvas.width = viewport.width;

          const renderContext = {
            canvasContext,
            viewport,
          };

          page.render(renderContext).then(function() {
            const image = canvas.toDataURL('image/jpeg', 1.0);
            const imgTag = `<img style="width: 100%;" src="${image}">`;

            newWindow.document.write(imgTag);
            newWindow.document.title = 'PDF Document';

            newWindow.document.addEventListener('contextmenu', function(e) {
              e.preventDefault();
            });

            newWindow.addEventListener('keydown', function(e) {
              if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
                e.preventDefault();
              }
            });
          });
        });
      });
    }
  }
}
</script>

在此示例中,我们使用的是 PDF.js 库的功能来加载 PDF 文件,并在新窗口中以图像格式显示 PDF 页面。我们将 PDF 页面呈现到一个 canvas 上并将 canvas 装换为图像,然后将该图像作为 img 标签插入到新窗口的文档中。

我们同样添加了 contextmenu 事件监听器以禁用右键菜单,并添加了一个 keydown 监听器以禁用打印快捷键。这样,PDF 文件就不会被打印或者复制至剪贴板。需要注意的是,这些限制不能保证完全禁止用户复制 PDF 内容或打印 PDF。

fuqiangqi commented 1 year ago

那怎么导入pdf.js的包呢

alabulei1 commented 1 year ago

你可以通过以下步骤在 Vue 项目中导入 PDF.js:

  1. 安装 PDF.js

    npm install pdfjs-dist
  2. main.js 中引入 PDF.js 的样式和脚本文件:

    import 'pdfjs-dist/web/pdf_viewer.css';
    import pdfjsLib from 'pdfjs-dist';
  3. 注册 PDF.js:

    Vue.prototype.$pdfjsLib = pdfjsLib;
  4. 在组件中使用 this.$pdfjsLib 调用 PDF.js 的功能。

下面是完整的 main.js 文件和使用示例:

import Vue from 'vue';
import App from './App.vue';
import 'pdfjs-dist/web/pdf_viewer.css';
import pdfjsLib from 'pdfjs-dist';

Vue.config.productionTip = false;
Vue.prototype.$pdfjsLib = pdfjsLib;

new Vue({
  render: (h) => h(App),
}).$mount('#app');

在组件中使用 this.$pdfjsLib 以显示 PDF 文件并禁用打印和文本复制:

<template>
  <div>
    <button @click="viewPDF()">View PDF</button>
  </div>
</template>

<script>
export default {
  methods: {
    viewPDF() {
      const newWindow = window.open('', '_blank');
      const pdfUrl = 'https://example.com/sample.pdf';

      this.$pdfjsLib.getDocument(pdfUrl).promise.then(function(pdfDoc) {
        pdfDoc.getPage(1).then(function(page) {
          const canvas = document.createElement('canvas');
          const viewport = page.getViewport({ scale: 1 });
          const canvasContext = canvas.getContext('2d');
          canvas.height = viewport.height;
          canvas.width = viewport.width;

          const renderContext = {
            canvasContext,
            viewport,
          };

          page.render(renderContext).then(function() {
            const image = canvas.toDataURL('image/jpeg', 1.0);
            const imgTag = `<img style="width: 100%;" src="${image}">`;

            newWindow.document.write(imgTag);
            newWindow.document.title = 'PDF Document';

            newWindow.document.addEventListener('contextmenu', function(e) {
              e.preventDefault();
            });

            newWindow.addEventListener('keydown', function(e) {
              if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
                e.preventDefault();
              }
            });
          });
        });
      });
    }
  }
}
</script>

需要注意,虽然禁止打印和复制便于保护 PDF 内容,但用户可以使用多种方式绕过这些限制。因此,这些限制不能保证完全禁止用户复制 PDF 内容或打印 PDF。

fuqiangqi commented 1 year ago

viewPDF这个函数能否改为接受blob的实现呢

alabulei1 commented 1 year ago

是的,你可以改变viewPDF()函数来支持接受一个 PDF 文件的 Blob 对象,而不是通过 AJAX 请求来获取 PDF 文件。

以下是修改后的代码:

<template>
  <div>
    <button @click="viewPDF()">View PDF</button>
  </div>
</template>

<script>
export default {
  methods: {
    viewPDF() {
      const newWindow = window.open('', '_blank');

      // Replace the URL with the URL of your PDF file
      this.fetchPDF().then(function (blob) {
        const objectUrl = URL.createObjectURL(blob);

        newWindow.location.href = objectUrl;
        newWindow.document.title = 'PDF Document';

        newWindow.document.addEventListener('contextmenu', function(e) {
          e.preventDefault();
        });

        newWindow.addEventListener('keydown', function(e) {
          if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
            e.preventDefault();
          }
        });
      });
    },

    async fetchPDF() {
      const response = await fetch('https://example.com/sample.pdf');
      const blob = await response.blob();
      return blob;
    },
  },
};
</script>

在这个例子中,我们使用了一个新的异步函数fetchPDF(),可以通过 Blob 对象获取 PDF 文件。我们修改了viewPDF()函数来使用 Blob 对象,而不是通过 AJAX 请求来获取这个 PDF 文件。并将 PDF 对象的 URL 赋值给新窗口的位置以便显示 PDF 文件。然后添加了contextmenukeydown事件监听器以禁用复制、粘贴和打印功能。

fuqiangqi commented 1 year ago

PDF.JS是否支持blob的文件流

alabulei1 commented 1 year ago

是的,PDF.js 在处理 PDF 文件时可以接受 Blob 对象并将其用作数据源。使用 Blob 格式的 PDF 文件,可以通过以下方式进行加载和渲染:

const url = URL.createObjectURL(blob);
pdfjsLib.getDocument(url).promise.then(function(pdf) {
    // Render PDF
});

在此示例中,我们使用 URL.createObjectURL() 方法将 Blob 对象转化为 URL,然后将 URL 作为参数提供给 getDocument() 函数。 PDF.js 将使用提供的文件源进行处理,并在加载完成后返回一个 pdf 对象,我们可以用它来渲染 PDF。

值得注意的是,不是所有浏览器都支持使用Blob对象而不需要创建URL的方式来加载和处理PDF。在某些浏览器(如Safari)中,可能需要先将Blob传递给FileReader对象并使用其结果来加载PDF:

const reader = new FileReader();
reader.readAsArrayBuffer(blob);
reader.onloadend = function() {
    pdfjsLib.getDocument(reader.result).promise.then(function(pdf) {
        // Render PDF
    });
};

在这个示例中,我们使用 FileReader.readAsArrayBuffer() 方法将 Blob 对象转换为一个 ArrayBuffer ,随后通过 getDocument() 函数异步地将 ArrayBuffer 作为参数传递给 PDF.js 。结果与前一种方法相同,我们可以使用返回的pdf对象来渲染 PDF。

总之,在使用 PDF.js 时,我们应该优先使用第一种方法,只有在其他浏览器没有提供对Blob URL的支持时,才使用第二种方法。

fuqiangqi commented 1 year ago

"vue-pdf": "^4.3.0", 实现pdf查看。并且用户不能复制打印

alabulei1 commented 1 year ago

好的,你可以使用 vue-pdf 库来实现 PDF 文件的查看,并且禁用打印和复制功能。

首先,通过以下命令安装 vue-pdf

npm install vue-pdf --save

接下来,在需要显示 PDF 文件的组件中,导入并注册 vue-pdf 组件。在模板中,使用 vue-pdf 标记并通过 src 属性指定 PDF 文件的 URL 或 Blob,使用 :options 属性来传递 PDF.js 的配置选项,例如禁用打印和文本选择功能。以下是示例代码:

<template>
  <div>
    <pdf :src="pdfUrl" :options="pdfOptions"></pdf>
  </div>
</template>

<script>
import { pdf } from 'vue-pdf';

export default {
  name: 'PdfViewer',
  components: {
    pdf,
  },
  data() {
    return {
      pdfUrl: 'https://example.com/sample.pdf',
      pdfOptions: {
        // Disable text selection
        textLayer: false,
        // Disable printing
        printing: false,
        // Disable context menu
        contextMenuItems: [
          { label: 'Adobe Reader', visible: false },
          { label: 'Print', visible: false },
          ...pdf.defaultOptions.contextMenuItems,
        ],
      },
    };
  },
};
</script>

在这个例子中,我们导入并注册了谷歌开发的 vue-pdf 组件,并在模板中使用了 pdf 组件。我们给 pdf 组件传递了 srcoptions 属性,其中 src 指定 PDF 文件的 URL 或 Blob,options 是一个对象,可用于传递 PDF.js 的配置选项。我们使用 textLayerprintingcontextMenuItems 选项来禁用 PDF 文件中的文本选择、打印和上下文菜单中的打印选项。

注意,在禁止文本选择时,不会删除文字内容。用户仍然可以通过复制整个页面或使用其他方法将 PDF 文件中的文本保存到本地。

最后,可以使用 CSS 样式来隐藏 PDF.js 中的 "下载" 按钮,如下所示:

.pdf-download {
  display: none;
}

这样就可以使用 vue-pdf 在 Vue 应用程序中显示 PDF 文件并禁用打印和复制功能了。

fuqiangqi commented 1 year ago

我需要从一个函数中触发查看pdf的操作,然后跳转到新页签查看,代码怎么完善

alabulei1 commented 1 year ago

好的,你可以在需要的函数中触发 viewPDF() 函数,然后跳转到新页签中查看 PDF 文件。以下是示例代码:

methods: {
  openPDF() {
    this.fetchPDF().then(blob => {
      const url = URL.createObjectURL(blob);
      const newWindow = window.open(url, '_blank');

      newWindow.addEventListener('load', () => {
        newWindow.document.title = 'PDF Document';
        newWindow.document.addEventListener('contextmenu', e => {
          e.preventDefault();
        });
        newWindow.addEventListener('keydown', e => {
          if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
            e.preventDefault();
          }
        });
      });
    });
  },

  async fetchPDF() {
    const response = await fetch('https://example.com/sample.pdf');
    const blob = await response.blob();
    return blob;
  },
}

在此示例中,openPDF() 函数触发了 fetchPDF() 函数,该函数使用 Blob 对象获取 PDF 文件。然后,我们使用 window.open() 方法在新标签页中打开 PDF 文件,并在 onload 事件中设置新页面的标题、禁用上下文菜单,防止打印 PDF 文件。 使用addEventListener()函数进行添加事件的监听器。在“keydown”事件中阻止组合键(如“Ctrl + P”)的默认行为,防止用户打印 PDF 文件。

你可以把 openPDF() 函数放到 Vue 组件的方法中,然后在模板中使用 @click 事件绑定来触发点击事件,如下所示:

<template>
  <div>
    <button @click="openPDF">Open PDF</button>
  </div>
</template>

<script>
export default {
  methods: {
    // openPDF function code here...
  },
};
</script>

这样就可以从一个函数中触发 viewPDF() ,然后跳转到新页签查看了。

fuqiangqi commented 1 year ago

结合vue-pdf 实现

alabulei1 commented 1 year ago

好的,你可以结合 vue-pdf 组件实现这个功能,以下是示例代码:

<template>
  <div>
    <button @click="openPDF">View PDF</button>
  </div>
</template>

<script>
import { pdf } from 'vue-pdf';

export default {
  components: { pdf },
  methods: {
    openPDF() {
      this.fetchPDF().then(blob => {
        const url = URL.createObjectURL(blob);
        const newWindow = window.open('', '_blank');

        newWindow.document.title = 'PDF Document';
        newWindow.document.body.innerHTML = '<pdf :src="pdfUrl" :options="pdfOptions"></pdf>';

        const app = new Vue({
          el: newWindow.document.body.firstChild,
          data: {
            pdfUrl: url,
            pdfOptions: {
              textLayer: false,
              printing: false,
              contextMenuItems: [
                { label: 'Adobe Reader', visible: false },
                { label: 'Print', visible: false },
                ...pdf.defaultOptions.contextMenuItems,
              ],
            },
          },
          components: { pdf }
        });
      });
    },

    async fetchPDF() {
      const response = await fetch('https://example.com/sample.pdf');
      const blob = await response.blob();
      return blob;
    },
  },
};
</script>

在此示例中,我们使用 vue-pdf 组件来查看 PDF 文件。在 openPDF() 函数中,我们使用 fetchPDF() 函数获取 PDF 文件的 Blob 对象,然后使用 window.open() 方法在新标签页中打开一个空页面。随后我们使用以下几个步骤实现 PDF 文件的预览:

  1. 创建一个空的 Vue 实例。

  2. pdf 组件放入空白的 body 标签中。

  3. pdf 组件的属性设置为 PDF 文件的 URL 和 PDF.js 的配置选项。

  4. 使用 Vue 实例将组件加载到空白的 body 标签中。

现在,当你点击“View PDF”按钮时,将会打开一个空白的新标签页。标签页会自动加载 PDF 文档,不支持复制和打印功能。

注意,这里我们使用了Vue.js Runtime + Compiler构建方式,这意味着在浏览器中要使用Vue.js,并且需要支持编译器才能正常使用。

如果你不想使用Vue.js,你可以简单地手动创建 PDF.js 容器并为其设置属性。例如:

newWindow.document.title = 'PDF Document';
newWindow.document.body.innerHTML = `
  <div id="pdf-container"></div>
`;

pdfjsLib.getDocument(url).promise.then(function(pdf) {
  pdf.getPage(1).then(function(page) {
    const scale = 1.5;
    const viewport = page.getViewport({scale: scale});

    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.height = viewport.height;
    canvas.width = viewport.width;

    newWindow.document.getElementById('pdf-container').appendChild(canvas);

    page.render({
      canvasContext: context,
      viewport: viewport,
    });
  });
});

这里我们手动创建了一个 #pdf-container DIV 元素作为 PDF.js 预览容器。我们通过 pdfjsLib.getDocument() 函数加载 PDF 文件,并使用 page.render() 函数将 PDF 文件渲染到画布上。这种方式需要手动创建和处理各种元素,可能会增加代码复杂度。

fuqiangqi commented 1 year ago

vue的vue-pdf插件 实现不分页预览pdf

alabulei1 commented 1 year ago

好的,你可以使用 vue-pdf 组件来实现不分页地预览 PDF 文件。

不分页预览 PDF 文件的方法是将 PDF 文件加载到单个页面中,而不是将每个页面加载到单独的页面中。要实现这一点,你可以启用 PDF.js 的 enableAllPlugin 选项将 PDF 文件加载到一个 DOM 元素中。这将使 PDF.js 将所有页面合并成单个页面,并将其渲染到该元素中。以下是示例代码:

<template>
  <div>
    <pdf ref="pdfViewer"></pdf>
  </div>
</template>

<script>
import { pdfjs } from 'vue-pdf';
pdfjs.GlobalOptions.disableAutoFetch = true;

export default {
  components: { pdf },
  mounted() {
    this.showPDF();
  },
  methods: {
    showPDF() {
      this.fetchPDF().then(blob => {
        const url = URL.createObjectURL(blob);
        this.$refs.pdfViewer.load(url, { enableAllPlugins: true });
      });
    },
    async fetchPDF() {
      const response = await fetch('https://example.com/sample.pdf');
      const blob = await response.blob();
      return blob;
    },
  }
};
</script>

在此示例中,我们在组件的 mounted() 钩子函数中调用 showPDF() 函数,该函数通过 fetchPDF() 函数获取 PDF 文件的 Blob 对象,并使用 load() 函数将其加载到 vue-pdf 组件中。注意 pdfjs.GlobalOptions.disableAutoFetch = true; 这句代码可以禁用自动下载 PDF 文件的功能,这样可以避免组件加载时就开始下载 PDF 文件。

load() 函数的第二个参数接收 PDF.js 的配置选项,这里我们启用了 enableAllPlugins 选项,以便在单个页面中预览整个 PDF 文件。

现在,当你在 Vue 应用程序中使用此组件时,它将在一个元素上渲染整个 PDF 文件,而不是将每个页面加载到单独的元素中。这将使用户能够在不分页的情况下浏览整个 PDF 文件。

注意,此方法加载整个 PDF 文件可能会消耗较多内存。如果你的 PDF 文件非常大,可能需要考虑其他分页方案。

fuqiangqi commented 1 year ago

使用vue-pdf 一次性展示所有pdf页

alabulei1 commented 1 year ago

使用 vue-pdf 组件一次性展示所有的 PDF 页面需要将所有 PDF 页面渲染到同一元素中。你可以使用 pdfjs-dist 库来实现这一点,它是 PDF.js 库的一个打包好的版本。以下是一个简单的示例:

<template>
  <div ref="pdfContainer"></div>
</template>

<script>
import pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.worker.js';

export default {
  mounted() {
    this.showPDF();
  },
  methods: {
    async showPDF() {
      const pdf = await this.fetchPDF();
      const totalPages = pdf.numPages;
      const canvasArray = [];

      const pdfContainer = this.$refs.pdfContainer;

      for (let i = 1; i <= totalPages; i++) {
        const canvas = document.createElement('canvas');
        canvasArray.push(canvas);
        pdfContainer.appendChild(canvas);
        const page = await pdf.getPage(i);
        const viewport = page.getViewport({scale: 1});
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        const renderContext = {
          canvasContext: canvas.getContext('2d'),
          viewport: viewport,
        };
        await page.render(renderContext).promise;
      }

      const pdfWidth = canvasArray[0].width;
      const pdfHeight = canvasArray[0].height * totalPages;

      pdfContainer.style.width = `${pdfWidth}px`;
      pdfContainer.style.height = `${pdfHeight}px`;

      canvasArray.forEach((canvas, index) => {
        canvas.style.position = 'absolute';
        canvas.style.top = `${canvas.height * index}px`;
      });
    },

    async fetchPDF() {
      const response = await fetch('https://example.com/sample.pdf');
      const data = await response.arrayBuffer();
      return pdfjsLib.getDocument({data});
    }
  }
};
</script>

在此示例中,我们获取了 PDF 文件并将每个页面渲染到 Canvas 元素中。然后,我们将所有的 Canvas 元素添加到 pdfContainer 引用的 DOM 元素中,并根据 Canvas 元素的总高度和宽度设置该元素的大小。最后,我们按照页面顺序排列所有的 Canvas 元素,使其呈现在同一元素中。

需要注意的是,使用这种方法加载所有页面可能会花费大量的内存,并且对于大型 PDF 文件可能会对性能产生影响。所以,在使用时应该理解它的局限性,以及根据需求进行分页处理,以避免不必要的资源消耗。

fuqiangqi commented 1 year ago

只基于vue-pdf实现。不需要pdfjs-dist这个库,

alabulei1 commented 1 year ago

好的,你可以使用 vue-pdf 组件的 page 插槽来实现一次性展示所有 PDF 页面。以下是一个简单的示例:

<template>
  <pdf ref="pdfViewer">
    <template v-for="page in totalPages" :slot="`page-${page}`">
      <canvas ref="canvas" :key="`canvas-${page}`"></canvas>
    </template>
  </pdf>
</template>

<script>
import { pdf } from 'vue-pdf';

export default {
  components: { pdf },
  data() {
    return {
      totalPages: 0,
    };
  },
  mounted() {
    this.showPDF();
  },
  methods: {
    async showPDF() {
      const pdf = await this.fetchPDF();
      this.totalPages = pdf.numPages;

      for (let i = 1; i <= this.totalPages; i++) {
        const page = await pdf.getPage(i);
        const viewport = page.getViewport({ scale: 1 });
        const canvas = this.$refs.canvas[i - 1];
        canvas.height = viewport.height;
        canvas.width = viewport.width;
        const context = canvas.getContext('2d');
        const renderContext = {
          canvasContext: context,
          viewport: viewport,
        };
        await page.render(renderContext).promise;
      }

      this.$refs.pdfViewer.fit();
    },
    async fetchPDF() {
      const response = await fetch('https://example.com/sample.pdf');
      const data = await response.arrayBuffer();
      return pdfjsLib.getDocument({ data });
    },
  }
};
</script>

在此示例中,我们在 vue-pdf 组件中,使用 v-for 循环来动态生成 Canvas 元素。在 showPDF() 方法中,我们获取 PDF 文档,并通过循环来按顺序将每个页面渲染到对应的 Canvas 元素中。

值得注意的是,通过 page 插槽来渲染 PDF 页面时,需要将 vue-pdf 组件的 fit 方法放在获取并渲染 PDF 页面的代码之后,以确保页面尺寸的自适应调整。

另外,需要注意的是,通过此方法加载所有页面可能会花费大量的内存,并且对于大型 PDF 文件可能会对性能产生影响。因此,这种方法适用于页面数量较少的 PDF 文件,如果 PDF 文件太大,你可以考虑使用分页加载的方法。

fuqiangqi commented 1 year ago

一个html页面。里面只有一个div,控制该div中的pdf在浏览器中居中显示,类似谷歌浏览器的pdf居中显示位置,给出这么一个样式

alabulei1 commented 1 year ago

你可以使用以下 CSS 样式来实现在浏览器中居中显示 PDF 的效果:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>PDF Centered in Browser</title>
    <style>
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
      #pdf-container {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 80%;
        height: 80%;
      }
      #pdf-embed {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <div id="pdf-container">
      <embed id="pdf-embed" src="sample.pdf" type="application/pdf" />
    </div>
  </body>
</html>

在此示例中,我们创建了一个名为 pdf-container 的 div,将其绝对定位于页面的中心位置,并设置其宽度和高度为浏览器视窗的 80%。我们在该 div 中嵌入了一个 embed 元素来加载 PDF 文件,该元素的宽度和高度设置为 100%,以确保 PDF 文件填充整个 div。

通过将 pdf-container div 的左侧和顶部偏移设置为 -50%(通过 transform: translate(-50%, -50%)),我们确保该 div 的中心位置与浏览器视窗的中心位置对齐,从而实现了在浏览器中居中显示 PDF 的效果。

以上代码中的 sample.pdf 指代 PDF 文件的 URL。你需要将其替换为你自己的 PDF 文件的 URL。

fuqiangqi commented 1 year ago

给一组pdf浏览器预览全部pdf的页面样式 pdf展示由div包裹多个span实现,样式要覆盖span的默认样式

alabulei1 commented 1 year ago

以下是一个展示多个 PDF 文件的页面样式示例,其中每个 PDF 文件都被一个 div 元素包裹,而该 div 元素下有多个 span 元素用于展示 PDF 文件的每一页。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Multiple PDF Viewer</title>
    <style>
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
      #pdf-container {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-around;
        align-items: center;
        height: 100%;
        width: 100%;
      }
      .pdf-wrapper {
        width: 80%;
        height: 80%;
        margin: 20px;
        position: relative;
      }
      .pdf-wrapper span {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
      }
      .pdf-wrapper span.canvas-container {
        overflow: hidden;
        position: relative;
      }
      .pdf-wrapper span canvas {
        display: block;
        position: absolute;
        top: 0;
        left: 0;
        transform-origin: top left;
      }
    </style>
  </head>
  <body>
    <div id="pdf-container">
      <div class="pdf-wrapper">
        <h3>PDF 1</h3>
        <span class="canvas-container">
          <canvas></canvas>
        </span>
      </div>
      <div class="pdf-wrapper">
        <h3>PDF 2</h3>
        <span class="canvas-container">
          <canvas></canvas>
        </span>
      </div>
      <div class="pdf-wrapper">
        <h3>PDF 3</h3>
        <span class="canvas-container">
          <canvas></canvas>
        </span>
      </div>
      <!--- 更多 PDF 文件区块 --->
    </div>
  </body>
</html>

在此样式中,我们使用了 flex 布局来使多个 PDF 文件在浏览器中自适应布局,并通过给 PDF 区块添加一定的 margin 来创造合适的间距。

每个 PDF 文件都被包裹在一个带有 pdf-wrapper 类的 div 元素中。我们给该元素设置了一个相对大小的宽度和高度,并将其相对定位,在其内部设置多个 span 元素来容纳 PDF 页面。我们在该元素中添加一个 h3 标题来显示 PDF 文件的名称。

对于 span 元素,我们为其添加了默认样式,并设置了 canvas 元素的位置和样式,以便使用 pdfjs-dist 库渲染 PDF 文件的页数。其中,.canvas-container 类的 span 元素用于将 canvas 元素包裹在内,从而方便在多页面 PDF 中进行滚动查看。

需要注意的是,这个样式只是一个示例,具体的样式布局还需要自行根据需求进行优化。此外,你需要额外使用 JavaScript 代码来计算 canvas 元素的尺寸并进行渲染,以便在浏览器中展示 PDF 文件的内容。

fuqiangqi commented 1 year ago

如何在 @Autowired注入多例bean的时候每次取不一样的实例对象,避免取到同一个对象

alabulei1 commented 1 year ago

对于每次获取不同的多例(prototype)bean 对象,可以使用 ObjectFactory 或者 Provider,而不是 @Autowired 注入。Spring 容器会为每个 ObjectFactory 或者 Provider 创建一个代理对象,当你调用其 getObject() 方法时,它将从 Spring 容器中获取一个新的 bean 实例并返回。

以下是使用 ObjectFactory 的代码示例:

@Service
public class MyService {

    @Autowired
    private ObjectFactory<MyPrototypeBean> myPrototypeBeanFactory;

    public void doSomething() {
        MyPrototypeBean bean = myPrototypeBeanFactory.getObject();
        // ...
    }
}

以下是使用 Provider 的代码示例:

@Service
public class MyService {

    @Autowired
    private Provider<MyPrototypeBean> myPrototypeBeanProvider;

    public void doSomething() {
        MyPrototypeBean bean = myPrototypeBeanProvider.get();
        // ...
    }
}

不管使用 ObjectFactory 还是 Provider,每次调用 getObject() 或 get() 方法时都会返回一个新的多例 bean 实例。这样,你就可以避免在 @Autowired 注入多例 bean 时,每次取到同一个实例对象的问题。

fuqiangqi commented 1 year ago

@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = ScopedProxyMode.TARGET_CLASS)

alabulei1 commented 1 year ago

@Scope 注解与 proxyMode 参数通常是一起使用的,用于将 bean 定义为多例作用域并为其创建代理。这是因为 Spring 容器无法直接注入多例 bean 的实例,因为每次注入都应该返回一个新的实例。因此,需要创建一个代理来处理每次注入时的新对象。

下面是 @Scope 注解和 proxyMode 参数的用法示例:

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyPrototypeBean {
    // ...
}

在此示例中,我们将 MyPrototypeBean 类标记为多例 bean,并将作用域范围设置为 ConfigurableBeanFactory.SCOPE_PROTOTYPE。然后,我们使用 proxyMode 参数将对象转化为代理。

ScopedProxyMode.TARGET_CLASS 是一种创建代理对象的方式。它会创建一个代理对象,代理对象和原对象具有相同的类类型,代理对象负责实现接口或基础类的方法并调用原对象。这个代理对象在每次调用时会在内部创建一个新的实例,从而实现多例的效果。

总之,使用 @Scope 注解和 proxyMode 参数可以标记一个 bean 为多例作用域,并为其创建代理对象,从而使其在每次注入时返回一个新的实例。

fuqiangqi commented 1 year ago

springboot中 多例bean对象被创建后 什么时候会被销毁