前面学习了解析PDF文档,并写入文档的知识,那篇文章的名字为深入学习Python解析并读取PDF文件内容的方法。

  链接如下:https://www.cnblogs.com/wj-1314/p/9429816.html

  但是最近出现了一个新问题,就是上面使用pdfminer这个库只能解析正常的PDF内容,然而在实际情况中,公司的一些文档可能是加密的,那么如何处理加密的PDF文件,就是本文学习的重点。

  在网上查找资料,发现pypdf2可以实现对pdf文件进行加密,解密,所以就学习了一下这个库,并留下笔记。

  首先说明pypdf2是Python3版本的,在之前的Python2版本有一个对应的pypdf库,但是本文下载了pypdf2这个库,在Python2 运行时没有报错的。

  注意:所有修改操作均无法再原文件中操作,只能将修改的结果写入新文件中。

一:PyPDF2介绍

  PyPDF2是源自pyPdf项目的纯python PDF工具包。它目前由Phaseit,Inc。维护。PyPDF2可以从PDF文件中提取数据,或者操纵现有的PDF来生成新文件。PyPDF2与Python版本2.6,2.7和3.2 - 3.5兼容。

作为PDF工具包构建的Pure-Python库。它能够:

  • 提取文档信息(标题,作者,......)
  • 逐页拆分文档
  • 逐页合并文档
  • 裁剪页面
  • 将多个页面合并为一个页面
  • 加密和解密PDF文件

  通过Pure-Python,它应该在任何Python平台上运行,而不依赖于外部库。它也可以完全在StringIO对象而不是文件流上工作,允许在内存中进行PDF操作。因此,它是管理或操作PDF的网站的有用工具。

  而本文主要学习加密解密PDF文件。

二:PyPDF2安装

2.1 下载

  在https://pypi.org/project/PyPDF2/ 中搜索PyPDF2 1.26.0可以安装包。

2.2  在Linux安装压缩包命令如下:

cd /data  && tar -xvf  PyPDF2-1.26.0.tar.gz

cd PyPDF2-1.26.0

python setup.py install

2.3 直接安装

pip install pypdf2

2.4   PyPDF的官方文档:https://pythonhosted.org/PyPDF2/

三:PyPDF 的使用目的

  首先 我这里有一个加密的PDF文件:

  那么我使用上一篇文章的代码(如下):

#coding:utf-8
import importlib
import sys
import time importlib.reload(sys)
time1 = time.time() from pdfminer.pdfparser import PDFParser, PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LTTextBoxHorizontal, LAParams
from pdfminer.pdfinterp import PDFTextExtractionNotAllowed text_path = r'5b931164edc09a226b3a12c4.pdf' def parse():
'''解析PDF文本,并保存到TXT文件中'''
fp = open(text_path, 'rb')
# 用文件对象创建一个PDF文档分析器
parser = PDFParser(fp)
# 创建一个PDF文档
doc = PDFDocument()
# 连接分析器,与文档对象
parser.set_document(doc)
doc.set_parser(parser) # 提供初始化密码,如果没有密码,就创建一个空的字符串
doc.initialize() # 检测文档是否提供txt转换,不提供就忽略
if not doc.is_extractable:
raise PDFTextExtractionNotAllowed
else:
# 创建PDF,资源管理器,来共享资源
rsrcmgr = PDFResourceManager()
# 创建一个PDF设备对象
laparams = LAParams()
device = PDFPageAggregator(rsrcmgr, laparams=laparams)
# 创建一个PDF解释其对象
interpreter = PDFPageInterpreter(rsrcmgr, device) # 循环遍历列表,每次处理一个page内容
# doc.get_pages() 获取page列表
for page in doc.get_pages():
interpreter.process_page(page)
# 接受该页面的LTPage对象
layout = device.get_result()
# 这里layout是一个LTPage对象 里面存放着 这个page解析出的各种对象
# 一般包括LTTextBox, LTFigure, LTImage, LTTextBoxHorizontal 等等
# 想要获取文本就获得对象的text属性,
for x in layout:
if (isinstance(x, LTTextBoxHorizontal)):
with open(r'2.txt', 'a') as f:
results = x.get_text()
print(results)
f.write(results + "\n") if __name__ == '__main__':
parse()
time2 = time.time()
print("总共消耗时间为:", time2 - time1)

  解析的时候,会主动触发异常(如下):

  那么,打开文件,我们会发现,实际情况是这样的:

  既然文件已经加密,那么正常渠道解析,肯定会触发异常,所以此时的重中之重就是解密PDF文件,然后再去解析即可。

  如何解密呢?  话不多说,直接看代码。

