76
© 2016, iText Group NV © 2016, iText Group NV Oops, I broke my API Bruno Lowagie & Raf Hens: iText @ JavaOne 2016 @bruno1970 – @rafhens – @iText

Oops, I broke my API

Embed Size (px)

Citation preview

Page 1: Oops, I broke my API

© 2016, iText Group NV© 2016, iText Group NV

Oops, I broke my APIBruno Lowagie & Raf Hens: iText @ JavaOne 2016@bruno1970 – @rafhens – @iText

Page 2: Oops, I broke my API

© 2016, iText Group NV

2 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText

Agenda: five important questions

Who? Who are we and how did we break our APIWhat? What does it mean when people say they broke their APIWhy? What are acceptable reasons to break an APIHow? Or rather: how not to break your APIWhen? When to break the API, once the decision is made to do it

Page 3: Oops, I broke my API

© 2016, iText Group NV© 2015, iText Group NV

Who?

Who are we? What inspired us to present this talk? What do we want to achieve?

Page 4: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText4

Bruno LowagieOriginal developer of iTextex-CEO, current CTO at iText Group

Raf HensDirector of Engineering at iText GroupProject lead of iText 7

Who are we?

Page 5: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText5

What is iText?iText is an open source library for creating, processing, and manipulating PDF files in Java and .NET.

Page 6: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText6

We broke our API (more than once)2000: iText 0.30; first release (LGPL, later MPL/LGPL)• 2003: iText 1.00• 2007: iText 2.0.0• 2009: iText 2.1.7; last version available under MPL/LGPL

2009: iText 5.0.0; API deliberately broken (switch to AGPL)• 2012: iText 5.2.0; broken release (undeliberate)• 2013: iText 5.3.x; deliberate API change for digital signature functionality• 2016: iText 5.5.9; last “backward compatible” version

2016: iText 7; rewritten from scratch, not backward compatible

Page 7: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText7

We’re not aloneIn this talk, we want to share our experience so that other developers can avoid making the mistakes we made. We’ll also show other examples of APIs that were broken and explain what we learned from those examples

Page 8: Oops, I broke my API

© 2016, iText Group NV© 2015, iText Group NV

What?

What does “breaking an API” actually mean? Binary compatibility Source code compatibility Behavioral compatibility

Page 9: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText9

Backwards compatibility

What is it?

• Java: original specification still 100% valid• PDF: PDF 1.0 should still render 100% correctly

Examples of backwards compatibility

Page 10: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText10

Three types of compatibility

• a certain type of compiled file can run on both systems (whether it be hardware or software) without any changes

Binary compatibility

• the input file must be recompiled for every system, but does not need to be changed before compiling.

Source code compatibility

• the behavior when processing the input file is the same

Behavioral compatibility

Page 11: Oops, I broke my API

© 2016, iText Group NV© 2015, iText Group NV

Why?

What are acceptable reasons to break an API? Why did we break our API?

Page 12: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText12

Involuntary API breaks

• Customized version of Bouncy Castle in Android• Renamed version “Spongy Castle” to work around this issue

Bouncy Castle

• A third party created a fork: iText 4• The official iText groupID was used (which is not allowed)• This broke many Maven builds

iText

Page 13: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText13

Avoid breaking the API when possible Use internal re-implementations Use two methods for the same function Deprecate old methods

Redundancy!

Page 14: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText14

Redundancy can get painfulRationale for deprecating XFA

Rationale for Python 3

• HTMLWorker / XML Worker / …• Rendering APIs iText 5 / iText 7

Rationale for iText 7

Page 15: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText15

In response to advancing technology

• Example: PDF

Standards and specifications change

• Hashing and encryption algorithms• Impact on digital signatures

Processing power increases

• ASCII isn’t sufficient anymore, we need Unicode

Internationalization

Page 16: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText16

Changing the API as a strategy

• Old games don’t work on new consoles

PlayStation, Xbox

• Reduce server load• Reduce abuse

