Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Oops, I broke my API

991 views

Published on

When is and when is it not a good idea to break your API when transitioning to a new version of your SDK?

Published in: Software
  • Be the first to comment

  • Be the first to like this

Oops, I broke my API

  1. 1. © 2016, iText Group NV© 2016, iText Group NV Oops, I broke my API Bruno Lowagie & Raf Hens: iText @ JavaOne 2016 @bruno1970 – @rafhens – @iText
  2. 2. © 2016, iText Group NV Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText2 Agenda: five important questions Who? Who are we and how did we break our API What? What does it mean when people say they broke their API Why? What are acceptable reasons to break an API How? Or rather: how not to break your API When? When to break the API, once the decision is made to do it
  3. 3. © 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?
  4. 4. © 2016, iText Group NV Bruno Lowagie Original developer of iText ex-CEO, current CTO at iText Group Raf Hens Director of Engineering at iText Group Project lead of iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText4 Who are we?
  5. 5. © 2016, iText Group NV What is iText? Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText5 iText is an open source library for creating, processing, and manipulating PDF files in Java and .NET.
  6. 6. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText6
  7. 7. © 2016, iText Group NV We’re not alone Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText7 In 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
  8. 8. © 2016, iText Group NV© 2015, iText Group NV What?  What does “breaking an API” actually mean?  Binary compatibility  Source code compatibility  Behavioral compatibility
  9. 9. © 2016, iText Group NV Backwards compatibility What is it? • Java: original specification still 100% valid • PDF: PDF 1.0 should still render 100% correctly Examples of backwards compatibility Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText9
  10. 10. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText10
  11. 11. © 2016, iText Group NV© 2015, iText Group NV Why?  What are acceptable reasons to break an API?  Why did we break our API?
  12. 12. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText12
  13. 13. © 2016, iText Group NV Avoid breaking the API when possible Use internal re-implementations Use two methods for the same function Deprecate old methods Redundancy! Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText13
  14. 14. © 2016, iText Group NV Redundancy can get painful Rationale for deprecating XFA Rationale for Python 3 • HTMLWorker / XML Worker / … • Rendering APIs iText 5 / iText 7 Rationale for iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText14
  15. 15. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText15
  16. 16. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText16
  17. 17. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText17
  18. 18. © 2016, iText Group NV© 2015, iText Group NV How?  What should you do to limit the damage?  What should you avoid?
  19. 19. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText19
  20. 20. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText20
  21. 21. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText21
  22. 22. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText22
  23. 23. © 2016, iText Group NV Ease the pain • iText: tutorials on Leanpub Provide good documentation • Python: tools • Microsoft Word: document format conversion Provide conversion tools Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText23
  24. 24. © 2016, iText Group NV Avoid back-porting • Disadvantages of back-porting Python 3 • No new functionality in iText 5 (e.g. PDF 2.0) iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText24
  25. 25. © 2016, iText Group NV Have a clear EOL strategy Python 2 procrastination • Constantly changing EOL date iText • iText 5: December 31, 2018 • iText 7: December 31, 2025 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText25
  26. 26. © 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?
  27. 27. © 2016, iText Group NV It’s marketing’s fault Product is announced before it’s ready • Example: the Universal Windows Platform Result: confusion Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText27 ?!
  28. 28. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText28
  29. 29. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText29
  30. 30. © 2016, iText Group NV© 2015, iText Group NV Use case: iText 7  New insights  Lessons Learned
  31. 31. © 2016, iText Group NV 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 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText31
  32. 32. © 2016, iText Group NV 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: Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText32
  33. 33. © 2016, iText Group NV© 2015, iText Group NV Questions?
  34. 34. © 2016, iText Group NV Hello World: iText 5 public 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(); } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText34
  35. 35. © 2016, iText Group NV 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(); } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText35
  36. 36. © 2016, iText Group NV 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!")); } } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText36
  37. 37. © 2016, iText Group NV Hello World: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText37
  38. 38. © 2016, iText Group NV Fonts: iText 5 Font 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); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText38
  39. 39. © 2016, iText Group NV Fonts: iText 7 PdfFont 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); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText39
  40. 40. © 2016, iText Group NV Fonts: iText 5 Paragraph 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("u0417u0434 ... u0440! ", russian); p.add(chunk); p.add(new Chunk("u4f60u597du4e16u754c! ", cjk)); p.add(new Chunk("uc5ecubcf4 ... uacc4!", cjk)); document.add(p); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText40
  41. 41. © 2016, iText Group NV Fonts: iText 7 document.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 ("u0417u0434 ... u0440! “).setFont(russian)) .add( new Text("u4f60u597du4e16u754c! ").setFont(cjk)) .add( new Text("uc5ecubcf4 ... uacc4!").setFont(cjk)); document.add(p); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText41
  42. 42. © 2016, iText Group NV Fonts: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText42
  43. 43. © 2016, iText Group NV Switching styles: iText 5 public 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("."); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText43
  44. 44. © 2016, iText Group NV Switching styles: iText 7 Style 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(".")); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText44
  45. 45. © 2016, iText Group NV Switching styles: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText45
  46. 46. © 2016, iText Group NV Tables: iText 5 PdfPTable 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); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText46
  47. 47. © 2016, iText Group NV Tables: iText 5 // text mode table.addCell("Cell 1.1"); // composite mode cell = new PdfPCell(); cell.addElement(new Phrase("Cell 1.2")); table.addCell(cell); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText47
  48. 48. © 2016, iText Group NV Tables: iText 5 cell = 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); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText48
  49. 49. © 2016, iText Group NV Tables: iText 7 Table 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); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText49
  50. 50. © 2016, iText Group NV Tables: iText 7 table.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)); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText50
  51. 51. © 2016, iText Group NV Tables: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText51
  52. 52. © 2016, iText Group NV TXT 2 PDF: iText 5 Document 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText52
  53. 53. © 2016, iText Group NV TXT 2 PDF: iText 7 PdfDocument 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText53
  54. 54. © 2016, iText Group NV TXT 2 PDF: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText54
  55. 55. © 2016, iText Group NV Columns: iText 5 Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest)); document.open(); ColumnText ct = new ColumnText(writer.getDirectContent()); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText55
  56. 56. © 2016, iText Group NV Columns: iText 5 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(); ct.addElement(p); } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText56
  57. 57. © 2016, iText Group NV Columns: iText 5 Rectangle[] 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText57
  58. 58. © 2016, iText Group NV Columns: iText 7 PdfDocument 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText58
  59. 59. © 2016, iText Group NV Columns: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText59
  60. 60. © 2016, iText Group NV Page events: iText 5 Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest)); MyPageEvents events = new MyPageEvents(); writer.setPageEvent(events); document.open(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText60
  61. 61. © 2016, iText Group NV Page events: iText 5 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); events.setTitle(title); document.add(p); title = line.isEmpty(); } document.close(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText61
  62. 62. © 2016, iText Group NV Page events: iText 5 class 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) { ... } } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText62
  63. 63. © 2016, iText Group NV Page events: iText 5 @Override public 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()); } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText63
  64. 64. © 2016, iText Group NV Page events: iText 5 @Override public void onParagraph(PdfWriter writer, Document document, float paragraphPosition) { startpos = paragraphPosition; } @Override public 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(); } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText64
  65. 65. © 2016, iText Group NV Event handlers: iText 7 PdfDocument 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText65
  66. 66. © 2016, iText Group NV Event handlers: iText 7 protected 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); } } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText66
  67. 67. © 2016, iText Group NV Events: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText67
  68. 68. © 2016, iText Group NV Renderers: iText 7 public 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) { ... }; } } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText68
  69. 69. © 2016, iText Group NV Renderers: iText 7 @Override public 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(); } Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText69
  70. 70. © 2016, iText Group NV Renderers: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText70
  71. 71. © 2016, iText Group NV Form creation: iText 5 PdfReader 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText71
  72. 72. © 2016, iText Group NV Form creation: iText 7 PdfReader 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText72
  73. 73. © 2016, iText Group NV Form creation: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText73
  74. 74. © 2016, iText Group NV Form filling: iText 5 PdfReader 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText74
  75. 75. © 2016, iText Group NV Form filling: iText 7 PdfReader 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(); Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText75
  76. 76. © 2016, iText Group NV Form filling: iText 5 vs iText 7 Oops, I broke my API - JavaOne 2016: @bruno1970, @rafhens, @iText76

×