如果不知道密码,最好设置为空,这样的话 大多数就可以解析,代码如下:

# coding:utf-8
import os
from PyPDF2 import PdfFileReader
from PyPDF2 import PdfFileWriter def get_reader(filename, password):
try:
old_file = open(filename, 'rb')
print('run jiemi1')
except Exception as err:
print('文件打开失败!' + str(err))
return None # 创建读实例
pdf_reader = PdfFileReader(old_file, strict=False) # 解密操作
if pdf_reader.isEncrypted:
if password is None:
print('%s文件被加密,需要密码!' % filename)
return None
else:
if pdf_reader.decrypt(password) != 1:
print('%s密码不正确!' % filename)
return None
if old_file in locals():
old_file.close()
return pdf_reader def decrypt_pdf(filename, password, decrypted_filename=None):
"""
将加密的文件及逆行解密,并生成一个无需密码pdf文件
:param filename: 原先加密的pdf文件
:param password: 对应的密码
:param decrypted_filename: 解密之后的文件名
:return:
"""
# 生成一个Reader和Writer
print('run jiemi')
pdf_reader = get_reader(filename, password)
if pdf_reader is None:
return
if not pdf_reader.isEncrypted:
print('文件没有被加密,无需操作!')
return
pdf_writer = PdfFileWriter() pdf_writer.appendPagesFromReader(pdf_reader) if decrypted_filename is None:
decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + 'decrypted' + '.pdf' # 写入新文件
pdf_writer.write(open(decrypted_filename, 'wb')) decrypt_pdf(r'5b931164edc09a226b3a12c4.pdf', '')

  运行结果如下:

  新生成的文件如下:

  打开是这样的:

  所以 ,这样的话 就可以打开了,也可以解析了,下面继续使用PDF解析文件解析,代码是上面的,结果如下:

  解析成功,那么会保存为txt格式。

但是这里要注意,我给解密的代码,把密码设置为abc,如下:

  那么会触发异常,代码结果表示如下:

  代码如下:

# coding:utf-8
import os
from PyPDF2 import PdfFileReader
from PyPDF2 import PdfFileWriter def get_reader(filename, password):
try:
old_file = open(filename, 'rb')
print('run jiemi1')
except Exception as err:
print('文件打开失败!' + str(err))
return None # 创建读实例
pdf_reader = PdfFileReader(old_file, strict=False) # 解密操作
if pdf_reader.isEncrypted:
if password is None:
print('%s文件被加密,需要密码!' % filename)
return None
else:
if pdf_reader.decrypt(password) != 1:
print('%s密码不正确!' % filename)
return None
if old_file in locals():
old_file.close()
return pdf_reader def decrypt_pdf(filename, password, decrypted_filename=None):
"""
将加密的文件及逆行解密,并生成一个无需密码pdf文件
:param filename: 原先加密的pdf文件
:param password: 对应的密码
:param decrypted_filename: 解密之后的文件名
:return:
"""
# 生成一个Reader和Writer
print('run jiemi')
pdf_reader = get_reader(filename, password)
if pdf_reader is None:
return
if not pdf_reader.isEncrypted:
print('文件没有被加密,无需操作!')
return
pdf_writer = PdfFileWriter() pdf_writer.appendPagesFromReader(pdf_reader) if decrypted_filename is None:
decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + 'decrypted' + '.pdf' # 写入新文件
pdf_writer.write(open(decrypted_filename, 'wb')) decrypt_pdf(r'5b931164edc09a226b3a12c4.pdf', 'abc')

四:PyPDF2的理论介绍

  PyPDF2 包含了 PdfFileReader PdfFileMerger PageObject PdfFileWriter 四个常用的主要 Class。

具体分析:

PyPDF2 将读与写分成两个类来操作:

from PyPDF2 import PdfFileWriter, PdfFileReader

writer = PdfFileWriter()
reader = PdfFileReader(open("document1.pdf", "rb"))

官方实例:

from PyPDF2 import PdfFileWriter, PdfFileReader