Twitter API change 2013

• Replace competing add-on with own add-on

SharePoint 2013

Page 17: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText17

Why we changed the iText design

• The core design hadn’t changed since 2000• Organic growth; many different contributors• The world has changed since 2000

iText 7 versus iText 5

Page 18: Oops, I broke my API

© 2016, iText Group NV© 2015, iText Group NV

How?

What should you do to limit the damage? What should you avoid?

Page 19: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText19

Use semantic versioning

Use an x.y.z notation

• The major version number (x):• for breaking changes

• The minor version number (y):• for new, yet backwards

compatible, features• The patch version number (z):

• for updates which fix bugs

How not to do it:

• BouncyCastle• PHP

Page 20: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText20

A good design to start with is key

Spring 2 and Spring 3

• The original design was future-proof

Python 2 and Python 3

• It took 4 years to build Python 3 (2004-2008)

• Adoption of Python 3 was extremely slow

Page 21: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText21

Avoid being a moving target

• Switch from Java 5 to Java 7• without changing the version (1.0.0-SNAPSHOT)

Commons-imaging

• No leadership: breaking changes in minor versions

PHP

• Developed internally until API was more or less stable

iText 7

Page 22: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText22

Use tools to detect breaking changes Clirr: http://clirr.sourceforge.net/

Maven plugin (mvn clirr:check) We run it in a separate profile

SonarQube plugin Tip: create a separate SQ dashboard

JDiff: http://javadiff.sourceforge.net/ Javadoc doclet Generates HTML report of API differences

Page 23: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText23

Ease the pain

• iText: tutorials on Leanpub

Provide good documentation

• Python: tools• Microsoft Word: document format conversion

Provide conversion tools

Page 24: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText24

Avoid back-porting

• Disadvantages of back-porting

Python 3

• No new functionality in iText 5 (e.g. PDF 2.0)

iText 7

Page 25: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText25

Have a clear EOL strategy

Python 2 procrastination

• Constantly changing EOL date

iText

• iText 5: December 31, 2018

• iText 7: December 31, 2025

Page 26: Oops, I broke my API

© 2016, iText Group NV© 2015, iText Group NV

When?

Which conditions should be met before breaking the API? When is it acceptable to make a release that breaks the API?

Page 27: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText27

It’s marketing’s fault

Product is announced before it’s ready• Example: the Universal

Windows PlatformResult: confusion?!

Page 28: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText28

It’s the sales’ fault

Want to sell an upgrade NOW

Pushing to release new versions

Don’t want to lose business

Putting on hold communication about new versions to keep current business old

version

Result: confusion

Page 29: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText29

In any case…

• Deprecate before you remove a class or a method• Warn when code is subject to change

Don’t let it happen unnoticed

• A license change also breaks the API

Beware of non-technical changes

Page 30: Oops, I broke my API

© 2016, iText Group NV© 2015, iText Group NV

Use case: iText 7

New insights Lessons Learned

Page 31: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText31

Making development future-proof

New insights that led to a complete rewrite of iText 7

Make it modular, make it extensible

Remove functional overlap to avoid

maintenance hell

Meet customer requests, e.g. special

writing systems

Page 32: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText32

Big bang release or early release?Big bang: what if we got it wrong?

Early release: heavy burden on support

• Release early for selected users• Release as open source

• As soon as the API is stable• Before the work is completely done

Something in-between:

Page 33: Oops, I broke my API

© 2016, iText Group NV© 2015, iText Group NV

Questions?

Page 34: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText34

Hello World: iText 5public void createPdf(String dest) throws DocumentException, IOException { Document document = new Document(); PdfWriter.getInstance( document, new FileOutputStream(dest)); document.open(); document.add(new Paragraph("Hello World!")); document.close();}

Page 35: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText35

Hello World: iText 7 (example 1)public void createPdf(String dest) throws IOException { PdfDocument pdf = new PdfDocument(new PdfWriter(dest)); Document document = new Document(pdf); document.add(new Paragraph("Hello World!")); document.close();}

