PDFBOX详解
PDFBOX详解
摘要
自从Adobe公司1993年第一次发布公共PDF参考以来,支持各种语言和平台的PDF工具和类库就如雨后春笋般涌现。然而,Java应用开发中Adobe技术的支持相对滞后了。
自从Adobe公司1993年第一次发布公共PDF参考以来,支持各种语言和平台的PDF工具和类库就如雨后春笋般涌现。然而,Java应用开发中Adobe技术的支持相对滞后了。这是个奇怪的现象,因为PDF文档是企业信息系统存储和交换信息的大势所趋,而Java技术特别适合这种应用。然而,Java开发人员似乎直到最近才获得成熟可用的PDF支持。
PDFBox(一个BSD许可下的源码开放项目)是一个为开发人员读取和创建PDF文档而准备的纯Java类库。它提供如下特性:
- 提取文本,包括Unicode字符。
- 和Jakarta Lucene等文本搜索引擎的整合过程十分简单。
- 加密/解密PDF文档。
- 从PDF和XFDF格式中导入或导出表单数据。
- 向已有PDF文档中追加内容。
- 将一个PDF文档切分为多个文档。
- 覆盖PDF文档。
PDFBox API
PDFBox设计时采用面向对象的方式来描述PDF文档。PDF文档的数据是一系列基本对象的集合:数组,布尔型,字典,数字,字符串和二进制流。PDFBox在org.pdfbox.cos包(COS模型)中定义这些基本对象类型。你可以使用这些对象与PDF文档进行任何交互,但你应该先对PDF文档内部结构以及高层概念作一些深入的了解。例如,页面和字体都是带有特殊属性的字典对象;PDF参考手册提供这些特殊属性的含义和类型的说明,但这是一个枯燥的文档查阅过程。
于是,org.pdfbox.pdfmodel包(PD模型)应运而生,它的基础是COS模型,但提供了以一种熟悉的方式访问PDF文档对象的高层API(如图1)。对底层COS模型进行了封装的PDPage和PDFont等类就在这个包中。
注意,虽然PD模型提供了一些优秀的功能,但它依然是一个开发中的模型。在有些实例中,你可能需要借助于COS模型才能访问PDF的特定功能性。所有的PD模型对象都提供返回相应的COS模型对象的方法。所以,在一般情况下,你都会使用PD模型,但PD模型鞭长莫及时你可以直接操作底层的COS模型。
上文对PDFBox作了大体上的介绍,现在是举一些例子的时候了。我们从如何读已存在的PDF文档开始:
PDDocument document =
PDDocument.load( "./test.pdf" );
上面的语句解析指定的PDF文件并在内存中创建其文档对象。考虑到处理大文档时的效率问题,PDFBox只在内存中存储文档结构,图像、内嵌字体和页面内容等对象将被缓存在一个临时文件中。
注意:PDDocument对象使用完毕时需要调用其close()方法来释放创建时使用的资源。
文本提取和Lucene整合
这是一个信息展现时代(an information retrieval age),不管信息存放在哪种媒体中,应用程序都应该支持检索和索引。对信息进行组织和分类从而形成可检索的格式是很关键的。这对于文本文档和HTML文档来说是很简单的,但PDF文档包含大量的结构和元信息,提取文档内容决不是一件简单的事情。PDF语言和Postscript相似,二者中的对象都是作为矢量绘制在页面的某些位置。例如:
- /Helv 12 Tf
- 0 13.0847 Td
- (Hello World) Tj
上面的指令将字体设为12号的Helvetica,移到下一行然后打印“Hello World”。这些命令流通常是经过压缩的,文字在屏幕上的显示顺序并不一定是文件中的字符出现顺序。因此,你有时无法直接从原始PDF文档中提取字符串。然而,PDFBox成熟的文本提取算法使得开发人员可以提取文档内容,就像在阅读器中呈现的那样。
Lucene是Apache Jakarta项目的子项目,它是一个流行的源代码开放的搜索引擎库。开发人员可以使用Lucene来创建索引,并基于该索引对大量的文本内容进行复杂的检索。Lucene只支持文本内容的检索,所以开发人员需要将其他形式的数据转换为文本形式才能使用Lucene。例如,Microsoft Word和StarOffice文档都必须先转换为文本形式才能添加到Lucene索引中。
PDF文件也不例外,但PDFBox提供一个特殊的整合对象,这让在Lucene索引中包含PDF文档变得非常容易。将一个基本PDF文档转换为Lucene文档只需要一条语句:
Document doc = LucenePDFDocument.getDocument( file );
这条语句解析指定的PDF文档,提取其内容并创建一个Lucene文档对象。然后你就可以将该对象添加到Lucene索引中了。如上文所述,PDF文档中也包含作者信息和关键词等元数据,在索引PDF文档时对这些元数据进行跟踪时很重要的。表1列出了创建Lucene文档时PDFBox将填写(populate)的字段。
这种整合使得开发人员可以轻松地使用Lucene来支持PDF文档的检索和索引。当然,有些应用程序要求更成熟的文本提取方法。此时可以直接使用PDFTextStripper类,或继承该类来满足这种复杂的需求。
通过继承PDFTextStripper并覆盖showCharacter()方法,你可以从许多方面对文本提取进行控制。例如,使用x、y位置信息进行限制以提取特定文本块。你可以有效地忽略所有的y坐标大于某个值的文本,这样文档头部内容就会被排除。
另一个例子。常常有这种情况:从表单创建了一组PDF文档,但这些原始数据被丢失了。也就是说,这些文档都包含一些你感兴趣的文本,而且这些文本都在相似的位置上,但填充文档的表单数据丢失了。例如,你有一些信封,在相同的位置上都有名字和地址信息。这时,你就可以使用PDFTextStripper的派生类来提取期望的字段,这个类就像一种截取屏幕区域的设备。
加密/解密
PDF的一个流行特性是允许对文档内容进行加密、对访问进行控制,限制只能阅读未加密文档。PDF文档加密时采用一个主密码和一个可选的用户密码。如果设定了用户密码,那么PDF阅读器(如Acrobat)将在显示文档之前提示输入密码。而主密码则用于授权修改文档内容。
PDF规范允许PDF文档的创建者对用户使用Acrobat阅读器查看文档时的某些操作进行限制。这些限制包括:
- 打印
- 修改内容
- 提取内容
PDF文档安全的讨论不在本文范畴之内,有兴趣的读者可以参考PDF规范的相关部分。PDF文档的安全模型是可插拔式的(pluggable),你可以在加密文档时使用不同的安全处理器(security handler)。对本文而言,PDFBox支持标准的安全处理器,它是大多数PDF文档所使用的。
加密文档时必须先指定一个安全处理器,然后使用一个主密码和用户密码进行加密。在下面的代码中,文档被加密,用户不需要敲入就可以在Acrobat中打开它(没有设置用户密码),但是该文档不可被打印。
//load the document
PDDocument pdf =
PDDocument.load( "test.pdf");
//create the encryption options
PDStandardEncryption encryptionOptions =
new PDStandardEncryption();
encryptionOptions.setCanPrint( false);
pdf.setEncryptionDictionary(
encryptionOptions );
//encrypt the document
pdf.encrypt( "master", null);
//save the encrypted document
//to the file system
pdf.save( "test-output.pdf");
更详细的示例参见PDFBox发布版中包含的加密工具类源代码:org.pdfbox.Encrypt。
许多应用程序可以生成PDF文档,但不支持控制文档的安全选项。这时PDFBox就可以用来在发送给用户之前截获并加密PDF文档。
表单整合
当应用程序的输出是一系列表单域的值时,提供将表单保存成文件的功能是很必要的。这时PDF技术将是一个很好的选择。开发人员可以手动编写PDF指令来绘制图形、表格和文本。或者将数据存成XML形式并使用XSL-FO模版来创建PDF文档。然而,这些办法都是比较耗时,容易出错,而且灵活性也比较差。对于简单的表单而言,一个更好的办法是创建模版,然后将给定的输入数据填入该模版,从而生成文档。
Employment Eligibility Verification是一个大多数人都熟悉的表单,它又叫做“I-9表单”,参见:http://uscis.gov/graphics/formsfee/forms/files/i-9.pdf
你可以使用PDFBox发布版中的一个示例程序列出表单域名单:
- java org.pdfbox.examples.fdf.PrintFields i-9.pdf
还有一个示例程序用于向指定的域中插入文本形式的数据:
- java org.pdfbox.examples.fdf.SetField i-9.pdf NAME1 Smith
在Acrobat中打开这个PDF文档你就会看到"Last Name"域已被填写了。你也可以使用以下代码来完成相同的操作:
PDDocument pdf =
PDDocument.load( "i-9.pdf");
PDDocumentCatalog docCatalog =
pdf.getDocumentCatalog();
PDAcroForm acroForm =
docCatalog.getAcroForm();
PDField field =
acroForm.getField( "NAME1");
field.setValue( "Smith");
pdf.save( "i-9-copy.pdf" );
下面的代码可用于提取刚才填写的表单域的值:
PDField field =
acroForm.getField( "NAME1");
System .out.println(
"First Name=" field.getValue() );
Acrobat支持将表单数据导入或导出到一个特定的文件格式“表单数据格式”(Forms Data Format)。这种文件有两类:FDF和XFDF。FDF文件存放表单数据的格式与PDF相同,而XFDF则以XML格式存放表单数据。PDFBox在一个类中处理FDF和XFDF:FDFDocument。下面的代码片断演示了如何从上面的I-9表单导出FDF数据:
PDDocument pdf =
PDDocument.load( "i-9.pdf");
PDDocumentCatalog docCatalog =
pdf.getDocumentCatalog();
PDAcroForm acroForm =
docCatalog.getAcroForm();
FDFDocument fdf = acroForm.exportFDF();
fdf.save( "exportedData.fdf" );
PDFBox表单整合步骤:
- 使用Acrobat或其他可视化工具创建PDF表单模版
- 记下每个需要的(desirable)表单域的名称
- 将模版存放在应用程序可以访问到的地方
- 当PDF被请求时,使用PDFBox解析PDF模版
- 填充指定的表单域
- 将填充结果(PDF)返回给用户
demo
1、创建PDF文件
1 public void createHelloPDF() {
2 PDDocument doc = null;
3 PDPage page = null;
4
5 try {
6 doc = new PDDocument();
7 page = new PDPage();
8 doc.addPage(page);
9 PDFont font = PDType1Font.HELVETICA_BOLD;
10 PDPageContentStream content = new PDPageContentStream(doc, page);
11 content.beginText();
12 content.setFont(font, 12);
13 content.moveTextPositionByAmount(100, 700);
14 content.drawString("hello");
15
16 content.endText();
17 content.close();
18 doc.save("F:\\java56班\\eclipse-SDK-4.2-win32\\pdfwithText.pdf");
19 doc.close();
20 } catch (Exception e) {
21 System.out.println(e);
22 }
23 }
2、读取PDF文件:
1 public void readPDF() {
2 PDDocument helloDocument = null;
3 try {
4 helloDocument = PDDocument.load(new File(
5 "F:\\java56班\\eclipse-SDK-4.2-win32\\pdfwithText.pdf"));
6 PDFTextStripper textStripper = new PDFTextStripper("GBK");
7 System.out.println(textStripper.getText(helloDocument));
8
9 helloDocument.close();
10 } catch (IOException e) {
11 // TODO Auto-generated catch block
12 e.printStackTrace();
13 }
14 }
3、修改PDF文件(处理中文乱码,我可以搞定的):
1 /**
2 * Locate a string in a PDF and replace it with a new string.
3 *
4 * @param inputFile The PDF to open.
5 * @param outputFile The PDF to write to.
6 * @param strToFind The string to find in the PDF document.
7 * @param message The message to write in the file.
8 *
9 * @throws IOException If there is an error writing the data.
10 * @throws COSVisitorException If there is an error writing the PDF.
11 */
12 public void doIt( String inputFile, String outputFile, String strToFind, String message)
13 throws IOException, COSVisitorException
14 {
15 // the document
16 PDDocument doc = null;
17 try
18 {
19 doc = PDDocument.load( inputFile );
20 // PDFTextStripper stripper=new PDFTextStripper("ISO-8859-1");
21 List pages = doc.getDocumentCatalog().getAllPages();
22 for( int i=0; i<pages.size(); i++ )
23 {
24 PDPage page = (PDPage)pages.get( i );
25 PDStream contents = page.getContents();
26 PDFStreamParser parser = new PDFStreamParser(contents.getStream() );
27 parser.parse();
28 List tokens = parser.getTokens();
29 for( int j=0; j<tokens.size(); j++ )
30 {
31 Object next = tokens.get( j );
32 if( next instanceof PDFOperator )
33 {
34 PDFOperator op = (PDFOperator)next;
35 //Tj and TJ are the two operators that display
36 //strings in a PDF
37 if( op.getOperation().equals( "Tj" ) )
38 {
39 //Tj takes one operator and that is the string
40 //to display so lets update that operator
41 COSString previous = (COSString)tokens.get( j-1 );
42 String string = previous.getString();
43 string = string.replaceFirst( strToFind, message );
44 System.out.println(string);
45 System.out.println(string.getBytes("GBK"));
46 previous.reset();
47 previous.append( string.getBytes("GBK") );
48 }
49 else if( op.getOperation().equals( "TJ" ) )
50 {
51 COSArray previous = (COSArray)tokens.get( j-1 );
52 for( int k=0; k<previous.size(); k++ )
53 {
54 Object arrElement = previous.getObject( k );
55 if( arrElement instanceof COSString )
56 {
57 COSString cosString = (COSString)arrElement;
58 String string = cosString.getString();
59 string = string.replaceFirst( strToFind, message );
60 cosString.reset();
61 cosString.append( string.getBytes("GBK") );
62 }
63 }
64 }
65 }
66 }
67 //now that the tokens are updated we will replace the
68 //page content stream.
69 PDStream updatedStream = new PDStream(doc);
70 OutputStream out = updatedStream.createOutputStream();
71 ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
72 tokenWriter.writeTokens( tokens );
73 page.setContents( updatedStream );
74 }
75 doc.save( outputFile );
76 }
77 finally
78 {
79 if( doc != null )
80 {
81 doc.close();
82 }
83 }
84 }
4、在PDF中加入图片:
1 /**
2 * Add an image to an existing PDF document.
3 *
4 * @param inputFile The input PDF to add the image to.
5 * @param image The filename of the image to put in the PDF.
6 * @param outputFile The file to write to the pdf to.
7 *
8 * @throws IOException If there is an error writing the data.
9 * @throws COSVisitorException If there is an error writing the PDF.
10 */
11 public void createPDFFromImage( String inputFile, String image, String outputFile )
12 throws IOException, COSVisitorException
13 {
14 // the document
15 PDDocument doc = null;
16 try
17 {
18 doc = PDDocument.load( inputFile );
19
20 //we will add the image to the first page.
21 PDPage page = (PDPage)doc.getDocumentCatalog().getAllPages().get( 0 );
22
23 PDXObjectImage ximage = null;
24 if( image.toLowerCase().endsWith( ".jpg" ) )
25 {
26 ximage = new PDJpeg(doc, new FileInputStream( image ) );
27 }
28 else if (image.toLowerCase().endsWith(".tif") || image.toLowerCase().endsWith(".tiff"))
29 {
30 ximage = new PDCcitt(doc, new RandomAccessFile(new File(image),"r"));
31 }
32 else
33 {
34 BufferedImage awtImage = ImageIO.read( new File( image ) );
35 ximage = new PDPixelMap(doc, awtImage);
36 }
37 PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true);
38
39 //contentStream.drawImage(ximage, 20, 20 );
40 // better method inspired by http://stackoverflow.com/a/22318681/535646
41 float scale = 0.5f; // reduce this value if the image is too large
42 System.out.println(ximage.getHeight());
43 System.out.println(ximage.getWidth());
44 // ximage.setHeight(ximage.getHeight()/5);
45 // ximage.setWidth(ximage.getWidth()/5);
46 contentStream.drawXObject(ximage, 20, 200, ximage.getWidth()*scale, ximage.getHeight()*scale);
47
48 contentStream.close();
49 doc.save( outputFile );
50 }
51 finally
52 {
53 if( doc != null )
54 {
55 doc.close();
56 }
57 }
58 }
5、PDF文件转换为图片:
1 public void toImage() {
2 try {
3 PDDocument doc = PDDocument
4 .load("F:\\java56班\\eclipse-SDK-4.2-win32\\pdfwithText.pdf");
5 int pageCount = doc.getPageCount();
6 System.out.println(pageCount);
7 List pages = doc.getDocumentCatalog().getAllPages();
8 for (int i = 0; i < pages.size(); i++) {
9 PDPage page = (PDPage) pages.get(i);
10 BufferedImage image = page.convertToImage();
11 Iterator iter = ImageIO.getImageWritersBySuffix("jpg");
12 ImageWriter writer = (ImageWriter) iter.next();
13 File outFile = new File("F:\\java56班\\eclipse-SDK-4.2-win32\\"
14 + i + ".jpg");
15 FileOutputStream out = new FileOutputStream(outFile);
16 ImageOutputStream outImage = ImageIO
17 .createImageOutputStream(out);
18 writer.setOutput(outImage);
19 writer.write(new IIOImage(image, null, null));
20 }
21 doc.close();
22 System.out.println("over");
23 } catch (FileNotFoundException e) {
24 // TODO Auto-generated catch block
25 e.printStackTrace();
26 } catch (IOException e) {
27 // TODO Auto-generated catch block
28 e.printStackTrace();
29 }
30 }
6、图片转换为PDF文件(支持多张图片转换为PDF文件):
1 /**
2 * create the second sample document from the PDF file format specification.
3 *
4 * @param file
5 * The file to write the PDF to.
6 * @param image
7 * The filename of the image to put in the PDF.
8 *
9 * @throws IOException
10 * If there is an error writing the data.
11 * @throws COSVisitorException
12 * If there is an error writing the PDF.
13 */
14 public void createPDFFromImage(String file, String image)throws IOException, COSVisitorException {
15 // 多张图片转换为PDF文件
16 PDDocument doc = null;
17 doc = new PDDocument();
18 PDPage page = null;
19 PDXObjectImage ximage = null;
20 PDPageContentStream contentStream = null;
21
22 File files = new File(image);
23 String[] a = files.list();
24 for (String string : a) {
25 if (string.toLowerCase().endsWith(".jpg")) {
26 String temp = image + "\\" + string;
27 ximage = new PDJpeg(doc, new FileInputStream(temp));
28 page = new PDPage();
29 doc.addPage(page);
30 contentStream = new PDPageContentStream(doc, page);
31 float scale = 0.5f;
32 contentStream.drawXObject(ximage, 20, 400, ximage.getWidth()
33 * scale, ximage.getHeight() * scale);
34
35 PDFont font = PDType1Font.HELVETICA_BOLD;
36 contentStream.beginText();
37 contentStream.setFont(font, 12);
38 contentStream.moveTextPositionByAmount(100, 700);
39 contentStream.drawString("Hello");
40 contentStream.endText();
41
42 contentStream.close();
43 }
44 }
45 doc.save(file);
46 doc.close();
47 }
7、替换PDF文件中的某个字符串:
1 /**
2 * Locate a string in a PDF and replace it with a new string.
3 *
4 * @param inputFile The PDF to open.
5 * @param outputFile The PDF to write to.
6 * @param strToFind The string to find in the PDF document.
7 * @param message The message to write in the file.
8 *
9 * @throws IOException If there is an error writing the data.
10 * @throws COSVisitorException If there is an error writing the PDF.
11 */
12 public void doIt( String inputFile, String outputFile, String strToFind, String message)
13 throws IOException, COSVisitorException
14 {
15 // the document
16 PDDocument doc = null;
17 try
18 {
19 doc = PDDocument.load( inputFile );
20 // PDFTextStripper stripper=new PDFTextStripper("ISO-8859-1");
21 List pages = doc.getDocumentCatalog().getAllPages();
22 for( int i=0; i<pages.size(); i++ )
23 {
24 PDPage page = (PDPage)pages.get( i );
25 PDStream contents = page.getContents();
26 PDFStreamParser parser = new PDFStreamParser(contents.getStream() );
27 parser.parse();
28 List tokens = parser.getTokens();
29 for( int j=0; j<tokens.size(); j++ )
30 {
31 Object next = tokens.get( j );
32 if( next instanceof PDFOperator )
33 {
34 PDFOperator op = (PDFOperator)next;
35 //Tj and TJ are the two operators that display
36 //strings in a PDF
37 if( op.getOperation().equals( "Tj" ) )
38 {
39 //Tj takes one operator and that is the string
40 //to display so lets update that operator
41 COSString previous = (COSString)tokens.get( j-1 );
42 String string = previous.getString();
43 string = string.replaceFirst( strToFind, message );
44 System.out.println(string);
45 System.out.println(string.getBytes("GBK"));
46 previous.reset();
47 previous.append( string.getBytes("GBK") );
48 }
49 else if( op.getOperation().equals( "TJ" ) )
50 {
51 COSArray previous = (COSArray)tokens.get( j-1 );
52 for( int k=0; k<previous.size(); k++ )
53 {
54 Object arrElement = previous.getObject( k );
55 if( arrElement instanceof COSString )
56 {
57 COSString cosString = (COSString)arrElement;
58 String string = cosString.getString();
59 string = string.replaceFirst( strToFind, message );
60 cosString.reset();
61 cosString.append( string.getBytes("GBK") );
62 }
63 }
64 }
65 }
66 }
67 //now that the tokens are updated we will replace the
68 //page content stream.
69 PDStream updatedStream = new PDStream(doc);
70 OutputStream out = updatedStream.createOutputStream();
71 ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
72 tokenWriter.writeTokens( tokens );
73 page.setContents( updatedStream );
74 }
75 doc.save( outputFile );
76 }
77 finally
78 {
79 if( doc != null )
80 {
81 doc.close();
82 }
83 }
84 }
工具
除了上文介绍的API之外,PDFBox还提供一系列命令行工具。表2列出了这些工具类并作简短介绍。
备注
PDF规范共有1172页之多,其实现的确是一浩大工程。同样,PDFBox发布版中说它“正在进行中”,新的功能会慢慢地添加上去。它的主要弱点是从零开始创建PDF文档。然而,有一些源码开放的Java项目可用于填补这个缺口。例如,Apache FOP项目支持从特殊的XML文档生成PDF,这个XML文档描述了要生成的PDF文档。此外,iText提供一个高层API用于创建表格和列表。
PDFBox的下一个版本将支持新的PDF 1.5 对象流和交叉引用流。然后将提供内嵌字体和图像的支持。在PDFBox的努力下,Java应用程序中的PDF技术有望得到充分的支持。
参考资源
PDFBox: www.pdfbox.org Apache FOP: http://xml.apache.org/fop/ iText: www.lowagie.com/iText/ PDF Reference: http://partners.adobe.com/asn/tech/pdf/specifications.jsp Jakarta Lucene: http://jakarta.spache.org/lucene/
PDFBOX详解的更多相关文章
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
- EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解
前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...
- Java 字符串格式化详解
Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...
- Android Notification 详解(一)——基本操作
Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...
- Android Notification 详解——基本操作
Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...
- Git初探--笔记整理和Git命令详解
几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...
- Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)
Android XML shape 标签使用详解 一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...
- Node.js npm 详解
一.npm简介 安装npm请阅读我之前的文章Hello Node中npm安装那一部分,不过只介绍了linux平台,如果是其它平台,有前辈写了更加详细的介绍. npm的全称:Node Package M ...
随机推荐
- emitted value instead of an instance of error the scope attribute for scoped slots webpack babel polyfill
api20180803.vue emitted value instead of an instance of error the scope attribute for scoped slots h ...
- 1490 ACM 数学
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1490 题意: 给出n*n 的矩阵,选出不同行不同列的n个元素,并求和: 如果所有选法所产生的和相等,则输出 ...
- 20172327 2018-2019-1 《第一行代码Android》第一章学习总结
学号 2018-2019-1 <第一行代码Android>第一章学习总结 教材学习内容总结 - Android系统架构: 1.Linux内核层 Android系统是基于Linux内核的,这 ...
- [P2058][NOIP2015]海港 (模拟)
%%%ADMAN #include<cstdio> using namespace std; int n,tot,now,ans,h; ],k[],a[],sum[]; int main( ...
- 4989: [Usaco2017 Feb]Why Did the Cow Cross the Road
题面:4989: [Usaco2017 Feb]Why Did the Cow Cross the Road 连接 http://www.lydsy.com/JudgeOnline/problem.p ...
- CSS_对齐
2016-10-25 <css入门经典>第15章 1.text-align属性: 块属性内部的文本对齐方式.该属性只对块盒子有意义,内联盒子的内容没有对齐方式.(注意:只是盒子内部的内容对 ...
- ssh远程登陆看不到用户名和主机名
使用secure crt远程登陆,发现看不到用户名和主机名,如下图所示 解决方法 sudo vim /etc/passwd root:x:::root:/root:/bin/bash sshd:x:: ...
- PMM 对MYSQL 的监控配制
系统选择: centos 7.2 关闭防火墙: systemctl stop firewalld.service systemctl disable firewalld.s ...
- txt2xls
#!/bin/env python# -*- encoding: utf-8 -*-import datetimeimport timeimport osimport sysimport openpy ...
- XSplit Quality, VBV-Buffer, VBV-Maxrate and Preset Settings
XSplit uses the x264 encoder, so let's start off by saying that parameters mentioned in the title, w ...