如何在 google docs / google apps 脚本中删除 pdf 导出的白色背景层?
How can I remove the white background layers of a pdf export in google docs / google apps script?
我遇到了 google 应用程序脚本/google 文档的问题。我想制作一个插件,用 API 向 google docs 文档添加水印。效果很好,但前提是我将水印放在文档前面。如果我把它放在文件后面,水印是不可见的。然后我试着查看 LibreOffice Draw 中的不同图层,我看到 google 文档在文本后面放置了一些白色图层,这些图层在水印上方。
那么有人知道吗,我如何在没有白色图层的情况下从 google 文档中导出 pdf 文件?
我还尝试通过 google apps 脚本将页面背景设置为 null 或 rgba(0,0,0,0.0),如下所示:
var highlightStyle = {};
highlightStyle[DocumentApp.Attribute.BACKGROUND_COLOR] = "rgba(0,0,0,0.0)";
let editedText = DocumentApp.getActiveDocument().getBody().editAsText().setAttributes(highlightStyle);
但是没有用。
Here is a test document,您可以在其中看到背景中的白色图层,就像在自由办公抽屉中一样。
在此先致谢。
这里是 google 应用程序脚本插件的代码:
function base64Encode(str) {
var encoded = Utilities.base64EncodeWebSafe(Utilities.newBlob(str).getBytes());
return encoded.replace(/=+$/, '');
};
function encodeJWT(public, secret) {
var header = JSON.stringify({
typ: 'JWT',
alg: 'HS256',
jti: public
});
var encodedHeader = base64Encode(header);
var iat = new Date().getTime() / 1000 - 60;
var payload = JSON.stringify({
iat: iat,
iss: 'name',
jti: public,
public_key: public
});
var encodedPayload = base64Encode(payload);
var toSign = [encodedHeader, encodedPayload].join('.');
var signature = Utilities.computeHmacSha256Signature(toSign, secret);
var encodedSignature = base64Encode(signature);
return [toSign, encodedSignature].join('.');
};
function onInstall(){
onOpen();
}
function onOpen(){
let menu = DocumentApp.getUi().createAddonMenu().addItem("Merge PDFs", "showSidebar").addToUi();
}
let folderName = "watermark data";
let fileName = "watermark template";
function showSidebar(){
var html = HtmlService.createTemplateFromFile("index").evaluate().setTitle("PDF Creator");
DocumentApp.getUi().showSidebar(html);
}
function mergePdf(transparency){
var highlightStyle = {};
highlightStyle[DocumentApp.Attribute.BACKGROUND_COLOR] = "#ffffff";
let editedText = DocumentApp.getActiveDocument().getBody().editAsText().setAttributes(highlightStyle);
let folders = DriveApp.getFoldersByName(folderName);
if(!folders.hasNext()){
DriveApp.getRootFolder().createFolder(folderName)
}
let files = DriveApp.getFilesByName(fileName);
if(files.hasNext()){
let header;
if(DocumentApp.getActiveDocument().getHeader()){
header = DocumentApp.getActiveDocument().getHeader();
}else{
header = DocumentApp.getActiveDocument().addHeader();
}
}
fetchAPIData("https://api.ilovepdf.com/v1/auth", "post", {
"public_key": "api_key"
});
let startData = JSON.parse(fetchAPIData("https://api.ilovepdf.com/v1/start/watermark", "get"));
let uploadData1 = JSON.parse(requestAPI(convertPDF(), "https://" + startData.server + "/v1/upload", {
"task": startData.task
}));
let file = DriveApp.getFilesByName(fileName + ".png");
let file2 = DriveApp.getFilesByName(fileName + ".jpg");
let file3 = DriveApp.getFilesByName(fileName + ".jpeg");
if(file.hasNext()){
file = file.next().getId();
}else if(file2.hasNext()){
file = file2.next().getId();
}else if(file3.hasNext()){
file = file3.next().getId();
}else{
DocumentApp.getUi().alert("Error, watermark not found!\n Please upload a watermark!");
}
let uploadData2 = JSON.parse(requestAPI(file, "https://" + startData.server + "/v1/upload", {
"task": startData.task
}));
// DocumentApp.getUi().alert(typeof transparency);
let processResponse = JSON.parse(fetchAPIData("https://" + startData.server + "/v1/process", "post",{
"task": startData.task,
"tool": "watermark",
"files": [
{
"server_filename": uploadData1.server_filename,
"filename": "Test"
}
],
"mode": "image",
"layer": "below",
"image": uploadData2.server_filename,
"transparency": transparency
}));
if(processResponse.status == "TaskSuccess"){
let responseData = fetchAPIData("https://" + startData.server + "/v1/download/" + startData.task, "get");
if(responseData){
file = DriveApp.getRootFolder().createFile(responseData.getAs('application/pdf'));
file.setName(DocumentApp.getActiveDocument().getName());
DocumentApp.getUi().alert('You can download your pdf file here: ' + file.getDownloadUrl());
var actionResponse = CardService.newActionResponseBuilder()
.setOpenLink(CardService.newOpenLink()
.setUrl(file.getDownloadUrl())
.setOpenAs(CardService.OpenAs.FULL_SIZE)
.setOnClose(CardService.OnClose.NOTHING))
.build();
}else{
DocumentApp.getUi().alert("Error, the API Request wasn't successful!");
}
}
}
function fetchAPIData(route, method, data){
var options = {method: method};
if(data){
options = {
'method' : method,
'headers': {
'Authorization': 'Bearer ' + encodeJWT("api_key", "api_key")
},
'contentType': 'application/json',
// Convert the JavaScript object to a JSON string.
'payload' : JSON.stringify(data)
};
}else{
options = {
'method' : method,
'headers': {
'Authorization': 'Bearer ' + encodeJWT("api_key", "api_key")
}
};
}
var response = UrlFetchApp.fetch(route, options);
return response;
}
function requestAPI(fileId, url, metadata) {
var file = DriveApp.getFileById(fileId);
var boundary = "name";
var data = "";
for (var i in metadata) {
data += "--" + boundary + "\r\n";
data += "Content-Disposition: form-data; name=\"" + i + "\"; \r\n\r\n" + metadata[i] + "\r\n";
}
data += "--" + boundary + "\r\n";
data += "Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"\r\n";
data += "Content-Type:" + file.getMimeType() + "\r\n\r\n";
var payload = Utilities.newBlob(data).getBytes()
.concat(file.getBlob().getBytes())
.concat(Utilities.newBlob("\r\n--" + boundary + "--").getBytes());
var options = {
method : "post",
contentType : "multipart/form-data; boundary=" + boundary,
payload : payload,
muteHttpExceptions: true,
'headers': {
'Authorization': 'Bearer ' + encodeJWT("api_key", "api_key")
}
};
var res = UrlFetchApp.fetch(url, options).getContentText();
Logger.log(res);
return res;
}
function convertPDF() {
doc = DocumentApp.getActiveDocument();
var ui = DocumentApp.getUi();
docblob = DocumentApp.getActiveDocument().getAs('application/pdf');
/* Add the PDF extension */
docblob.setName(doc.getName() + ".pdf");
let file = DriveApp.getFoldersByName(folderName);
if(!file.hasNext()){
DriveApp.getRootFolder().createFolder(folderName)
}
var files = DriveApp.getFilesByName(doc.getName() + ".pdf");
while (files.hasNext()) {
files.next().setTrashed(true);
}
file = DriveApp.getFoldersByName(folderName).next().createFile(docblob);
return file.getId();
}
function saveFile(obj) {
var files = DriveApp.getFilesByName(fileName + ".jpg");
while (files.hasNext()) {
files.next().setTrashed(true);
}
files = DriveApp.getFilesByName(fileName + ".png");
while (files.hasNext()) {
files.next().setTrashed(true);
}
var blob = Utilities.newBlob(Utilities.base64Decode(obj.data), obj.mimeType, fileName + "." + obj.mimeType.split("/")[1]);
DocumentApp.getUi().alert("Watermark is uploaded successfully!");
return DriveApp.getFoldersByName(folderName).next().createFile(blob).getId();
}
我用的水印api是ilovepdfapi
我最终手动删除了背景:
使用任何合适的工具解压缩 PDF(我使用 pdftk
)
pdftk source.pdf output decompressed.pdf decompress
使用任何手动或自动文本编辑器编辑解压缩的 PDF(我使用 Vim)
您正在查找如下所示的代码块:
1 1 1 RG 1 1 1 rg
/G3 gs
0 1123 794 1123 re
f
0 1123 794 1123 re
f
0 0 794 6738 re
f
即设置背景色为白色1 1 1 rg
然后绘制一个或多个大矩形re
填充背景f
您想删除或评论 (%
) 所有这些行
使用 PDF 查看器检查文件并可选择重新压缩它:
pdftk edited.pdf output final.pdf compress
这里是 link 到 PDF language reference。只需使用字母索引查找运算符,例如 re
和 f
,了解它们的作用。
我遇到了 google 应用程序脚本/google 文档的问题。我想制作一个插件,用 API 向 google docs 文档添加水印。效果很好,但前提是我将水印放在文档前面。如果我把它放在文件后面,水印是不可见的。然后我试着查看 LibreOffice Draw 中的不同图层,我看到 google 文档在文本后面放置了一些白色图层,这些图层在水印上方。
那么有人知道吗,我如何在没有白色图层的情况下从 google 文档中导出 pdf 文件?
我还尝试通过 google apps 脚本将页面背景设置为 null 或 rgba(0,0,0,0.0),如下所示:
var highlightStyle = {};
highlightStyle[DocumentApp.Attribute.BACKGROUND_COLOR] = "rgba(0,0,0,0.0)";
let editedText = DocumentApp.getActiveDocument().getBody().editAsText().setAttributes(highlightStyle);
但是没有用。
Here is a test document,您可以在其中看到背景中的白色图层,就像在自由办公抽屉中一样。
在此先致谢。
这里是 google 应用程序脚本插件的代码:
function base64Encode(str) {
var encoded = Utilities.base64EncodeWebSafe(Utilities.newBlob(str).getBytes());
return encoded.replace(/=+$/, '');
};
function encodeJWT(public, secret) {
var header = JSON.stringify({
typ: 'JWT',
alg: 'HS256',
jti: public
});
var encodedHeader = base64Encode(header);
var iat = new Date().getTime() / 1000 - 60;
var payload = JSON.stringify({
iat: iat,
iss: 'name',
jti: public,
public_key: public
});
var encodedPayload = base64Encode(payload);
var toSign = [encodedHeader, encodedPayload].join('.');
var signature = Utilities.computeHmacSha256Signature(toSign, secret);
var encodedSignature = base64Encode(signature);
return [toSign, encodedSignature].join('.');
};
function onInstall(){
onOpen();
}
function onOpen(){
let menu = DocumentApp.getUi().createAddonMenu().addItem("Merge PDFs", "showSidebar").addToUi();
}
let folderName = "watermark data";
let fileName = "watermark template";
function showSidebar(){
var html = HtmlService.createTemplateFromFile("index").evaluate().setTitle("PDF Creator");
DocumentApp.getUi().showSidebar(html);
}
function mergePdf(transparency){
var highlightStyle = {};
highlightStyle[DocumentApp.Attribute.BACKGROUND_COLOR] = "#ffffff";
let editedText = DocumentApp.getActiveDocument().getBody().editAsText().setAttributes(highlightStyle);
let folders = DriveApp.getFoldersByName(folderName);
if(!folders.hasNext()){
DriveApp.getRootFolder().createFolder(folderName)
}
let files = DriveApp.getFilesByName(fileName);
if(files.hasNext()){
let header;
if(DocumentApp.getActiveDocument().getHeader()){
header = DocumentApp.getActiveDocument().getHeader();
}else{
header = DocumentApp.getActiveDocument().addHeader();
}
}
fetchAPIData("https://api.ilovepdf.com/v1/auth", "post", {
"public_key": "api_key"
});
let startData = JSON.parse(fetchAPIData("https://api.ilovepdf.com/v1/start/watermark", "get"));
let uploadData1 = JSON.parse(requestAPI(convertPDF(), "https://" + startData.server + "/v1/upload", {
"task": startData.task
}));
let file = DriveApp.getFilesByName(fileName + ".png");
let file2 = DriveApp.getFilesByName(fileName + ".jpg");
let file3 = DriveApp.getFilesByName(fileName + ".jpeg");
if(file.hasNext()){
file = file.next().getId();
}else if(file2.hasNext()){
file = file2.next().getId();
}else if(file3.hasNext()){
file = file3.next().getId();
}else{
DocumentApp.getUi().alert("Error, watermark not found!\n Please upload a watermark!");
}
let uploadData2 = JSON.parse(requestAPI(file, "https://" + startData.server + "/v1/upload", {
"task": startData.task
}));
// DocumentApp.getUi().alert(typeof transparency);
let processResponse = JSON.parse(fetchAPIData("https://" + startData.server + "/v1/process", "post",{
"task": startData.task,
"tool": "watermark",
"files": [
{
"server_filename": uploadData1.server_filename,
"filename": "Test"
}
],
"mode": "image",
"layer": "below",
"image": uploadData2.server_filename,
"transparency": transparency
}));
if(processResponse.status == "TaskSuccess"){
let responseData = fetchAPIData("https://" + startData.server + "/v1/download/" + startData.task, "get");
if(responseData){
file = DriveApp.getRootFolder().createFile(responseData.getAs('application/pdf'));
file.setName(DocumentApp.getActiveDocument().getName());
DocumentApp.getUi().alert('You can download your pdf file here: ' + file.getDownloadUrl());
var actionResponse = CardService.newActionResponseBuilder()
.setOpenLink(CardService.newOpenLink()
.setUrl(file.getDownloadUrl())
.setOpenAs(CardService.OpenAs.FULL_SIZE)
.setOnClose(CardService.OnClose.NOTHING))
.build();
}else{
DocumentApp.getUi().alert("Error, the API Request wasn't successful!");
}
}
}
function fetchAPIData(route, method, data){
var options = {method: method};
if(data){
options = {
'method' : method,
'headers': {
'Authorization': 'Bearer ' + encodeJWT("api_key", "api_key")
},
'contentType': 'application/json',
// Convert the JavaScript object to a JSON string.
'payload' : JSON.stringify(data)
};
}else{
options = {
'method' : method,
'headers': {
'Authorization': 'Bearer ' + encodeJWT("api_key", "api_key")
}
};
}
var response = UrlFetchApp.fetch(route, options);
return response;
}
function requestAPI(fileId, url, metadata) {
var file = DriveApp.getFileById(fileId);
var boundary = "name";
var data = "";
for (var i in metadata) {
data += "--" + boundary + "\r\n";
data += "Content-Disposition: form-data; name=\"" + i + "\"; \r\n\r\n" + metadata[i] + "\r\n";
}
data += "--" + boundary + "\r\n";
data += "Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"\r\n";
data += "Content-Type:" + file.getMimeType() + "\r\n\r\n";
var payload = Utilities.newBlob(data).getBytes()
.concat(file.getBlob().getBytes())
.concat(Utilities.newBlob("\r\n--" + boundary + "--").getBytes());
var options = {
method : "post",
contentType : "multipart/form-data; boundary=" + boundary,
payload : payload,
muteHttpExceptions: true,
'headers': {
'Authorization': 'Bearer ' + encodeJWT("api_key", "api_key")
}
};
var res = UrlFetchApp.fetch(url, options).getContentText();
Logger.log(res);
return res;
}
function convertPDF() {
doc = DocumentApp.getActiveDocument();
var ui = DocumentApp.getUi();
docblob = DocumentApp.getActiveDocument().getAs('application/pdf');
/* Add the PDF extension */
docblob.setName(doc.getName() + ".pdf");
let file = DriveApp.getFoldersByName(folderName);
if(!file.hasNext()){
DriveApp.getRootFolder().createFolder(folderName)
}
var files = DriveApp.getFilesByName(doc.getName() + ".pdf");
while (files.hasNext()) {
files.next().setTrashed(true);
}
file = DriveApp.getFoldersByName(folderName).next().createFile(docblob);
return file.getId();
}
function saveFile(obj) {
var files = DriveApp.getFilesByName(fileName + ".jpg");
while (files.hasNext()) {
files.next().setTrashed(true);
}
files = DriveApp.getFilesByName(fileName + ".png");
while (files.hasNext()) {
files.next().setTrashed(true);
}
var blob = Utilities.newBlob(Utilities.base64Decode(obj.data), obj.mimeType, fileName + "." + obj.mimeType.split("/")[1]);
DocumentApp.getUi().alert("Watermark is uploaded successfully!");
return DriveApp.getFoldersByName(folderName).next().createFile(blob).getId();
}
我用的水印api是ilovepdfapi
我最终手动删除了背景:
使用任何合适的工具解压缩 PDF(我使用
pdftk
)pdftk source.pdf output decompressed.pdf decompress
使用任何手动或自动文本编辑器编辑解压缩的 PDF(我使用 Vim)
您正在查找如下所示的代码块:
1 1 1 RG 1 1 1 rg /G3 gs 0 1123 794 1123 re f 0 1123 794 1123 re f 0 0 794 6738 re f
即设置背景色为白色
1 1 1 rg
然后绘制一个或多个大矩形re
填充背景f
您想删除或评论 (
%
) 所有这些行使用 PDF 查看器检查文件并可选择重新压缩它:
pdftk edited.pdf output final.pdf compress
这里是 link 到 PDF language reference。只需使用字母索引查找运算符,例如 re
和 f
,了解它们的作用。