Page 36: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText36

Hello World: iText 7 (example 2)public void createPdf(String dest) throws IOException { PdfDocument pdf = new PdfDocument(new PdfWriter(dest)); try (Document document = new Document(pdf)) { document.add(new Paragraph("Hello World!")); }}

Page 37: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText37

Hello World: iText 5 vs iText 7

Page 38: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText38

Fonts: iText 5Font font = new Font(FontFamily.TIMES_ROMAN);Font font14pt = new Font(FontFamily.TIMES_ROMAN, 14);Font font10pt = new Font(FontFamily.TIMES_ROMAN, 10);BaseFont bf_russian = BaseFont.createFont( "resources/fonts/FreeSans.ttf", "CP1251", BaseFont.EMBEDDED);Font russian = new Font(bf_russian, 12);BaseFont bf_cjk = BaseFont.createFont( "resources/fonts/NotoSansCJKsc-Regular.otf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);Font cjk = new Font(bf_cjk, 12);

Page 39: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText39

Fonts: iText 7PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);PdfFont russian = PdfFontFactory.createFont( "src/main/resources/fonts/FreeSans.ttf", "CP1251", true);PdfFont cjk = PdfFontFactory.createFont( "src/main/resources/fonts/NotoSansCJKsc-Regular.otf", PdfEncodings.IDENTITY_H, true);

Page 40: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText40

Fonts: iText 5Paragraph p = new Paragraph("Hello World! ", font);Chunk chunk = new Chunk("Hallo Wereld! ", font14pt);p.add(chunk);chunk = new Chunk("Bonjour le monde! ", font10pt);chunk.setTextRise(4);p.add(chunk);chunk = new Chunk("\u0417\u0434 ... \u0440! ", russian);p.add(chunk);p.add(new Chunk("\u4f60\u597d\u4e16\u754c! ", cjk));p.add(new Chunk("\uc5ec\ubcf4 ... \uacc4!", cjk));document.add(p);

Page 41: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText41

Fonts: iText 7document.setFont(font);Paragraph p = new Paragraph("Hello World! ") .add(new Text("Hallo Wereld! ") .setFontSize(14)) .add( new Text("Bonjour le monde! ") .setFontSize(10) .setTextRise(4)) .add( new Text ("\u0417\u0434 ... \u0440! “).setFont(russian)) .add( new Text("\u4f60\u597d\u4e16\u754c! ").setFont(cjk)) .add( new Text("\uc5ec\ubcf4 ... \uacc4!").setFont(cjk));document.add(p);

Page 42: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText42

Fonts: iText 5 vs iText 7

Page 43: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText43

Switching styles: iText 5public Chunk createBgChunk(String s, Font font) { Chunk chunk = new Chunk(s, font); chunk.setBackground(BaseColor.LIGHT_GRAY); return chunk;}Font code = new Font( FontFamily.COURIER, 12, Font.NORMAL, BaseColor.RED);Paragraph p = new Paragraph("In this example, named ");p.add(createBgChunk("HelloWorldStyles", code));p.add(", we experiment with some text in ");p.add(createBgChunk("code style", code));p.add(".");

Page 44: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText44

Switching styles: iText 7Style style = new Style() .setFont(code) .setFontSize(12) .setFontColor(Color.RED) .setBackgroundColor(Color.LIGHT_GRAY);document.add( new Paragraph() .add("In this example, named ") .add(new Text("HelloWorldStyles").addStyle(style)) .add(", we experiment with some text in ") .add(new Text("code style").addStyle(style)) .add("."));

Page 45: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText45

Switching styles: iText 5 vs iText 7

Page 46: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText46

Tables: iText 5PdfPTable table = new PdfPTable(3);

PdfPCell cell = new PdfPCell(new Phrase("Cell with colspan 3"));cell.setColspan(3);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);

cell = new PdfPCell(new Phrase("Cell with rowspan 2"));cell.setRowspan(2);cell.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell);