output = PdfFileWriter()
input1 = PdfFileReader(open("document1.pdf", "rb")) # print how many pages input1 has:
print "document1.pdf has %d pages." % input1.getNumPages() # add page 1 from input1 to output document, unchanged
output.addPage(input1.getPage(0)) # add page 2 from input1, but rotated clockwise 90 degrees
output.addPage(input1.getPage(1).rotateClockwise(90)) # add page 3 from input1, rotated the other way:
output.addPage(input1.getPage(2).rotateCounterClockwise(90))
# alt: output.addPage(input1.getPage(2).rotateClockwise(270)) # add page 4 from input1, but first add a watermark from another PDF:
page4 = input1.getPage(3)
watermark = PdfFileReader(open("watermark.pdf", "rb"))
page4.mergePage(watermark.getPage(0))
output.addPage(page4) # add page 5 from input1, but crop it to half size:
page5 = input1.getPage(4)
page5.mediaBox.upperRight = (
page5.mediaBox.getUpperRight_x() / 2,
page5.mediaBox.getUpperRight_y() / 2
)
output.addPage(page5) # add some Javascript to launch the print window on opening this PDF.
# the password dialog may prevent the print dialog from being shown,
# comment the the encription lines, if that's the case, to try this out
output.addJS("this.print({bUI:true,bSilent:false,bShrinkToFit:true});") # encrypt your new PDF and add a password
password = "secret"
output.encrypt(password) # finally, write "output" to document-output.pdf
outputStream = file("PyPDF2-output.pdf", "wb")
output.write(outputStream)

五 :PdfFileReader类

class PyPDF2.PdfFileReader(stream,strict = True,warndest = None,
overwriteWarnings = True )

初始化PdfFileReader对象。此操作可能需要一些时间,因为PDF流的交叉引用表被读入内存。

参数:

stream - File对象或支持类似于File对象的标准读取和搜索方法的对象。也可以是表示PDF文件路径的字符串。

  • strictbool) - 确定是否应该警告用户所有问题并且还会导致一些可纠正的问题致命。默认为True
  • warndest - 记录警告的目的地(默认为 sys.stderr)。
  • overwriteWarningsbool) - 确定是否warnings.py使用自定义实现覆盖Python的 模块(默认为 True)。

decrypt(密码)

  使用带有PDF标准加密处理程序的加密/安全PDF文件时,此功能将允许解密文件。它根据文档的用户密码和所有者密码检查给定的密码,如果密码正确,则存储生成的解密密钥。

  哪个密码匹配无关紧要。两个密码都提供了正确的解密密钥,允许文档与此库一起使用。

参数:password(str) 要匹配的密码

  返回0如果密码失败,1密码是否与用户密码匹配,密码2是否与所有者密码匹配。

  返回类型: INT

  引发NotImplementedError:如果文档使用不受支持的加密方法。

documentInfo

  访问给定Destination对象的页码

getDestinationPageNumber(destination)

  检索PDF文件的文档信息字典(如果存在)。请注意,某些PDF文件使用元数据流而不是docinfo词典,此功能不会访问这些元数据流。

  返回:页码或者如果找不到页面的话 则为-1

  返回类型:INT

getDocumentInfo()

  检索PDF文件的文档信息字典(如果存在)。请注意,某些PDF文件使用元数据流而不是docinfo词典,此功能不会访问这些元数据流。

  返回:该PDF文件的文档信息

  返回类型DocumentInformation或者None如果不存在。

getFieldstree = Noneretval = Nonefileobj = None 

  如果此PDF包括交互式表单字段,则提取字段数据,该树和retval的参数是递归使用。

  参数:fileobj  用于在找到的所有交互式表单字段上写入报告的文件对象(通常是文本文件)

  返回:一个字典,其中每个键是一个字段名称,每个值都是一个个Field对象。默认情况下,映射名称用于键。

  返回类型:dict  或者None无法找到表单数据。

getFormTextFields()

  使用文本数据从文档中检索表单域(输入,下拉列表)

getNameDestinations(tree=None,retval=None)

  检索文档中存在的指定目标

  返回:将名称映射到的字典 Destinations

  返回类型:字典

getNumPages()

  计算此PDF文件中的页面。

  返回:页面

  返回类型:INT

  引发PDFReadError:如果文件已加密且限制阻止此操作。

getOutlines(node=None,outlines=None)

  检查文档中存在的文档大纲。

  返回:一个PageObject实例。

  返回类型PageObject

