diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/CharactersAndPositions.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/CharactersAndPositions.java new file mode 100644 index 00000000000..c1bf6ae488d --- /dev/null +++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/CharactersAndPositions.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Volker Kunert 2026 + */ + +package org.apache.pdfbox.examples.pdmodel.glyphposition; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Stores sublists of characters and positions in a list + * Helper class for GlyphPositioner + */ +public class CharactersAndPositions { + private final ArrayList list = new ArrayList<>(); + + /** + * Adds a character + * + * @param ch to be added + */ + public void add(char ch) { + Object last = !list.isEmpty() ? list.get(list.size() - 1) : null; + GlyphSubList glyphSubList; + if (!(last instanceof GlyphSubList)) { + glyphSubList = new GlyphSubList(); + list.add(glyphSubList); + } else { + glyphSubList = (GlyphSubList) last; + } + glyphSubList.add(ch); + } + + /** + * Add a position + * + * @param position to be added + */ + public void add(Float position) { + list.add(position); + } + + /** + * Checks if the list is empty + * + * @return true if it is empty + */ + public boolean isEmpty() { + return list.isEmpty(); + } + + /** + * Clears the list + */ + public void clear() { + list.clear(); + } + + /** + * Converts CharactersAndPositions to a list of objects (GlyphSubList and Float) + * + * @return the list + */ + public List toList() { + return Collections.unmodifiableList(list); + } + + /** + * Converts CharactersAndPositions to an array of objects (GlyphSubList and Float) + * + * @return the array + */ + public Object[] toArray() { + return Collections.unmodifiableList(list).toArray(); + } + + /** + * Sublist to store adjacent glyphs + */ + public static class GlyphSubList extends ArrayList { + } +} diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/GlyphPositioner.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/GlyphPositioner.java new file mode 100644 index 00000000000..6c86979dd9c --- /dev/null +++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/GlyphPositioner.java @@ -0,0 +1,234 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Volker Kunert 2026 + */ + +package org.apache.pdfbox.examples.pdmodel.glyphposition; + +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDType0Font; + + +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.io.IOException; +import java.io.InputStream; +import java.text.AttributedString; +import java.text.Bidi; +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Character positioning + * (No glyph reordering or substitution) + */ +public class GlyphPositioner { + private final Map awtFontMap = new ConcurrentHashMap<>(); + private final PDPageContentStream contentStream; + private PDType0Font font; + private float fontSize; + + /** + * Constructs a GlyphPositioner + * + */ + public GlyphPositioner(PDPageContentStream contentStream) { + this.contentStream = contentStream; + } + + /** + * Checks if the glyphVector contains adjustments + * that make advanced glyph layout necessary + * + * @param glyphVector glyph vector containing the positions + * @return true if the glyphVector contains adjustments + */ + private static boolean hasAdjustments(GlyphVector glyphVector) { + boolean retVal = false; + float lastX = 0f; + float lastY = 0f; + + for (int i = 0; i < glyphVector.getNumGlyphs(); i++) { + Point2D p = glyphVector.getGlyphPosition(i); + float dx = (float) p.getX() - lastX; + float dy = (float) p.getY() - lastY; + + float ax = (i == 0) ? 0.0f : glyphVector.getGlyphMetrics(i - 1).getAdvanceX(); + float ay = (i == 0) ? 0.0f : glyphVector.getGlyphMetrics(i - 1).getAdvanceY(); + + if (dx != ax || dy != ay) { + retVal = true; + break; + } + lastX = (float) p.getX(); + lastY = (float) p.getY(); + } + return retVal; + } + + /** + * Loads the AWT font needed + * + * @param pdType0Font PDFBOX font + * @param inputStream of the font file + * @throws RuntimeException if font can not be loaded + */ + public void loadAwtFont(PDType0Font pdType0Font, InputStream inputStream) { + Font awtFont = null; + try { + if (!awtFontMap.containsKey(pdType0Font)) { + awtFont = Font.createFont(java.awt.Font.TRUETYPE_FONT, inputStream); + if (awtFont == null) { + throw new RuntimeException("Font is null"); + } + awtFontMap.put(pdType0Font, awtFont); + } + } catch (Exception e) { + throw new RuntimeException(String.format("AWT Font creation failed for %s.", pdType0Font.getName()), e); + } finally { + try { + inputStream.close(); + } catch (Exception e) { + //ignore + } + } + } + + /** + * Computes glyph positioning + * + * @param text input text + * @return glyph vector containing reordered text, width and positioning info + */ + public GlyphVector computeGlyphVector(String text) { + char[] chars = text.toCharArray(); + + FontRenderContext fontRenderContext = new FontRenderContext(new AffineTransform(), false, true); + // use fractional metrics + AttributedString as = new AttributedString(text); + Bidi bidi = new Bidi(as.getIterator()); + int localFlags = bidi.isLeftToRight() ? java.awt.Font.LAYOUT_LEFT_TO_RIGHT : java.awt.Font.LAYOUT_RIGHT_TO_LEFT; + + java.awt.Font awtFont = awtFontMap.get(font).deriveFont(fontSize); + + return awtFont.layoutGlyphVector(fontRenderContext, chars, 0, chars.length, localFlags); + } + + + /** + * Sets the font and fontSize + * + * @param font to be set + * @param fontSize font size + */ + public void setFontAndSize(PDType0Font font, float fontSize) { + this.font = font; + this.fontSize = fontSize; + } + + /** + * Shows a text using glyph positioning (if needed) + * + * @param text text to show + * @throws IOException in case of IO-error + */ + public void showText(String text) throws IOException { + GlyphVector glyphVector = computeGlyphVector(text); + if (!hasAdjustments(glyphVector)) { + contentStream.showText(text); + return; + } + + final float delta = 1e-5f; + final float factorX = 1000f / fontSize; + float lastX = 0f; + char[] characters = text.toCharArray(); + + CharactersAndPositions ga = new CharactersAndPositions(); + + for (int i = 0; i < glyphVector.getNumGlyphs(); i++) { + Point2D p = glyphVector.getGlyphPosition(i); + float ax = (i == 0) ? 0.0f : glyphVector.getGlyphMetrics(i - 1).getAdvanceX(); + float dx = (float) p.getX() - lastX - ax; + float py = (float) p.getY(); + + if (Math.abs(py) >= delta) { + if (!ga.isEmpty()) { + showCharactersWithPositioning(contentStream, ga); + ga.clear(); + } + contentStream.setTextRise(-py); + } + if (Math.abs(dx) >= delta) { + ga.add(-dx * factorX); + } + + // There is no method in PDPageContentStream to position + // glyphs using the glyph id, so we have to use the + // characters. + ga.add(characters[i]); + if (Math.abs(py) >= delta) { + showCharactersWithPositioning(contentStream, ga); + ga.clear(); + contentStream.setTextRise(0.0f); + } + lastX = (float) p.getX(); + } + // adjust the end position + Point2D p = glyphVector.getGlyphPosition(glyphVector.getNumGlyphs()); + float ax = (glyphVector.getNumGlyphs() == 0) ? 0.0f + : glyphVector.getGlyphMetrics(glyphVector.getNumGlyphs() - 1).getAdvanceX(); + float dx = (float) p.getX() - lastX - ax; + if (Math.abs(dx) >= delta) { + ga.add(-dx * factorX); + } + showCharactersWithPositioning(contentStream, ga); + ga.clear(); + } + + /** + * Show the given characters at the given positions + * @param contentStream to show the text + * @param charactersAndPositions List of characters and positions + * @throws IOException if an IO error occurs + */ + public void showCharactersWithPositioning(PDPageContentStream contentStream, CharactersAndPositions charactersAndPositions) throws IOException { + ArrayList textWithPositioning = new ArrayList<>(); + for (Object obj : charactersAndPositions.toArray()) { + if (obj instanceof CharactersAndPositions.GlyphSubList) { + CharactersAndPositions.GlyphSubList glyphSubList = (CharactersAndPositions.GlyphSubList) obj; + StringBuilder sb = new StringBuilder(); + for (Object o : glyphSubList.toArray()) { + sb.append(o); + } + textWithPositioning.add(sb.toString()); + } else if (obj instanceof Float) { + textWithPositioning.add(obj); + } else { + if (obj == null) { + throw new NullPointerException("Argument contains null entry"); + } + throw new IllegalArgumentException("Argument must consist of array of Float and CharactersAndPositions.GlyphSubList types, not " + obj.getClass().getName()); + } + } + contentStream.showTextWithPositioning(textWithPositioning.toArray()); + } +} diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/ShowCharactersOfDin91379.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/ShowCharactersOfDin91379.java new file mode 100644 index 00000000000..06971f47005 --- /dev/null +++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphposition/ShowCharactersOfDin91379.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Volker Kunert 2026 + */ + +package org.apache.pdfbox.examples.pdmodel.glyphposition; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDType0Font; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Example of formatting for letters defined in: DIN 91379: Characters in Unicode for the electronic processing + * of names and data exchange in Europe + *

+ * Font used: Arimo-Regular.ttf, see + * https://fonts.google.com/specimen/Arimo + *

+ * Use of the positioning features of Java. + */ +public class ShowCharactersOfDin91379 { + public static String LATIN_CHARS_DIN_91379 = + "DIN 91379: Characters in Unicode for the electronic processing of names \n" + + "and data exchange in Europe\n" + + "Font used: Arimo-Regular.ttf\n" + + "\n" + + "bll; Latin Letters (normative)\n" + + "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z\n" + + "À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó\n" + + "ô õ ö ø ù ú û ü ý þ ÿ Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě Ĝ ĝ Ğ ğ Ġ ġ Ģ ģ Ĥ ĥ Ħ\n" + + "ħ Ĩ ĩ Ī ī Ĭ ĭ Į į İ ı IJ ij Ĵ ĵ Ķ ķ ĸ Ĺ ĺ Ļ ļ Ľ ľ Ŀ ŀ Ł ł Ń ń Ņ ņ Ň ň ʼn Ŋ ŋ Ō ō Ŏ ŏ Ő ő Œ œ Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ŝ ŝ Ş\n" + + "ş Š š Ţ ţ Ť ť Ŧ ŧ Ũ ũ Ū ū Ŭ ŭ Ů ů Ű ű Ų ų Ŵ ŵ Ŷ ŷ Ÿ Ź ź Ż ż Ž ž Ƈ ƈ Ə Ɨ Ơ ơ Ư ư Ʒ Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ Ǖ ǖ\n" + + "Ǘ ǘ Ǚ ǚ Ǜ ǜ Ǟ ǟ Ǣ ǣ Ǥ ǥ Ǧ ǧ Ǩ ǩ Ǫ ǫ Ǭ ǭ Ǯ ǯ ǰ Ǵ ǵ Ǹ ǹ Ǻ ǻ Ǽ ǽ Ǿ ǿ Ȓ ȓ Ș ș Ț ț Ȟ ȟ ȧ Ȩ ȩ Ȫ ȫ Ȭ ȭ\n" + + "Ȯ ȯ Ȱ ȱ Ȳ ȳ ə ɨ ʒ Ḃ ḃ Ḇ ḇ Ḋ ḋ Ḍ ḍ Ḏ ḏ Ḑ ḑ ḗ Ḝ ḝ Ḟ ḟ Ḡ ḡ Ḣ ḣ Ḥ ḥ Ḧ ḧ Ḩ ḩ Ḫ ḫ ḯ Ḱ ḱ Ḳ ḳ Ḵ ḵ Ḷ ḷ Ḻ ḻ Ṁ\n" + + "ṁ Ṃ ṃ Ṅ ṅ Ṇ ṇ Ṉ ṉ Ṓ ṓ Ṕ ṕ Ṗ ṗ Ṙ ṙ Ṛ ṛ Ṟ ṟ Ṡ ṡ Ṣ ṣ Ṫ ṫ Ṭ ṭ Ṯ ṯ Ẁ ẁ Ẃ ẃ Ẅ ẅ Ẇ ẇ Ẍ ẍ Ẏ ẏ Ẑ ẑ Ẓ ẓ Ẕ ẕ\n" + + "ẖ ẗ ẞ Ạ ạ Ả ả Ấ ấ Ầ ầ Ẩ ẩ Ẫ ẫ Ậ ậ Ắ ắ Ằ ằ Ẳ ẳ Ẵ ẵ Ặ ặ Ẹ ẹ Ẻ ẻ Ẽ ẽ Ế ế Ề ề Ể ể Ễ ễ Ệ ệ Ỉ ỉ Ị ị Ọ ọ Ỏ ỏ Ố\n" + + "ố Ồ ồ Ổ ổ Ỗ ỗ Ộ ộ Ớ ớ Ờ ờ Ở ở Ỡ ỡ Ợ ợ Ụ ụ Ủ ủ Ứ ứ Ừ ừ Ử ử Ữ ữ Ự ự Ỳ ỳ Ỵ ỵ Ỷ ỷ Ỹ ỹ\n" + + "Sequences\n" + + "A̋ C̀ C̄ C̆ C̈ C̕ C̣ C̦ C̨̆ D̂ F̀ F̄ G̀ H̄ H̦ H̱ J́ J̌ K̀ K̂ K̄ K̇ K̕ K̛ K̦ K͟H \n" + + "K͟h L̂ L̥ L̥̄ L̦ M̀ M̂ M̆ M̐ N̂ N̄ N̆ N̦ P̀ P̄ P̕ P̣ R̆ R̥ R̥̄ S̀ S̄ S̛̄ S̱ T̀ T̄ \n" + + "T̈ T̕ T̛ U̇ Z̀ Z̄ Z̆ Z̈ Z̧ a̋ c̀ c̄ c̆ c̈ c̕ c̣ c̦ c̨̆ d̂ f̀ f̄ g̀ h̄ h̦ j́ k̀ \n" + + "k̂ k̄ k̇ k̕ k̛ k̦ k͟h l̂ l̥ l̥̄ l̦ m̀ m̂ m̆ m̐ n̂ n̄ n̆ n̦ p̀ p̄ p̕ p̣ r̆ r̥ r̥̄ \n" + + "s̀ s̄ s̛̄ s̱ t̀ t̄ t̕ t̛ u̇ z̀ z̄ z̆ z̈ z̧ Ç̆ Û̄ ç̆ û̄ ÿ́ Č̕ Č̣ č̕ č̣ ē̍ Ī́ ī́ \n" + + "ō̍ Ž̦ Ž̧ ž̦ ž̧ Ḳ̄ ḳ̄ Ṣ̄ ṣ̄ Ṭ̄ ṭ̄ Ạ̈ ạ̈ Ọ̈ ọ̈ Ụ̄ Ụ̈ ụ̄ ụ̈ \n" + + "bnlreq; Non-Letters N1 (normative)\n" + + " ' , - . ` ~ ¨ ´ · ʹ ʺ ʾ ʿ ˈ ˌ ’ ‡ \n" + + "bnl; Non-Letters N2 (normative)\n" + + "! \" # $ % & ( ) * + / 0 1 2 3 4 5 6 7 8 9 : ; < = > \n" + + "? @ [ \\ ] ^ _ { | } ¡ ¢ £ ¥ § © ª « ¬ ® ¯ ° ± ² ³ µ \n" + + "¶ ¹ º » ¿ × ÷ € \n" + + "bnlopt; Non-Letters N3 (normative)\n" + + "¤ ¦ ¸ ¼ ½ ¾ \n" + + "gl; Greek Letters (extended)\n" + + "Ά Έ Ή Ί Ό Ύ Ώ ΐ Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ \n" + + "Τ Υ Φ Χ Ψ Ω Ϊ Ϋ ά έ ή ί ΰ α β γ δ ε ζ η θ ι κ λ μ ν \n" + + "ξ ο π ρ ς σ τ υ φ χ ψ ω ϊ ϋ ό ύ ώ \n" + + "cl; Cyrillic Letters (extended)\n" + + "Ѝ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш \n" + + "Щ Ъ Ь Ю Я а б в г д е ж з и й к л м н о п р с т у ф \n" + + "х ц ч ш щ ъ ь ю я ѝ \n" + + "enl; Non-Letters E1 (extended)\n" + + "ƒ ʰ ʳ ˆ ˜ ˢ ᵈ ᵗ ‘ ‚ “ ” „ † … ‰ ′ ″ ‹ › ⁰ ⁴ ⁵ ⁶ ⁷ ⁸ \n" + + "⁹ ⁿ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ™ ∞ ≤ ≥ \n" + + "Additional non-letters (not included in DIN 91379): – — •�"; + + /* + * Start the test + */ + public static void main(String[] args) { + try { + + String outputFilename = "ShowCharactersOfDin91379.pdf"; + float fontSize = 12.0f; + + PDDocument pdDocument = new PDDocument(); + InputStream fontStream1 = ShowCharactersOfDin91379.class. + getResourceAsStream("/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf"); + PDType0Font font = PDType0Font.load(pdDocument, fontStream1); + // Duplicate loading necessary + InputStream fontStream2 = ShowCharactersOfDin91379.class. + getResourceAsStream("/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf"); + + + PDPage blankPage = new PDPage(); + pdDocument.addPage(blankPage); + PDPageContentStream cs = new PDPageContentStream(pdDocument, pdDocument.getPage(0), + PDPageContentStream.AppendMode.APPEND, true); + + GlyphPositioner glyphPositioner = new GlyphPositioner(cs); + glyphPositioner.loadAwtFont(font, fontStream2); + + float x = blankPage.getBBox().getLowerLeftX() + fontSize; + float y = blankPage.getBBox().getUpperRightY() - fontSize; + showComposites(glyphPositioner, cs, font, fontSize, x, y, LATIN_CHARS_DIN_91379); + cs.close(); + pdDocument.save(outputFilename); + pdDocument.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /* + * break the text into lines and show them + */ + public static void showComposites(GlyphPositioner glyphPositioner, PDPageContentStream cs, PDType0Font font, float fontSize, + float x, float y, String s) throws Exception { + + s = s.replaceAll("\t", " "); + String[] lines = s.split("[\n]"); + + float height = font.getBoundingBox().getHeight(); + + for (String line : lines) { + if (!line.isEmpty()) { + showCompositesLine(glyphPositioner, cs, font, fontSize, x, y, line); + y -= height / 1000f * fontSize; + } + } + } + + /* + * show one line + */ + public static void showCompositesLine(GlyphPositioner glyphPositioner, PDPageContentStream cs, PDType0Font font, float fontSize, + float x, float y, String line) throws IOException { + cs.beginText(); + cs.setFont(font, fontSize); + glyphPositioner.setFontAndSize(font, fontSize); + cs.newLineAtOffset(x, y); + glyphPositioner.showText(line); + cs.endText(); + } +} diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf new file mode 100644 index 00000000000..c14e0a41892 Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf differ diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/LICENSE.txt b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/LICENSE.txt new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.