Page 47: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText47

Tables: iText 5// text modetable.addCell("Cell 1.1");

// composite modecell = new PdfPCell();cell.addElement(new Phrase("Cell 1.2"));table.addCell(cell);

Page 48: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText48

Tables: iText 5cell = new PdfPCell(new Phrase("Cell 2.1"));cell.setPadding(5);cell.setUseAscender(true);cell.setUseDescender(true);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);cell = new PdfPCell();cell.setPadding(5);cell.setUseAscender(true);cell.setUseDescender(true);Paragraph p = new Paragraph("Cell 2.2");p.setAlignment(Element.ALIGN_CENTER);cell.addElement(p);table.addCell(cell);

Page 49: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText49

Tables: iText 7Table table = new Table(3);Cell cell = new Cell(1, 3) .setTextAlignment(TextAlignment.CENTER) .add("Cell with colspan 3");table.addCell(cell);cell = new Cell(2, 1) .add("Cell with rowspan 2") .setVerticalAlignment(VerticalAlignment.MIDDLE);table.addCell(cell);

Page 50: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText50

Tables: iText 7table.addCell("Cell 1.1");table.addCell(new Cell().add("Cell 1.2"));table.addCell(new Cell() .add("Cell 2.1") .setBackgroundColor(Color.LIGHT_GRAY) .setMargin(5));table.addCell(new Cell() .add("Cell 1.2") .setBackgroundColor(Color.LIGHT_GRAY) .setPadding(5));

Page 51: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText51

Tables: iText 5 vs iText 7

Page 52: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText52

TXT 2 PDF: iText 5Document document = new Document();PdfWriter.getInstance(document, new FileOutputStream(dest));document.open();BufferedReader br = new BufferedReader(new FileReader(TEXT));String line;Paragraph p;Font normal = new Font(FontFamily.TIMES_ROMAN, 12);Font bold = new Font(FontFamily.TIMES_ROMAN, 12, Font.BOLD);boolean title = true;while ((line = br.readLine()) != null) { p = new Paragraph(line, title ? bold : normal); p.setAlignment(Element.ALIGN_JUSTIFIED); title = line.isEmpty(); document.add(p);}document.close();

Page 53: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText53

TXT 2 PDF: iText 7PdfDocument pdf = new PdfDocument(new PdfWriter(dest));Document document = new Document(pdf) .setTextAlignment(TextAlignment.JUSTIFIED);BufferedReader br = new BufferedReader(new FileReader(TEXT));String line;PdfFont normal = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);PdfFont bold = PdfFontFactory.createFont(FontConstants.TIMES_BOLD);boolean title = true;while ((line = br.readLine()) != null) { document.add(new Paragraph(line).setFont(title ? bold : normal)); title = line.isEmpty();}document.close();

Page 54: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText54

TXT 2 PDF: iText 5 vs iText 7

Page 55: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText55

Columns: iText 5Document document = new Document();PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));document.open();ColumnText ct = new ColumnText(writer.getDirectContent());

Page 56: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText56

Columns: iText 5BufferedReader br = new BufferedReader(new FileReader(TEXT));String line;Paragraph p;Font normal = new Font(FontFamily.TIMES_ROMAN, 12);Font bold = new Font(FontFamily.TIMES_ROMAN, 12, Font.BOLD);boolean title = true;while ((line = br.readLine()) != null) { p = new Paragraph(line, title ? bold : normal); p.setAlignment(Element.ALIGN_JUSTIFIED); title = line.isEmpty(); ct.addElement(p);}

Page 57: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText57

Columns: iText 5Rectangle[] columns = { new Rectangle(36, 36, 290, 806), new Rectangle(305, 36, 559, 806) }int c = 0;int status = ColumnText.START_COLUMN;while (ColumnText.hasMoreText(status)) { ct.setSimpleColumn(columns[c]); status = ct.go(); if (++c == 2) { document.newPage(); c = 0; }}document.close();