getPageLayout()

  获取页面布局,有关setPageLayout() 有效布局的说明,请参阅参考资料。

  返回:目前正在使用的页面布局

  返回类型:str None如果没有指定。

getPageMode()

  获取页面布局,有关setPageMode() 有效模式的说明,请参阅。

  返回:目前正在使用的页面模式。

  返回类型strNone如果没有指定。

getPageNumber()

  检索给定PageObject的页面。

  参数:page(PageObject) - 获取页码的页面。应该是一个实例PageObject

  返回:页码或如果找不到页面,则为-1

  返回类型:INT

getXmpMetadata()

  从PDF文档跟目录中检索XMP(可扩展元数据平台)数据。

  返回XmpInformation 可用于从文档访问XMP元数据的实例

  返回类型XmpInformation或者 None如果在文档根目录中未找到元数据。

isEncrypted

  只读布尔属性,显示此PDF文件是否已加密。请注意,即使decrypt()调用该方法,此属性(如果为true)仍将保持为true 。

namedDestinations

  访问该getNamedDestinations()函数的只读属性 。

numPages

  访问该getNumPages()函数的只读属性 。

outlines

  只读属性访问getOutlines() 功能。

pageLayout

  访问该getPageLayout()方法的只读属性 。

pageMode

  访问该getPageMode()方法的只读属性 。

pages

  只读属性,它根据getNumPages()和 getPage()方法模拟列表 。

xmpMetadata

访问该getXmpMetadata()函数的只读属性 。

PDF读取操作:

# encoding:utf-8
from PyPDF2 import PdfFileReader, PdfFileWriter readFile = 'C:/ learn.pdf'
# 获取 PdfFileReader 对象
pdfFileReader = PdfFileReader(readFile)
# 或者这个方式:pdfFileReader = PdfFileReader(open(readFile, 'rb'))
# 获取 PDF 文件的文档信息
documentInfo = pdfFileReader.getDocumentInfo()
print('documentInfo = %s' % documentInfo)
# 获取页面布局
pageLayout = pdfFileReader.getPageLayout()
print('pageLayout = %s ' % pageLayout) # 获取页模式
pageMode = pdfFileReader.getPageMode()
print('pageMode = %s' % pageMode) xmpMetadata = pdfFileReader.getXmpMetadata()
print('xmpMetadata = %s ' % xmpMetadata) # 获取 pdf 文件页数
pageCount = pdfFileReader.getNumPages() print('pageCount = %s' % pageCount)
for index in range(0, pageCount):
# 返回指定页编号的 pageObject
pageObj = pdfFileReader.getPage(index)
print('index = %d , pageObj = %s' % (index, type(pageObj)))
# <class 'PyPDF2.pdf.PageObject'>
# 获取 pageObject 在 PDF 文档中处于的页码
pageNumber = pdfFileReader.getPageNumber(pageObj)
print('pageNumber = %s ' % pageNumber)

六: PDFFileWriter类

  这个类支持PDF文件,给出其他类生成的页面。

属性和方法 描述
addAttachment(fname,fdata) 在 PDF 中嵌入文件
addBlankPage(width= None,height=None) 追加一个空白页面到这个 PDF 文件并返回它
addBookmark(title,pagenum,parent=None,
color=None,bold=False,italic=False,fit=’/fit,*args’)
 
addJS(javascript) 添加将在打开此 PDF 是启动的 javascript
addLink(pagenum,pagedest,rect,border=None,fit=’/fit’,*args) 从一个矩形区域添加一个内部链接到指定的页面
addPage(page) 添加一个页面到这个PDF 文件,该页面通常从 PdfFileReader 实例获取
getNumpages() 页数
getPage(pageNumber) 从这个 PDF 文件中检索一个编号的页面
insertBlankPage(width=None,height=None,index=0) 插入一个空白页面到这个 PDF 文件并返回它,如果没有指定页面大小,就使用最后一页的大小
insertPage(page,index=0) 在这个 PDF 文件中插入一个页面,该页面通常从 PdfFileReader 实例获取
removeLinks() 从次数出中删除连接盒注释
removeText(ignoreByteStringObject = False) 从这个输出中删除图像
write(stream) 将添加到此对象的页面集合写入 PDF 文件

PDF写入操作