Page 58: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText58

Columns: iText 7PdfDocument pdf = new PdfDocument(new PdfWriter(dest));Document document = new Document(pdf) .setTextAlignment(TextAlignment.JUSTIFIED);Rectangle[] columns = { new Rectangle(36, 36, 254, 770), new Rectangle(305, 36, 254, 770) };document.setRenderer(new ColumnDocumentRenderer(document, columns));BufferedReader br = new BufferedReader(new FileReader(TEXT));String line;PdfFont normal = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);PdfFont bold = PdfFontFactory.createFont(FontConstants.TIMES_BOLD);boolean title = true;while ((line = br.readLine()) != null) { document.add(new Paragraph(line).setFont(title ? bold : normal)); title = line.isEmpty();}document.close();

Page 59: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText59

Columns: iText 5 vs iText 7

Page 60: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText60

Page events: iText 5Document document = new Document();PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));MyPageEvents events = new MyPageEvents();writer.setPageEvent(events);document.open();

Page 61: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText61

Page events: iText 5BufferedReader br = new BufferedReader(new FileReader(TEXT));String line;Paragraph p;Font normal = new Font(FontFamily.TIMES_ROMAN, 12);Font bold = new Font(FontFamily.TIMES_ROMAN, 12, Font.BOLD);boolean title = true;while ((line = br.readLine()) != null) { p = new Paragraph(line, title ? bold : normal); p.setAlignment(Element.ALIGN_JUSTIFIED); events.setTitle(title); document.add(p); title = line.isEmpty();}document.close();

Page 62: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText62

Page events: iText 5class MyPageEvents extends PdfPageEventHelper { protected float startpos = -1; protected boolean title = true;

public void setTitle(boolean title) { this.title = title; } @Override public void onEndPage(PdfWriter writer, Document document) { ... } @Override public void onParagraph(PdfWriter writer, Document document, float paragraphPosition) { ... } @Override public void onParagraphEnd(PdfWriter writer, Document document, float paragraphPosition) { ... }}

Page 63: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText63

Page events: iText 5@Overridepublic void onEndPage(PdfWriter writer, Document document) { Rectangle pagesize = document.getPageSize(); ColumnText.showTextAligned( writer.getDirectContent(), Element.ALIGN_CENTER, new Phrase(String.valueOf(writer.getPageNumber())), (pagesize.getLeft() + pagesize.getRight()) / 2, pagesize.getBottom() + 15, 0); if (startpos != -1) onParagraphEnd(writer, document, pagesize.getBottom(document.bottomMargin())); startpos = pagesize.getTop(document.topMargin());}

Page 64: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText64

Page events: iText 5@Overridepublic void onParagraph(PdfWriter writer, Document document, float paragraphPosition) { startpos = paragraphPosition;}@Overridepublic void onParagraphEnd(PdfWriter writer, Document document, float paragraphPosition) { if (!title) return; PdfContentByte canvas = writer.getDirectContentUnder(); Rectangle pagesize = document.getPageSize(); canvas.saveState(); canvas.setColorStroke(BaseColor.BLUE); canvas.rectangle( pagesize.getLeft(document.leftMargin()), paragraphPosition - 3, pagesize.getWidth() - document.leftMargin() - document.rightMargin(), startpos - paragraphPosition); canvas.stroke(); canvas.restoreState();}

Page 65: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText65

Event handlers: iText 7PdfDocument pdf = new PdfDocument(new PdfWriter(dest));pdf.addEventHandler(PdfDocumentEvent.END_PAGE, new Footer());Document document = new Document(pdf).setTextAlignment(TextAlignment.JUSTIFIED);BufferedReader br = new BufferedReader(new FileReader(TEXT));String line;PdfFont normal = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);PdfFont bold = PdfFontFactory.createFont(FontConstants.TIMES_BOLD);boolean title = true;Border border = new SolidBorder(Color.BLUE, 1);while ((line = br.readLine()) != null) { document.add(new Paragraph(line) .setFont(title ? bold : normal) .setBorder(title ? border : Border.NO_BORDER)); title = line.isEmpty();}document.close();