def addBlankpage():
readFile = 'C:/study.pdf'
outFile = 'C:/copy.pdf'
pdfFileWriter = PdfFileWriter() # 获取 PdfFileReader 对象
pdfFileReader = PdfFileReader(readFile) # 或者这个方式:pdfFileReader = PdfFileReader(open(readFile, 'rb'))
numPages = pdfFileReader.getNumPages() for index in range(0, numPages):
pageObj = pdfFileReader.getPage(index)
pdfFileWriter.addPage(pageObj) # 根据每页返回的 PageObject,写入到文件
pdfFileWriter.write(open(outFile, 'wb')) pdfFileWriter.addBlankPage() # 在文件的最后一页写入一个空白页,保存至文件中
pdfFileWriter.write(open(outFile,'wb'))

  结果是:在写入的 copy.pdf 文档的最后最后一页写入了一个空白页。

分割文档(取第五页之后的页面)

def splitPdf():
readFile = 'C:/learn.pdf'
outFile = 'C://copy.pdf'
pdfFileWriter = PdfFileWriter() # 获取 PdfFileReader 对象
pdfFileReader = PdfFileReader(readFile)
# 或者这个方式:pdfFileReader = PdfFileReader(open(readFile, 'rb'))
# 文档总页数
numPages = pdfFileReader.getNumPages() if numPages > 5:
# 从第五页之后的页面,输出到一个新的文件中,即分割文档
for index in range(5, numPages):
pageObj = pdfFileReader.getPage(index)
pdfFileWriter.addPage(pageObj)
# 添加完每页,再一起保存至文件中
pdfFileWriter.write(open(outFile, 'wb'))

合并文档

def mergePdf(inFileList, outFile):
'''
合并文档
:param inFileList: 要合并的文档的 list
:param outFile: 合并后的输出文件
:return:
'''
pdfFileWriter = PdfFileWriter()
for inFile in inFileList:
# 依次循环打开要合并文件
pdfReader = PdfFileReader(open(inFile, 'rb'))
numPages = pdfReader.getNumPages()
for index in range(0, numPages):
pageObj = pdfReader.getPage(index)
pdfFileWriter.addPage(pageObj) # 最后,统一写入到输出文件中
pdfFileWriter.write(open(outFile, 'wb'))

PageObject类

class PyPDF2.pdf.PageObject(pdf = None,indirectRef = None )

此类表示 PDF 文件中的单个页面,通常这个对象是通过访问 PdfFileReader 对象的 getPage() 方法来得到的,也可以使用 createBlankPage() 静态方法创建一个空的页面。

参数:

  • pdf : 页面所属的 PDF 文件。
  • indirectRef:将源对象的原始间接引用存储在其源 PDF 中。

PageObject 对象的属性和方法

属性或方法 描述
static createBlankPage(pdf=None,width=None,height=None) 返回一个新的空白页面
extractText() 找到所有文本绘图命令,按照他们在内容流中提供的顺序,并提取文本
getContents() 访问页面内容,返回 Contents 对象或 None
rotateClockwise(angle) 顺时针旋转 90 度
scale(sx,sy) 通过向其内容应用转换矩阵并更新页面大小

粗略读取PDF文本内容

def getPdfContent(filename):
pdf = PdfFileReader(open(filename, "rb"))
content = ""
for i in range(0, pdf.getNumPages()):
pageObj = pdf.getPage(i) extractedText = pageObj.extractText()
content += extractedText + "\n"
# return content.encode("ascii", "ignore")
return content