Page 66: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText66

Event handlers: iText 7protected class Footer implements IEventHandler { @Override public void handleEvent(Event event) { PdfDocumentEvent docEvent = (PdfDocumentEvent) event; PdfDocument pdf = docEvent.getDocument(); PdfPage page = docEvent.getPage(); Rectangle pageSize = page.getPageSize(); PdfCanvas pdfCanvas = new PdfCanvas( page.getLastContentStream(), page.getResources(), pdf); Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize); float x = (pageSize.getLeft() + pageSize.getRight()) / 2; float y = pageSize.getBottom() + 15; canvas.showTextAligned( String.valueOf(pdf.getPageNumber(page)), x, y, TextAlignment.CENTER); }}

Page 67: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText67

Events: iText 5 vs iText 7

Page 68: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText68

Renderers: iText 7public class TitleParagraph extends Paragraph { public TitleParagraph(String line) { super(line); try { setFont(PdfFontFactory.createFont(FontConstants.TIMES_BOLD)); } catch (IOException ioe) { } } @Override protected IRenderer makeNewRenderer() { return new ParagraphRenderer(this) { ... }; } }

Page 69: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText69

Renderers: iText 7@Overridepublic void drawBorder(DrawContext drawContext) { Rectangle occupiedAreaBBox = getOccupiedAreaBBox(); float[] margins = getMargins(); Rectangle rectangle = applyMargins(occupiedAreaBBox, margins, false); PdfCanvas canvas = drawContext.getCanvas(); canvas.roundRectangle(rectangle.getX() - 1, rectangle.getY() - 1, rectangle.getWidth() + 2, rectangle.getHeight() + 2, 5).stroke();}

Page 70: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText70

Renderers: iText 5 vs iText 7

Page 71: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText71

Form creation: iText 5PdfReader reader = new PdfReader(src);PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));TextField tf = new TextField(stamper.getWriter(), new Rectangle(110, 780, 180, 806), "text");tf.setBorderColor(BaseColor.BLUE);tf.setBorderWidth(2);tf.setTextColor(BaseColor.RED);tf.setFontSize(12);tf.setText("Text field");PdfFormField field = tf.getTextField();stamper.addAnnotation(field, 1);stamper.close();reader.close();

Page 72: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText72

Form creation: iText 7PdfReader reader = new PdfReader(src);PdfDocument pdf = new PdfDocument(reader, new PdfWriter(dest));PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);PdfFormField tf = PdfTextFormField.createText(pdf, new Rectangle(110, 780, 70, 26), "text", "Text Field") .setBorderColor(Color.BLUE) .setBorderWidth(2) .setColor(Color.RED) .setFontSize(12);form.addField(tf);pdf.close();

Page 73: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText73

Form creation: iText 5 vs iText 7

Page 74: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText74

Form filling: iText 5PdfReader reader = new PdfReader(src);PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));AcroFields fields = stamper.getAcroFields();fields.setFieldProperty("text", "textcolor", BaseColor.BLUE, null);fields.setFieldProperty("text", "bordercolor", BaseColor.RED, null);fields.setFieldProperty("text", "fontsize", 14, null);fields.setField("text", "Field Text");stamper.close();reader.close();

Page 75: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText75

Form filling: iText 7PdfReader reader = new PdfReader(src);PdfDocument pdf = new PdfDocument(reader, new PdfWriter(dest));PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);PdfFormField tf = form.getFormFields().get("text");tf.setBorderColor(Color.RED) .setColor(Color.BLUE) .setFontSize(14) .setValue("Field Text");pdf.close();

Page 76: Oops, I broke my API

© 2016, iText Group NV

Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText76

Form filling: iText 5 vs iText 7