深入学习Python解析并解密PDF文件内容的方法的更多相关文章

  1. 深入学习python解析并读取PDF文件内容的方法

    这篇文章主要学习了python解析并读取PDF文件内容的方法,包括对学习库的应用,python2.7和python3.6中python解析PDF文件内容库的更新,包括对pdfminer库的详细解释和应 ...

  2. python解析VOC的xml文件并转成自己需要的txt格式

    在进行神经网络训练的时候,自己标注的数据集往往会有数据量不够大以及代表性不强等问题,因此我们会采用开源数据集作为训练,开源数据集往往具有特定的格式,如果我们想将开源数据集为我们所用的话,就需要对其格式 ...

  3. 利用Python将多个PDF文件合并

    from PyPDF2 import PdfFileMerger import os files = os.listdir()#列出目录中的所有文件 merger = PdfFileMerger() ...

  4. robotframework 测试工具添加PDF文件内容匹配插件

    robotframework  这个需要了解的请度娘.本文实现的是一个小功能.大体分为如下几个步骤 1)给定一个pdf文件. 2)读取pdf文件内容,并解析为文本内容. 3)通过给定的内容,比对pdf ...

  5. 如何修改PDF文件内容,PDF怎么添加背景

    很多的情况下,大家都会遇到PDF文件,不管是在学习中还是在工作中,对于PDF文件,文件的修改编辑是需要用到PDF编辑软件的,在编辑文件的时候,发现文件的页面是有背景颜色的,又该如何修改背景颜色呢,不会 ...

  6. pdf文件内容查看器 -- 采用wpf开发

    前言 pdf是一种应用非常广的版式文档格式,已成为事实上的国际标准.关于pdf格式的文章汗牛充栋,本文也是关于pdf格式的文章,但是本文不是纸上谈兵:本人这几周一直研究pdf格式内容,不但对pfd格式 ...

  7. 怎么编辑PDF文件内容,PDF文件编辑方法

    怎样编辑PDF文件内容?这是一个常常困扰我们的问题,工作当中我们经常会收到PDF格式的文件,但有时的文件内容不是我们想要的或者是觉得不合理的需要改掉.但是每次有这样的问题时都没有什么好的解决方法,每次 ...

  8. 编辑方法分享之如何编辑PDF文件内容

    我们现在在工作中会经常使用到PDF文件,还会有遇到需要编辑PDF文件的时候,PDF文件的编辑问题一直是个大难题.很多朋友在面对PDF文件的时候束手无策,不知道该怎么对它进行编辑.下面小编就教给大家一个 ...

  9. linux几种快速清空文件内容的方法

    linux几种快速清空文件内容的方法 几种快速清空文件内容的方法: $ : > filename #其中的 : 是一个占位符, 不产生任何输出. $ > filename $ echo & ...

随机推荐

  1. Oracle 忘记sys与system管理员密码重置操作

    首先打开cmd 执行 orapwd file=C:\app\PWDorcl.ora password=orclorcl C:\app\PWDorcl.ora是你要存放的路径文件 Password=or ...

  2. python小结 1

    1.变量 记录状态 类型:数字,字符串,元组,列表,字典 可变不可变(内存地址不变的情况下,值能不能改变): 不可变:字符串,数字,元组 可变:列表,字典 访问顺序: 直接访问:数字 有序:字符串,列 ...

  3. 必须知道的Spring Boot中的一些Controller注解

    这篇文章是抄其他人的,原址:https://cloud.tencent.com/developer/article/1082720 本文旨在向你介绍在Spring Boot中controller中最基 ...

  4. Android单片机与蓝牙模块通信实例代码

    Android单片机与蓝牙模块通信实例代码 参考路径:http://www.jb51.net/article/83349.htm 啦啦毕业了,毕业前要写毕业设计,需要写一个简单的蓝牙APP进行交互,通 ...

  5. Java:ConcurrentHashMap

    ConcurrentHashMap的目的 多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap.虽然已经有一个线程安全的Ha ...

  6. python中None与0、Null、false区别

    None是Python中的一个关键字,None本身也是个一个数据类型,而这个数据类型就是None,它可0.空字符串以及false均不一样,这些都只是对象,而None也是一个类. 给个bool测试: v ...

  7. javascript字符串方法总结

    一.单引号字符串内部可以使用双引号,双引号字符串内部也可以使用单引号 "hello 'world'" 'welcome "to" js' 二.多行和转义 如果要 ...

  8. ABP框架系列之三十三:(Module-System-模块系统)

    Introduction ASP.NET Boilerplate provides an infrastructure to build modules and compose them to cre ...

  9. (PMP)第9章-----项目资源管理

    9.1 规划资源管理 数据表现: 1.层级型(高层次的角色):工作分解结构,组织分解结构,资源分解结构 2.责任分配矩阵:RAM,RACI,执行,负责,咨询,知情(只有一个A) 3.文本型(记录详细职 ...

  10. Photoshop制作仿等高线着色图

    起因是最近玩游戏The Long Dark,看到贴吧还是Steam上有人放了等高线图,看起来非常炫酷,于是想自己折腾下. 解包了游戏高度图 Matlab绘制如下 自己瞎写的量化+颜色映射如下,Shad ...