sambox-1.1.19/000077500000000000000000000000001320103431700130665ustar00rootroot00000000000000sambox-1.1.19/.gitignore000066400000000000000000000002071320103431700150550ustar00rootroot00000000000000*.class .classpath .project .launch .settings/ target/ .externalToolBuilders/ # Package Files # *.jar *.war *.ear .idea/ *.iml *.iprsambox-1.1.19/.travis.yml000066400000000000000000000001341320103431700151750ustar00rootroot00000000000000language: java sudo: false jdk: - oraclejdk8 notifications: email: - info@sejda.org sambox-1.1.19/LICENSE000066400000000000000000000261361320103431700141030ustar00rootroot00000000000000 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. sambox-1.1.19/README.md000066400000000000000000000054121320103431700143470ustar00rootroot00000000000000SAMBox PDF processor ===================== [![Build Status](https://travis-ci.org/torakiki/sambox.png)](https://travis-ci.org/torakiki/sambox) [![License](http://img.shields.io/badge/license-APLv2-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) An [Apache PDFBox](https://github.com/apache/pdfbox) fork intended to be used as PDF processor for [Sejda](https://github.com/torakiki/sejda) and [PDFsam](https://github.com/torakiki/pdfsam) related projects What's different from PDFBox? --------- + Requires JDK8 + Lazy loading/parsing of PDF objects. Only the document xref table(s)/stream(s) is(are) initially parsed and information to lookup objects are retrieved, when later a PDF object is requested, the object is retrieve/parsed using the lookup information. This allows minimal memory footprint when you only need part of the document (Ex. you only need the information dictionary or the number of pages of the document). + Multiple I/O implementations to read from. SAMBox uses [Sejda-io](https://github.com/torakiki/sejda-io) allowing to use one of the provided implementation based on `java.nio.channels.FileChannel`, `java.io.InputStream` and `java.nio.MappedByteBuffer` (buffered or not). + Minimized GC through the use of a pool of `java.lang.StringBuilder`. + PDF streams are read directly from the underlying source through the concept of bounded views. + Use and discard of lazy PDF objects. PDF objects can be written (sync/async) and discarded as soon as they have been written, this is particularly useful with existing documents where objects are lazy loaded, written and then discarded, keeping a low memory footprint. + All the I/O and parsing logic has been refactored to smaller classes which are nearly 100% unit tested. + Some of the PDFBox features are currently of no use for Sejda or PDFsam and they have been removed from SAMBox (preflight validator, fdf, digital signature... ). + Documents can be saved using objects stream to store PDF objects. Are PDFBox commits merged to SAMBox? --------- SAMBox is a fork of a SNAPSHOT of PDFBox 2.0.0 and we try to keep it aligned with it. We performed massive changes on the original codebase and the same did the PDFBox guys since the time of the forking so merging back stuff from the PDFBox trunk is sometime challenging, we do our best. How do I load a document? --------- Here is a snippet to load a document from a file and write it back to a newFile. ``` try(PDDocument document = PDFParser.parse(SeekableSources.seekableSourceFrom(file))){ document.writeTo(newFile, WriteOption.XREF_STREAM); } ``` Tuning --------- Some system properties are available to modify SAMBox default behaviour. Take a look at `org.sejda.io.SeekableSources` and `org.sejda.sambox.SAMBox` to find out which are currently available.sambox-1.1.19/development/000077500000000000000000000000001320103431700154105ustar00rootroot00000000000000sambox-1.1.19/development/eclipse/000077500000000000000000000000001320103431700170345ustar00rootroot00000000000000sambox-1.1.19/development/eclipse/checkstyle plugin/000077500000000000000000000000001320103431700224515ustar00rootroot00000000000000sambox-1.1.19/development/eclipse/checkstyle plugin/checkstyle.xml000066400000000000000000000105061320103431700253330ustar00rootroot00000000000000 sambox-1.1.19/development/eclipse/checkstyle plugin/suppression.xml000066400000000000000000000003611320103431700255650ustar00rootroot00000000000000 sambox-1.1.19/development/eclipse/codetemplates.xml000066400000000000000000000125471320103431700224200ustar00rootroot00000000000000sambox-1.1.19/development/eclipse/eclipse_compiler_settings.epf000066400000000000000000000053451320103431700247750ustar00rootroot00000000000000#Sat Jul 23 15:11:52 CEST 2011 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error \!/= /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.emptyStatement=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.enumIdentifier=error /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.fieldHiding=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.assertIdentifier=error /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=disabled /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=disabled /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.compliance=1.6 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedImport=error /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.source=1.6 file_export_version=3.0 @org.eclipse.jdt.core=3.6.2.v_A76_R36x sambox-1.1.19/development/eclipse/pdfbox-eclipse-formatter.xml000066400000000000000000000756671320103431700245100ustar00rootroot00000000000000 sambox-1.1.19/pom.xml000066400000000000000000000171231320103431700144070ustar00rootroot00000000000000 4.0.0 org.sejda sambox jar sambox 1.1.19 An Apache PDFBox fork intended to be used as PDF processor for Sejda and PDFsam related projects http://www.sejda.org GitHub https://github.com/torakiki/sambox/issues sejda http://www.sejda.org Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0 repo ASLv2 scm:git:git@github.com:torakiki/sambox.git scm:git:git@github.com:torakiki/sambox.git scm:git:git@github.com:torakiki/sambox.git v1.1.19 torakiki Andrea Vacondio andrea.vacondio@gmail.com ediweissmann Eduard Weissmann edi.weissmann@gmail.com UTF-8 false pdfbox-snapshot http://repository.apache.org/snapshots/ true sonatype-snapshot https://oss.sonatype.org/content/repositories/snapshots/ true sonatype-nexus-snapshots https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ release org.apache.maven.plugins maven-javadoc-plugin 2.10.3 attach-javadocs jar org.apache.maven.plugins maven-release-plugin 2.5.2 v@{project.version} clean install true org.apache.maven.plugins maven-gpg-plugin 1.6 sign-artifacts verify sign org.sonatype.plugins nexus-staging-maven-plugin 1.6.7 true ossrh https://oss.sonatype.org/ true src/main/resources true **/*.properties src/main/resources false **/*.properties org.apache.maven.plugins maven-compiler-plugin 3.3 1.8 1.8 true lines,vars,source org.apache.maven.plugins maven-jar-plugin 2.6 org.apache.maven.plugins maven-source-plugin 2.4 attach-sources jar org.apache.maven.plugins maven-surefire-plugin 2.19.1 -Xmx768m org/sejda/sambox/rendering/TestPDFToImage.java 1C false org.slf4j slf4j-api 1.7.25 org.slf4j jcl-over-slf4j 1.7.25 org.sejda sejda-io 1.1.3.RELEASE commons-io commons-io 2.5 org.apache.pdfbox fontbox 2.0.8 commons-logging commons-logging org.bouncycastle bcmail-jdk15on true 1.56 org.bouncycastle bcprov-jdk15on true 1.56 ch.qos.logback logback-classic 1.2.2 test org.mockito mockito-core 1.10.19 test junit junit 4.12 test org.hamcrest hamcrest-core 1.3 test com.googlecode.java-diff-utils diffutils 1.3.0 test nl.jqno.equalsverifier equalsverifier 1.7.5 test sambox-1.1.19/src/000077500000000000000000000000001320103431700136555ustar00rootroot00000000000000sambox-1.1.19/src/main/000077500000000000000000000000001320103431700146015ustar00rootroot00000000000000sambox-1.1.19/src/main/java/000077500000000000000000000000001320103431700155225ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/000077500000000000000000000000001320103431700163115ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/000077500000000000000000000000001320103431700173775ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/000077500000000000000000000000001320103431700206705ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/SAMBox.java000066400000000000000000000027531320103431700226330ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox; /** * Holder for configurable system properties. This is supposed to give developers a single place where they can find out * what's configurable in SAMBox. * * @author Andrea Vacondio */ public final class SAMBox { /** * Pool size used in org.sejda.sambox.input.SourceReader */ public static final String BUFFERS_POOL_SIZE_PROPERTY = "org.sejda.sambox.buffers.pool.size"; /** * The number of objects to include in a single ObjectsStream. */ public static final String OBJECTS_STREAM_SIZE_PROPERTY = "org.sejda.sambox.objects.stream.size"; public static final String SAMBOX_PROPERTIES = "org/sejda/sambox/resources/version.properties"; } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/000077500000000000000000000000001320103431700235565ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/PDContentStream.java000066400000000000000000000031741320103431700274400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream; import java.io.IOException; import java.io.InputStream; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.util.Matrix; /** * A content stream. * * @author John Hewson */ public interface PDContentStream { /** * Returns this stream's content, if any. * * @return An InputStream or null. * @throws IOException If the stream could not be read */ InputStream getContents() throws IOException; /** * Returns this stream's resources, if any. */ PDResources getResources(); /** * Returns the bounding box of the contents. */ PDRectangle getBBox(); /** * Returns the matrix which transforms from the stream's space to user space. */ Matrix getMatrix(); } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/PDFGraphicsStreamEngine.java000066400000000000000000000262671320103431700310320ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream; import java.awt.geom.Point2D; import java.io.IOException; import org.sejda.sambox.contentstream.operator.color.SetNonStrokingColor; import org.sejda.sambox.contentstream.operator.color.SetNonStrokingColorN; import org.sejda.sambox.contentstream.operator.color.SetNonStrokingColorSpace; import org.sejda.sambox.contentstream.operator.color.SetNonStrokingDeviceCMYKColor; import org.sejda.sambox.contentstream.operator.color.SetNonStrokingDeviceGrayColor; import org.sejda.sambox.contentstream.operator.color.SetNonStrokingDeviceRGBColor; import org.sejda.sambox.contentstream.operator.color.SetStrokingColor; import org.sejda.sambox.contentstream.operator.color.SetStrokingColorN; import org.sejda.sambox.contentstream.operator.color.SetStrokingColorSpace; import org.sejda.sambox.contentstream.operator.color.SetStrokingDeviceCMYKColor; import org.sejda.sambox.contentstream.operator.color.SetStrokingDeviceGrayColor; import org.sejda.sambox.contentstream.operator.color.SetStrokingDeviceRGBColor; import org.sejda.sambox.contentstream.operator.graphics.AppendRectangleToPath; import org.sejda.sambox.contentstream.operator.graphics.BeginInlineImage; import org.sejda.sambox.contentstream.operator.graphics.ClipEvenOddRule; import org.sejda.sambox.contentstream.operator.graphics.ClipNonZeroRule; import org.sejda.sambox.contentstream.operator.graphics.CloseAndStrokePath; import org.sejda.sambox.contentstream.operator.graphics.CloseFillEvenOddAndStrokePath; import org.sejda.sambox.contentstream.operator.graphics.CloseFillNonZeroAndStrokePath; import org.sejda.sambox.contentstream.operator.graphics.ClosePath; import org.sejda.sambox.contentstream.operator.graphics.CurveTo; import org.sejda.sambox.contentstream.operator.graphics.CurveToReplicateFinalPoint; import org.sejda.sambox.contentstream.operator.graphics.CurveToReplicateInitialPoint; import org.sejda.sambox.contentstream.operator.graphics.DrawObject; import org.sejda.sambox.contentstream.operator.graphics.EndPath; import org.sejda.sambox.contentstream.operator.graphics.FillEvenOddAndStrokePath; import org.sejda.sambox.contentstream.operator.graphics.FillEvenOddRule; import org.sejda.sambox.contentstream.operator.graphics.FillNonZeroAndStrokePath; import org.sejda.sambox.contentstream.operator.graphics.FillNonZeroRule; import org.sejda.sambox.contentstream.operator.graphics.LegacyFillNonZeroRule; import org.sejda.sambox.contentstream.operator.graphics.LineTo; import org.sejda.sambox.contentstream.operator.graphics.MoveTo; import org.sejda.sambox.contentstream.operator.graphics.ShadingFill; import org.sejda.sambox.contentstream.operator.graphics.StrokePath; import org.sejda.sambox.contentstream.operator.state.Concatenate; import org.sejda.sambox.contentstream.operator.state.Restore; import org.sejda.sambox.contentstream.operator.state.Save; import org.sejda.sambox.contentstream.operator.state.SetFlatness; import org.sejda.sambox.contentstream.operator.state.SetGraphicsStateParameters; import org.sejda.sambox.contentstream.operator.state.SetLineCapStyle; import org.sejda.sambox.contentstream.operator.state.SetLineDashPattern; import org.sejda.sambox.contentstream.operator.state.SetLineJoinStyle; import org.sejda.sambox.contentstream.operator.state.SetLineMiterLimit; import org.sejda.sambox.contentstream.operator.state.SetLineWidth; import org.sejda.sambox.contentstream.operator.state.SetMatrix; import org.sejda.sambox.contentstream.operator.state.SetRenderingIntent; import org.sejda.sambox.contentstream.operator.text.BeginText; import org.sejda.sambox.contentstream.operator.text.EndText; import org.sejda.sambox.contentstream.operator.text.MoveText; import org.sejda.sambox.contentstream.operator.text.MoveTextSetLeading; import org.sejda.sambox.contentstream.operator.text.NextLine; import org.sejda.sambox.contentstream.operator.text.SetCharSpacing; import org.sejda.sambox.contentstream.operator.text.SetFontAndSize; import org.sejda.sambox.contentstream.operator.text.SetTextHorizontalScaling; import org.sejda.sambox.contentstream.operator.text.SetTextLeading; import org.sejda.sambox.contentstream.operator.text.SetTextRenderingMode; import org.sejda.sambox.contentstream.operator.text.SetTextRise; import org.sejda.sambox.contentstream.operator.text.SetWordSpacing; import org.sejda.sambox.contentstream.operator.text.ShowText; import org.sejda.sambox.contentstream.operator.text.ShowTextAdjusted; import org.sejda.sambox.contentstream.operator.text.ShowTextLine; import org.sejda.sambox.contentstream.operator.text.ShowTextLineAndSpace; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.graphics.image.PDImage; /** * PDFStreamEngine subclass for advanced processing of graphics. * This class should be subclasses by end users looking to hook into graphics operations. * * @author John Hewson */ public abstract class PDFGraphicsStreamEngine extends PDFStreamEngine { // may be null, for example if the stream is a tiling pattern private final PDPage page; /** * Constructor. */ protected PDFGraphicsStreamEngine(PDPage page) { this.page = page; addOperator(new CloseFillNonZeroAndStrokePath()); addOperator(new FillNonZeroAndStrokePath()); addOperator(new CloseFillEvenOddAndStrokePath()); addOperator(new FillEvenOddAndStrokePath()); addOperator(new BeginInlineImage()); addOperator(new BeginText()); addOperator(new CurveTo()); addOperator(new Concatenate()); addOperator(new SetStrokingColorSpace()); addOperator(new SetNonStrokingColorSpace()); addOperator(new SetLineDashPattern()); addOperator(new DrawObject()); // special graphics version addOperator(new EndText()); addOperator(new FillNonZeroRule()); addOperator(new LegacyFillNonZeroRule()); addOperator(new FillEvenOddRule()); addOperator(new SetStrokingDeviceGrayColor()); addOperator(new SetNonStrokingDeviceGrayColor()); addOperator(new SetGraphicsStateParameters()); addOperator(new ClosePath()); addOperator(new SetFlatness()); addOperator(new SetLineJoinStyle()); addOperator(new SetLineCapStyle()); addOperator(new SetStrokingDeviceCMYKColor()); addOperator(new SetNonStrokingDeviceCMYKColor()); addOperator(new LineTo()); addOperator(new MoveTo()); addOperator(new SetLineMiterLimit()); addOperator(new EndPath()); addOperator(new Save()); addOperator(new Restore()); addOperator(new AppendRectangleToPath()); addOperator(new SetStrokingDeviceRGBColor()); addOperator(new SetNonStrokingDeviceRGBColor()); addOperator(new SetRenderingIntent()); addOperator(new CloseAndStrokePath()); addOperator(new StrokePath()); addOperator(new SetStrokingColor()); addOperator(new SetNonStrokingColor()); addOperator(new SetStrokingColorN()); addOperator(new SetNonStrokingColorN()); addOperator(new ShadingFill()); addOperator(new NextLine()); addOperator(new SetCharSpacing()); addOperator(new MoveText()); addOperator(new MoveTextSetLeading()); addOperator(new SetFontAndSize()); addOperator(new ShowText()); addOperator(new ShowTextAdjusted()); addOperator(new SetTextLeading()); addOperator(new SetMatrix()); addOperator(new SetTextRenderingMode()); addOperator(new SetTextRise()); addOperator(new SetWordSpacing()); addOperator(new SetTextHorizontalScaling()); addOperator(new CurveToReplicateInitialPoint()); addOperator(new SetLineWidth()); addOperator(new ClipNonZeroRule()); addOperator(new ClipEvenOddRule()); addOperator(new CurveToReplicateFinalPoint()); addOperator(new ShowTextLine()); addOperator(new ShowTextLineAndSpace()); } /** * Returns the page. */ protected final PDPage getPage() { return page; } /** * Append a rectangle to the current path. */ public abstract void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException; /** * Draw the image. * * @param pdImage The image to draw. */ public abstract void drawImage(PDImage pdImage) throws IOException; /** * Modify the current clipping path by intersecting it with the current path. * The clipping path will not be updated until the succeeding painting operator is called. * * @param windingRule The winding rule which will be used for clipping. */ public abstract void clip(int windingRule) throws IOException; /** * Starts a new path at (x,y). */ public abstract void moveTo(float x, float y) throws IOException; /** * Draws a line from the current point to (x,y). */ public abstract void lineTo(float x, float y) throws IOException; /** * Draws a curve from the current point to (x3,y3) using (x1,y1) and (x2,y2) as control points. */ public abstract void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException; /** * Returns the current point of the current path. */ public abstract Point2D getCurrentPoint() throws IOException; /** * Closes the current path. */ public abstract void closePath() throws IOException; /** * Ends the current path without filling or stroking it. The clipping path is updated here. */ public abstract void endPath() throws IOException; /** * Stroke the path. * * @throws IOException If there is an IO error while stroking the path. */ public abstract void strokePath() throws IOException; /** * Fill the path. * * @param windingRule The winding rule this path will use. */ public abstract void fillPath(int windingRule) throws IOException; /** * Fills and then strokes the path. * * @param windingRule The winding rule this path will use. */ public abstract void fillAndStrokePath(int windingRule) throws IOException; /** * Fill with Shading. * * @param shadingName The name of the Shading Dictionary to use for this fill instruction. */ public abstract void shadingFill(COSName shadingName) throws IOException; } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/PDFStreamEngine.java000066400000000000000000001025061320103431700273400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream; import static java.util.Optional.ofNullable; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.contentstream.operator.state.EmptyGraphicsStackException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.filter.MissingImageReaderException; import org.sejda.sambox.input.ContentStreamParser; import org.sejda.sambox.pdmodel.MissingResourceException; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.font.PDFontFactory; import org.sejda.sambox.pdmodel.font.PDType3CharProc; import org.sejda.sambox.pdmodel.font.PDType3Font; import org.sejda.sambox.pdmodel.graphics.PDLineDashPattern; import org.sejda.sambox.pdmodel.graphics.blend.BlendMode; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; import org.sejda.sambox.pdmodel.graphics.form.PDTransparencyGroup; import org.sejda.sambox.pdmodel.graphics.pattern.PDTilingPattern; import org.sejda.sambox.pdmodel.graphics.state.PDGraphicsState; import org.sejda.sambox.pdmodel.graphics.state.PDTextState; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotation; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceStream; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Processes a PDF content stream and executes certain operations. Provides a callback interface for clients that want * to do things with the stream. * * @author Ben Litchfield */ public abstract class PDFStreamEngine { private static final Logger LOG = LoggerFactory.getLogger(PDFStreamEngine.class); private final Map operators = new HashMap<>(80); private Matrix textMatrix; private Matrix textLineMatrix; private Stack graphicsStack = new Stack<>(); private PDResources resources; private PDPage currentPage; private boolean isProcessingPage; private Matrix initialMatrix; /** * Creates a new PDFStreamEngine. */ protected PDFStreamEngine() { } /** * Adds an operator processor to the engine. * * @param op operator processor */ public final void addOperator(OperatorProcessor op) { op.setContext(this); operators.put(op.getName(), op); } /** * Initialises the stream engine for the given page. */ private void initPage(PDPage page) { if (page == null) { throw new IllegalArgumentException("Page cannot be null"); } currentPage = page; graphicsStack.clear(); graphicsStack.push(new PDGraphicsState(page.getCropBox())); textMatrix = null; textLineMatrix = null; resources = null; initialMatrix = page.getMatrix(); } /** * This will initialise and process the contents of the stream. * * @param page the page to process * @throws IOException if there is an error accessing the stream */ public void processPage(PDPage page) throws IOException { initPage(page); if (page.hasContents()) { isProcessingPage = true; processStream(page); isProcessingPage = false; } } /** * Shows a transparency group from the content stream. * * @param form transparency group (form) XObject * @throws IOException if the transparency group cannot be processed */ public void showTransparencyGroup(PDTransparencyGroup form) throws IOException { processTransparencyGroup(form); } /** * Shows a form from the content stream. * * @param form form XObject * @throws IOException if the form cannot be processed */ public void showForm(PDFormXObject form) throws IOException { if (currentPage == null) { throw new IllegalStateException("No current page, call " + "#processChildStream(PDContentStream, PDPage) instead"); } processStream(form); } /** * Processes a soft mask transparency group stream. * * @param group * @throws IOException */ protected void processSoftMask(PDTransparencyGroup group) throws IOException { saveGraphicsState(); Matrix softMaskCTM = getGraphicsState().getSoftMask().getInitialTransformationMatrix(); getGraphicsState().setCurrentTransformationMatrix(softMaskCTM); processTransparencyGroup(group); restoreGraphicsState(); } /** * Processes a transparency group stream. * * @param group * @throws IOException */ protected void processTransparencyGroup(PDTransparencyGroup group) throws IOException { if (currentPage == null) { throw new IllegalStateException("No current page, call " + "#processChildStream(PDContentStream, PDPage) instead"); } PDResources parent = pushResources(group); Stack savedStack = saveGraphicsStack(); Matrix parentMatrix = initialMatrix; // the stream's initial matrix includes the parent CTM, e.g. this allows a scaled form initialMatrix = getGraphicsState().getCurrentTransformationMatrix().clone(); // transform the CTM using the stream's matrix getGraphicsState().getCurrentTransformationMatrix().concatenate(group.getMatrix()); // Before execution of the transparency group XObject’s content stream, // the current blend mode in the graphics state shall be initialized to Normal, // the current stroking and nonstroking alpha constants to 1.0, and the current soft mask to None. getGraphicsState().setBlendMode(BlendMode.NORMAL); getGraphicsState().setAlphaConstant(1); getGraphicsState().setNonStrokeAlphaConstants(1); getGraphicsState().setSoftMask(null); // clip to bounding box clipToRect(group.getBBox()); processStreamOperators(group); initialMatrix = parentMatrix; restoreGraphicsStack(savedStack); popResources(parent); } /** * Processes a Type 3 character stream. * * @param charProc Type 3 character procedure * @param textRenderingMatrix the Text Rendering Matrix */ protected void processType3Stream(PDType3CharProc charProc, Matrix textRenderingMatrix) throws IOException { if (currentPage == null) { throw new IllegalStateException("No current page, call " + "#processChildStream(PDContentStream, PDPage) instead"); } PDResources parent = pushResources(charProc); Stack savedStack = saveGraphicsStack(); // replace the CTM with the TRM getGraphicsState().setCurrentTransformationMatrix(textRenderingMatrix); // transform the CTM using the stream's matrix (this is the FontMatrix) getGraphicsState().getCurrentTransformationMatrix().concatenate(charProc.getMatrix()); // note: we don't clip to the BBox as it is often wrong, see PDFBOX-1917 // save text matrices (Type 3 stream may contain BT/ET, see PDFBOX-2137) Matrix textMatrixOld = textMatrix; textMatrix = new Matrix(); Matrix textLineMatrixOld = textLineMatrix; textLineMatrix = new Matrix(); processStreamOperators(charProc); // restore text matrices textMatrix = textMatrixOld; textLineMatrix = textLineMatrixOld; restoreGraphicsStack(savedStack); popResources(parent); } /** * Process the given annotation with the specified appearance stream. * * @param annotation The annotation containing the appearance stream to process. * @param appearance The appearance stream to process. */ protected void processAnnotation(PDAnnotation annotation, PDAppearanceStream appearance) throws IOException { PDResources parent = pushResources(appearance); Stack savedStack = saveGraphicsStack(); PDRectangle bbox = appearance.getBBox(); PDRectangle rect = annotation.getRectangle(); Matrix matrix = appearance.getMatrix(); // zero-sized rectangles are not valid if (rect != null && rect.getWidth() > 0 && rect.getHeight() > 0 && bbox != null) { // transformed appearance box fixme: may be an arbitrary shape Rectangle2D transformedBox = bbox.transform(matrix).getBounds2D(); // compute a matrix which scales and translates the transformed appearance box to align // with the edges of the annotation's rectangle Matrix a = Matrix.getTranslateInstance(rect.getLowerLeftX(), rect.getLowerLeftY()); a.concatenate( Matrix.getScaleInstance((float) (rect.getWidth() / transformedBox.getWidth()), (float) (rect.getHeight() / transformedBox.getHeight()))); a.concatenate(Matrix.getTranslateInstance((float) -transformedBox.getX(), (float) -transformedBox.getY())); // // HOWEVER only the opposite order works for rotated pages with // filled fields / annotations that have a matrix in the appearance stream, see PDFBOX-3083 Matrix aa = Matrix.concatenate(a, matrix); // make matrix AA the CTM getGraphicsState().setCurrentTransformationMatrix(aa); // clip to bounding box clipToRect(bbox); // needed for patterns in appearance streams, e.g. PDFBOX-2182 initialMatrix = aa.clone(); processStreamOperators(appearance); } restoreGraphicsStack(savedStack); popResources(parent); } /** * Process the given tiling pattern. * * @param tilingPattern the tiling pattern * @param color color to use, if this is an uncoloured pattern, otherwise null. * @param colorSpace color space to use, if this is an uncoloured pattern, otherwise null. */ protected final void processTilingPattern(PDTilingPattern tilingPattern, PDColor color, PDColorSpace colorSpace) throws IOException { processTilingPattern(tilingPattern, color, colorSpace, tilingPattern.getMatrix()); } /** * Process the given tiling pattern. Allows the pattern matrix to be overridden for custom rendering. * * @param tilingPattern the tiling pattern * @param color color to use, if this is an uncoloured pattern, otherwise null. * @param colorSpace color space to use, if this is an uncoloured pattern, otherwise null. * @param patternMatrix the pattern matrix, may be overridden for custom rendering. */ protected final void processTilingPattern(PDTilingPattern tilingPattern, PDColor color, PDColorSpace colorSpace, Matrix patternMatrix) throws IOException { PDResources parent = pushResources(tilingPattern); Matrix parentMatrix = initialMatrix; initialMatrix = Matrix.concatenate(initialMatrix, patternMatrix); // save the original graphics state Stack savedStack = saveGraphicsStack(); // save a clean state (new clipping path, line path, etc.) Rectangle2D bbox = tilingPattern.getBBox().transform(patternMatrix).getBounds2D(); PDRectangle rect = new PDRectangle((float) bbox.getX(), (float) bbox.getY(), (float) bbox.getWidth(), (float) bbox.getHeight()); graphicsStack.push(new PDGraphicsState(rect)); // non-colored patterns have to be given a color if (colorSpace != null) { color = new PDColor(color.getComponents(), colorSpace); getGraphicsState().setNonStrokingColorSpace(colorSpace); getGraphicsState().setNonStrokingColor(color); getGraphicsState().setStrokingColorSpace(colorSpace); getGraphicsState().setStrokingColor(color); } // transform the CTM using the stream's matrix getGraphicsState().getCurrentTransformationMatrix().concatenate(patternMatrix); // clip to bounding box clipToRect(tilingPattern.getBBox()); processStreamOperators(tilingPattern); initialMatrix = parentMatrix; restoreGraphicsStack(savedStack); popResources(parent); } /** * Shows the given annotation. * * @param annotation An annotation on the current page. * @throws IOException If an error occurred reading the annotation */ public void showAnnotation(PDAnnotation annotation) throws IOException { PDAppearanceStream appearanceStream = getAppearance(annotation); if (appearanceStream != null) { processAnnotation(annotation, appearanceStream); } } /** * Returns the appearance stream to process for the given annotation. May be used to render a specific appearance * such as "hover". * * @param annotation The current annotation. * @return The stream to process. */ public PDAppearanceStream getAppearance(PDAnnotation annotation) { return annotation.getNormalAppearanceStream(); } /** * Process a child stream of the given page. Cannot be used with #processPage(PDPage). * * @param contentStream the child content stream * @throws IOException if there is an exception while processing the stream */ protected void processChildStream(PDContentStream contentStream, PDPage page) throws IOException { if (isProcessingPage) { throw new IllegalStateException("Current page has already been set via " + " #processPage(PDPage) call #processChildStream(PDContentStream) instead"); } initPage(page); processStream(contentStream); currentPage = null; } /** * Process a content stream. * * @param contentStream the content stream * @throws IOException if there is an exception while processing the stream */ protected void processStream(PDContentStream contentStream) throws IOException { PDResources parent = pushResources(contentStream); Stack savedStack = saveGraphicsStack(); Matrix parentMatrix = initialMatrix; // transform the CTM using the stream's matrix getGraphicsState().getCurrentTransformationMatrix().concatenate(contentStream.getMatrix()); // the stream's initial matrix includes the parent CTM, e.g. this allows a scaled form initialMatrix = getGraphicsState().getCurrentTransformationMatrix().clone(); // clip to bounding box PDRectangle bbox = contentStream.getBBox(); clipToRect(bbox); processStreamOperators(contentStream); initialMatrix = parentMatrix; restoreGraphicsStack(savedStack); popResources(parent); } /** * Processes the operators of the given content stream. */ private void processStreamOperators(PDContentStream contentStream) throws IOException { List arguments = new ArrayList<>(); ContentStreamParser parser = new ContentStreamParser(contentStream); Object token; while ((token = parser.nextParsedToken()) != null) { if (token instanceof Operator) { processOperator((Operator) token, arguments); arguments.clear(); } else { arguments.add((COSBase) token); } } } /** * Pushes the given stream's resources, returning the previous resources. */ private PDResources pushResources(PDContentStream contentStream) { // resource lookup: first look for stream resources, then fallback to the current page PDResources parentResources = resources; PDResources streamResources = contentStream.getResources(); if (streamResources != null) { resources = streamResources; } else if (resources != null) { // inherit directly from parent stream, this is not in the PDF spec, but the file from // PDFBOX-1359 does this and works in Acrobat } else { resources = currentPage.getResources(); } // resources are required in PDF if (resources == null) { resources = new PDResources(); } return parentResources; } /** * Pops the current resources, replacing them with the given resources. */ private void popResources(PDResources parentResources) { resources = parentResources; } /** * Transforms the given rectangle using the CTM and then intersects it with the current clipping area. */ private void clipToRect(PDRectangle rectangle) { if (rectangle != null) { GeneralPath clip = rectangle .transform(getGraphicsState().getCurrentTransformationMatrix()); getGraphicsState().intersectClippingPath(clip); } } /** * Called when the BT operator is encountered. This method is for overriding in subclasses, the default * implementation does nothing. * * @throws IOException if there was an error processing the text */ public void beginText() throws IOException { // overridden in subclasses } /** * Called when the ET operator is encountered. This method is for overriding in subclasses, the default * implementation does nothing. * * @throws IOException if there was an error processing the text */ public void endText() throws IOException { // overridden in subclasses } /** * Called when a string of text is to be shown. * * @param string the encoded text * @throws IOException if there was an error showing the text */ public void showTextString(byte[] string) throws IOException { showText(string); } /** * Called when a string of text with spacing adjustments is to be shown. * * @param array array of encoded text strings and adjustments * @throws IOException if there was an error showing the text */ public void showTextStrings(COSArray array) throws IOException { PDTextState textState = getGraphicsState().getTextState(); float fontSize = textState.getFontSize(); float horizontalScaling = textState.getHorizontalScaling() / 100f; PDFont font = textState.getFont(); boolean isVertical = ofNullable(font).map(f -> f.isVertical()).orElse(false); for (COSBase obj : array) { if (obj instanceof COSNumber) { float tj = ((COSNumber) obj).floatValue(); // calculate the combined displacements float tx, ty; if (isVertical) { tx = 0; ty = -tj / 1000 * fontSize; } else { tx = -tj / 1000 * fontSize * horizontalScaling; ty = 0; } applyTextAdjustment(tx, ty); } else if (obj instanceof COSString) { byte[] string = ((COSString) obj).getBytes(); showText(string); } else { throw new IOException("Unknown type in array for TJ operation:" + obj); } } } /** * Applies a text position adjustment from the TJ operator. May be overridden in subclasses. * * @param tx x-translation * @param ty y-translation */ protected void applyTextAdjustment(float tx, float ty) throws IOException { // update the text matrix textMatrix.concatenate(Matrix.getTranslateInstance(tx, ty)); } /** * Process text from the PDF Stream. You should override this method if you want to perform an action when encoded * text is being processed. * * @param string the encoded text * @throws IOException if there is an error processing the string */ protected void showText(byte[] string) throws IOException { PDGraphicsState state = getGraphicsState(); PDTextState textState = state.getTextState(); // get the current font PDFont font = textState.getFont(); if (font == null) { LOG.warn("No current font, will use default"); font = PDFontFactory.createDefaultFont(); } float fontSize = textState.getFontSize(); float horizontalScaling = textState.getHorizontalScaling() / 100f; float charSpacing = textState.getCharacterSpacing(); // put the text state parameters into matrix form Matrix parameters = new Matrix(fontSize * horizontalScaling, 0, // 0 0, fontSize, // 0 0, textState.getRise()); // 1 // read the stream until it is empty InputStream in = new ByteArrayInputStream(string); while (in.available() > 0) { // decode a character int before = in.available(); int code = font.readCode(in); int codeLength = before - in.available(); String unicode = font.toUnicode(code); // Word spacing shall be applied to every occurrence of the single-byte character code // 32 in a string when using a simple font or a composite font that defines code 32 as // a single-byte code. float wordSpacing = 0; if (codeLength == 1 && code == 32) { wordSpacing += textState.getWordSpacing(); } // text rendering matrix (text space -> device space) Matrix ctm = state.getCurrentTransformationMatrix(); Matrix textRenderingMatrix = parameters.multiply(textMatrix).multiply(ctm); // get glyph's position vector if this is vertical text // changes to vertical text should be tested with PDFBOX-2294 and PDFBOX-1422 if (font.isVertical()) { // position vector, in text space Vector v = font.getPositionVector(code); // apply the position vector to the horizontal origin to get the vertical origin textRenderingMatrix.translate(v); } // get glyph's horizontal and vertical displacements, in text space Vector w = font.getDisplacement(code); // process the decoded glyph saveGraphicsState(); Matrix textMatrixOld = textMatrix; Matrix textLineMatrixOld = textLineMatrix; showGlyph(textRenderingMatrix, font, code, unicode, w); textMatrix = textMatrixOld; textLineMatrix = textLineMatrixOld; restoreGraphicsState(); // calculate the combined displacements float tx, ty; if (font.isVertical()) { tx = 0; ty = w.getY() * fontSize + charSpacing + wordSpacing; } else { tx = (w.getX() * fontSize + charSpacing + wordSpacing) * horizontalScaling; ty = 0; } // update the text matrix textMatrix.concatenate(Matrix.getTranslateInstance(tx, ty)); } } /** * Called when a glyph is to be processed.This method is intended for overriding in subclasses, the default * implementation does nothing. * * @param textRenderingMatrix the current text rendering matrix, Trm * @param font the current font * @param code internal PDF character code for the glyph * @param unicode the Unicode text for this glyph, or null if the PDF does provide it * @param displacement the displacement (i.e. advance) of the glyph in text space * @throws IOException if the glyph cannot be processed */ protected void showGlyph(Matrix textRenderingMatrix, PDFont font, int code, String unicode, Vector displacement) throws IOException { if (font instanceof PDType3Font) { showType3Glyph(textRenderingMatrix, (PDType3Font) font, code, unicode, displacement); } else { showFontGlyph(textRenderingMatrix, font, code, unicode, displacement); } } /** * Called when a glyph is to be processed.This method is intended for overriding in subclasses, the default * implementation does nothing. * * @param textRenderingMatrix the current text rendering matrix, Trm * @param font the current font * @param code internal PDF character code for the glyph * @param unicode the Unicode text for this glyph, or null if the PDF does provide it * @param displacement the displacement (i.e. advance) of the glyph in text space * @throws IOException if the glyph cannot be processed */ protected void showFontGlyph(Matrix textRenderingMatrix, PDFont font, int code, String unicode, Vector displacement) throws IOException { // overridden in subclasses } /** * Called when a glyph is to be processed.This method is intended for overriding in subclasses, the default * implementation does nothing. * * @param textRenderingMatrix the current text rendering matrix, Trm * @param font the current font * @param code internal PDF character code for the glyph * @param unicode the Unicode text for this glyph, or null if the PDF does provide it * @param displacement the displacement (i.e. advance) of the glyph in text space * @throws IOException if the glyph cannot be processed */ protected void showType3Glyph(Matrix textRenderingMatrix, PDType3Font font, int code, String unicode, Vector displacement) throws IOException { PDType3CharProc charProc = font.getCharProc(code); if (charProc != null) { processType3Stream(charProc, textRenderingMatrix); } } /** * This is used to handle an operation. * * @param operation The operation to perform. * @param arguments The list of arguments. * @throws IOException If there is an error processing the operation. */ public void processOperator(String operation, List arguments) throws IOException { Operator operator = Operator.getOperator(operation); processOperator(operator, arguments); } /** * This is used to handle an operation. * * @param operator The operation to perform. * @param operands The list of arguments. * @throws IOException If there is an error processing the operation. */ protected void processOperator(Operator operator, List operands) throws IOException { String name = operator.getName(); OperatorProcessor processor = operators.get(name); if (processor != null) { processor.setContext(this); try { processor.process(operator, operands); } catch (IOException e) { operatorException(operator, operands, e); } } else { unsupportedOperator(operator, operands); } } /** * Called when an unsupported operator is encountered. * * @param operator The unknown operator. * @param operands The list of operands. */ protected void unsupportedOperator(Operator operator, List operands) throws IOException { // overridden in subclasses } /** * Called when an exception is thrown by an operator. * * @param operator The unknown operator. * @param operands The list of operands. */ protected void operatorException(Operator operator, List operands, IOException e) throws IOException { if (e instanceof MissingOperandException || e instanceof MissingResourceException || e instanceof MissingImageReaderException) { LOG.error(e.getMessage()); } else if (e instanceof EmptyGraphicsStackException) { LOG.warn(e.getMessage()); } else if (operator.getName().equals("Do")) { // todo: this too forgiving, but PDFBox has always worked this way for DrawObject // some careful refactoring is needed LOG.warn(e.getMessage()); } else { throw e; } } /** * Pushes the current graphics state to the stack. */ public void saveGraphicsState() { graphicsStack.push(graphicsStack.peek().clone()); } /** * Pops the current graphics state from the stack. */ public void restoreGraphicsState() { graphicsStack.pop(); } /** * Saves the entire graphics stack. */ protected final Stack saveGraphicsStack() { Stack savedStack = graphicsStack; graphicsStack = new Stack(); graphicsStack.add(savedStack.peek().clone()); return savedStack; } /** * Restores the entire graphics stack. */ protected final void restoreGraphicsStack(Stack snapshot) { graphicsStack = snapshot; } /** * @return Returns the size of the graphicsStack. */ public int getGraphicsStackSize() { return graphicsStack.size(); } /** * @return Returns the graphicsState. */ public PDGraphicsState getGraphicsState() { return graphicsStack.peek(); } /** * @return Returns the textLineMatrix. */ public Matrix getTextLineMatrix() { return textLineMatrix; } /** * @param value The textLineMatrix to set. */ public void setTextLineMatrix(Matrix value) { textLineMatrix = value; } /** * @return Returns the textMatrix. */ public Matrix getTextMatrix() { return textMatrix; } /** * @param value The textMatrix to set. */ public void setTextMatrix(Matrix value) { textMatrix = value; } /** * @param array dash array * @param phase dash phase */ public void setLineDashPattern(COSArray array, int phase) { if (phase < 0) { LOG.warn("Dash phase has negative value " + phase + ", set to 0"); phase = 0; } PDLineDashPattern lineDash = new PDLineDashPattern(array, phase); getGraphicsState().setLineDashPattern(lineDash); } /** * Returns the stream' resources. */ public PDResources getResources() { return resources; } /** * Returns the current page. */ public PDPage getCurrentPage() { return currentPage; } /** * Gets the stream's initial matrix. */ public Matrix getInitialMatrix() { return initialMatrix; } /** * Transforms a point using the CTM. */ public Point2D.Float transformedPoint(float x, float y) { float[] position = { x, y }; getGraphicsState().getCurrentTransformationMatrix().createAffineTransform() .transform(position, 0, position, 0, 1); return new Point2D.Float(position[0], position[1]); } /** * Transforms a width using the CTM. */ protected float transformWidth(float width) { Matrix ctm = getGraphicsState().getCurrentTransformationMatrix(); float x = ctm.getScaleX() + ctm.getShearX(); float y = ctm.getScaleY() + ctm.getShearY(); return width * (float) Math.sqrt((x * x + y * y) * 0.5); } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/000077500000000000000000000000001320103431700254115ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/DrawObject.java000066400000000000000000000044631320103431700303070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator; import java.io.IOException; import java.util.List; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; import org.sejda.sambox.pdmodel.graphics.form.PDTransparencyGroup; /** * Do: Draws an XObject. * * @author Ben Litchfield * @author Mario Ivankovits */ public class DrawObject extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } COSBase base0 = arguments.get(0); if (!(base0 instanceof COSName)) { return; } COSName name = (COSName) base0; if (getContext().getResources().isImageXObject(name)) { // we're done here, don't decode images when doing text extraction return; } PDXObject xobject = getContext().getResources().getXObject(name); if (xobject instanceof PDTransparencyGroup) { getContext().showTransparencyGroup((PDTransparencyGroup) xobject); } else if (xobject instanceof PDFormXObject) { PDFormXObject form = (PDFormXObject) xobject; getContext().showForm(form); } } @Override public String getName() { return "Do"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/MissingOperandException.java000066400000000000000000000023501320103431700330550ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator; import java.io.IOException; import java.util.List; import org.sejda.sambox.cos.COSBase; /** * Throw when a PDF operator is missing required operands. */ public final class MissingOperandException extends IOException { public MissingOperandException(Operator operator, List operands) { super("Operator " + operator.getName() + " has too few operands: " + operands); } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/Operator.java000066400000000000000000000104571320103431700300560ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.sejda.sambox.cos.COSDictionary; /** * An Operator in a PDF content stream. * * @author Ben Litchfield */ public final class Operator { public static final String BI_OPERATOR = "BI"; public static final String ID_OPERATOR = "ID"; public static final String EI_OPERATOR = "EI"; private final String theOperator; private byte[] imageData; private COSDictionary imageParameters; /** map for singleton operator objects; use {@link ConcurrentHashMap} for better scalability with multiple threads */ private static final ConcurrentMap operators = new ConcurrentHashMap<>(); /** * Constructor. * * @param aOperator The operator that this object will represent. */ private Operator(String aOperator) { theOperator = aOperator; if (aOperator.startsWith("/")) { throw new RuntimeException("Operators are not allowed to start with / '" + aOperator + "'"); } } /** * This is used to create/cache operators in the system. * * @param operator The operator for the system. * * @return The operator that matches the operator keyword. */ public static Operator getOperator(String operator) { Operator operation; if (ID_OPERATOR.equals(operator) || BI_OPERATOR.equals(operator)) { // we can't cache the ID operators. operation = new Operator(operator); } else { operation = operators.get(operator); if (operation == null) { // another thread may has already added an operator of this kind // make sure that we get the same operator operation = operators.putIfAbsent(operator, new Operator(operator)); if (operation == null) { operation = operators.get(operator); } } } return operation; } /** * This will get the name of the operator. * * @return The string representation of the operation. */ public String getName() { return theOperator; } /** * This will print a string rep of this class. * * @return A string rep of this class. */ @Override public String toString() { return "PDFOperator{" + theOperator + "}"; } /** * This is the special case for the ID operator where there are just random bytes inlined the stream. * * @return Value of property imageData. */ public byte[] getImageData() { return this.imageData; } /** * This will set the image data, this is only used for the ID operator. * * @param imageDataArray New value of property imageData. */ public void setImageData(byte[] imageDataArray) { imageData = imageDataArray; } /** * This will get the image parameters, this is only valid for BI operators. * * @return The image parameters. */ public COSDictionary getImageParameters() { return imageParameters; } /** * This will set the image parameters, this is only valid for BI operators. * * @param params The image parameters. */ public void setImageParameters(COSDictionary params) { imageParameters = params; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/OperatorConsumer.java000066400000000000000000000023361320103431700315670ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator; import java.io.IOException; import java.util.List; import org.sejda.sambox.cos.COSBase; /** * Consumes an operator * * @author Andrea Vacondio * */ @FunctionalInterface public interface OperatorConsumer { public static final OperatorConsumer NO_OP = (operator, operands) -> { // no op }; void apply(Operator operator, List operands) throws IOException; } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/OperatorProcessor.java000066400000000000000000000047011320103431700317510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.PDFStreamEngine; import org.sejda.sambox.cos.COSBase; /** * Processes a PDF operator. * * @author Laurent Huault */ public abstract class OperatorProcessor { private PDFStreamEngine context; /** * Creates a new OperatorProcessor. */ protected OperatorProcessor() { } /** * @return the processing context */ public PDFStreamEngine getContext() { return context; } /** * Sets the processing context. * * @param context the processing context. */ public void setContext(PDFStreamEngine context) { this.context = context; } /** * Process the operator. * * @param operator the operator to process * @param operands the operands to use when processing * @throws IOException if the operator cannot be processed */ public abstract void process(Operator operator, List operands) throws IOException; /** * Returns the name of this operator, e.g. "BI". */ public abstract String getName(); /** * Check whether all operands list elements are an instance of a specific class. * * @param operands The operands list. * @param clazz The expected class. * @return the boolean */ public boolean checkArrayTypesClass(List operands, Class clazz) { for (COSBase base : operands) { if (!clazz.isInstance(base)) { return false; } } return true; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/OperatorProcessorDecorator.java000066400000000000000000000051071320103431700336150ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator; import static org.sejda.sambox.contentstream.operator.OperatorConsumer.NO_OP; import java.io.IOException; import java.util.List; import java.util.Optional; import org.sejda.sambox.contentstream.PDFStreamEngine; import org.sejda.sambox.cos.COSBase; /** * decorator for an {@link OperatorProcessor} * * @author Andrea Vacondio */ public class OperatorProcessorDecorator extends OperatorProcessor { private OperatorProcessor delegate; private Optional consumer; /** * Decorates the given {@link OperatorProcessor} with the given {@link OperatorConsumer} function * * @param delegate * @param consumer */ public OperatorProcessorDecorator(OperatorProcessor delegate, OperatorConsumer consumer) { this.delegate = delegate; this.consumer = Optional.ofNullable(consumer); } public OperatorProcessorDecorator(OperatorProcessor delegate) { this.delegate = delegate; this.consumer = Optional.empty(); } @Override public void process(Operator operator, List operands) throws IOException { delegate.process(operator, operands); consumer.orElse(NO_OP).apply(operator, operands); } @Override public String getName() { return delegate.getName(); } @Override public PDFStreamEngine getContext() { return delegate.getContext(); } @Override public void setContext(PDFStreamEngine context) { delegate.setContext(context); } /** * Set the consumer that decorates this OperatorProcessor * * @param consumer */ public void setConsumer(OperatorConsumer consumer) { this.consumer = Optional.ofNullable(consumer); } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/000077500000000000000000000000001320103431700265275ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/SetColor.java000066400000000000000000000051411320103431700311250ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDPattern; /** * sc,scn,SC,SCN: Sets the color to use for stroking or non-stroking operations. * * @author John Hewson */ public abstract class SetColor extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { PDColorSpace colorSpace = getColorSpace(); if (!(colorSpace instanceof PDPattern)) { if (arguments.size() < colorSpace.getNumberOfComponents()) { throw new MissingOperandException(operator, arguments); } if (!checkArrayTypesClass(arguments, COSNumber.class)) { return; } } COSArray array = new COSArray(); array.addAll(arguments); setColor(new PDColor(array, colorSpace)); } /** * @return The stroking or non-stroking color value. */ public abstract PDColor getColor(); /** * Sets either the stroking or non-stroking color value. * * @param color The stroking or non-stroking color value. */ protected abstract void setColor(PDColor color); /** * @return The stroking or non-stroking color space. */ public abstract PDColorSpace getColorSpace(); } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/SetNonStrokingColor.java000066400000000000000000000035731320103431700333300ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * sc: Sets the colour to use for stroking non-stroking operations. * * @author John Hewson */ public class SetNonStrokingColor extends SetColor { /** * Returns the non-stroking color. * @return The non-stroking color. */ @Override public PDColor getColor() { return getContext().getGraphicsState().getNonStrokingColor(); } /** * Sets the non-stroking color. * @param color The new non-stroking color. */ @Override protected void setColor(PDColor color) { getContext().getGraphicsState().setNonStrokingColor(color); } /** * Returns the non-stroking color space. * @return The non-stroking color space. */ @Override public PDColorSpace getColorSpace() { return getContext().getGraphicsState().getNonStrokingColorSpace(); } @Override public String getName() { return "sc"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/SetNonStrokingColorN.java000066400000000000000000000022261320103431700334400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.color; /** * scn: Sets the colour to use for stroking non-stroking operations. * Supports Pattern, Separation, DeviceN and ICCBased colour spaces. * * @author John Hewson */ public class SetNonStrokingColorN extends SetNonStrokingColor { @Override public String getName() { return "scn"; } } SetNonStrokingColorSpace.java000066400000000000000000000034201320103431700342140ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * cs: Sets the non-stroking color space. * * @author Ben Litchfield * @author John Hewson */ public class SetNonStrokingColorSpace extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { COSName name = (COSName)arguments.get(0); PDColorSpace cs = getContext().getResources().getColorSpace(name); getContext().getGraphicsState().setNonStrokingColorSpace(cs); getContext().getGraphicsState().setNonStrokingColor(cs.getInitialColor()); } @Override public String getName() { return "cs"; } } SetNonStrokingDeviceCMYKColor.java000066400000000000000000000032641320103431700350520ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * k: Set the non-stroking colour space to DeviceCMYK and set the colour to * use for non-stroking operations. * * @author John Hewson */ public class SetNonStrokingDeviceCMYKColor extends SetNonStrokingColor { @Override public void process(Operator operator, List arguments) throws IOException { PDColorSpace cs = getContext().getResources().getColorSpace(COSName.DEVICECMYK); getContext().getGraphicsState().setNonStrokingColorSpace(cs); super.process(operator, arguments); } @Override public String getName() { return "k"; } } SetNonStrokingDeviceGrayColor.java000066400000000000000000000032701320103431700352060ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * g: Set the non-stroking colour space to DeviceGray and set the gray * level to use for non-stroking operations. * * @author John Hewson */ public class SetNonStrokingDeviceGrayColor extends SetNonStrokingColor { @Override public void process(Operator operator, List arguments) throws IOException { PDColorSpace cs = getContext().getResources().getColorSpace(COSName.DEVICEGRAY); getContext().getGraphicsState().setNonStrokingColorSpace(cs); super.process(operator, arguments); } @Override public String getName() { return "g"; } } SetNonStrokingDeviceRGBColor.java000066400000000000000000000032631320103431700347200ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * rg: Set the non-stroking colour space to DeviceRGB and set the colour to * use for non-stroking operations. * * @author John Hewson */ public class SetNonStrokingDeviceRGBColor extends SetNonStrokingColor { @Override public void process(Operator operator, List arguments) throws IOException { PDColorSpace cs = getContext().getResources().getColorSpace(COSName.DEVICERGB); getContext().getGraphicsState().setNonStrokingColorSpace(cs); super.process(operator, arguments); } @Override public String getName() { return "rg"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/SetStrokingColor.java000066400000000000000000000035231320103431700326500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * SC: Sets the colour to use for stroking stroking operations. * * @author John Hewson */ public class SetStrokingColor extends SetColor { /** * Returns the stroking color. * @return The stroking color. */ @Override public PDColor getColor() { return getContext().getGraphicsState().getStrokingColor(); } /** * Sets the stroking color. * @param color The new stroking color. */ @Override protected void setColor(PDColor color) { getContext().getGraphicsState().setStrokingColor(color); } /** * Returns the stroking color space. * @return The stroking color space. */ @Override public PDColorSpace getColorSpace() { return getContext().getGraphicsState().getStrokingColorSpace(); } @Override public String getName() { return "SC"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/SetStrokingColorN.java000066400000000000000000000022141320103431700327620ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.color; /** * SCN: Sets the colour to use for stroking stroking operations. * Supports Pattern, Separation, DeviceN and ICCBased colour spaces. * * @author John Hewson */ public class SetStrokingColorN extends SetStrokingColor { @Override public String getName() { return "SCN"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/SetStrokingColorSpace.java000066400000000000000000000035221320103431700336230ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * CS: Set color space for stroking operations. * * @author Ben Litchfield * @author John Hewson */ public class SetStrokingColorSpace extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { COSBase base = arguments.get(0); if (base instanceof COSName) { PDColorSpace cs = getContext().getResources().getColorSpace((COSName) base); getContext().getGraphicsState().setStrokingColorSpace(cs); getContext().getGraphicsState().setStrokingColor(cs.getInitialColor()); } } @Override public String getName() { return "CS"; } } SetStrokingDeviceCMYKColor.java000066400000000000000000000032401320103431700343710ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * K: Set the stroking colour space to DeviceCMYK and set the colour to use for stroking operations. * * @author John Hewson */ public class SetStrokingDeviceCMYKColor extends SetStrokingColor { @Override public void process(Operator operator, List arguments) throws IOException { PDColorSpace cs = getContext().getResources().getColorSpace(COSName.DEVICECMYK); getContext().getGraphicsState().setStrokingColorSpace(cs); super.process(operator, arguments); } @Override public String getName() { return "K"; } } SetStrokingDeviceGrayColor.java000066400000000000000000000032311320103431700345300ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * G: Set the stroking colour space to DeviceGray and set the gray level to use for stroking * operations. * * @author John Hewson */ public class SetStrokingDeviceGrayColor extends SetStrokingColor { public void process(Operator operator, List arguments) throws IOException { PDColorSpace cs = getContext().getResources().getColorSpace(COSName.DEVICEGRAY); getContext().getGraphicsState().setStrokingColorSpace(cs); super.process(operator, arguments); } @Override public String getName() { return "G"; } } SetStrokingDeviceRGBColor.java000066400000000000000000000036561320103431700342530ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/color/* * 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. */ package org.sejda.sambox.contentstream.operator.color; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * RG: Set the stroking colour space to DeviceRGB and set the colour to use for stroking operations. * * @author John Hewson */ public class SetStrokingDeviceRGBColor extends SetStrokingColor { /** * RG Set the stroking colour space to DeviceRGB and set the colour to * use for stroking operations. * * @param operator The operator that is being executed. * @param arguments List * @throws IOException If the color space cannot be read. */ public void process(Operator operator, List arguments) throws IOException { PDColorSpace cs = getContext().getResources().getColorSpace(COSName.DEVICERGB); getContext().getGraphicsState().setStrokingColorSpace(cs); super.process(operator, arguments); } @Override public String getName() { return "RG"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/000077500000000000000000000000001320103431700272115ustar00rootroot00000000000000AppendRectangleToPath.java000066400000000000000000000045761320103431700341650ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.Point2D; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * re Appends a rectangle to the path. * * @author Ben Litchfield */ public final class AppendRectangleToPath extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 4) { throw new MissingOperandException(operator, operands); } if (!checkArrayTypesClass(operands, COSNumber.class)) { return; } COSNumber x = (COSNumber) operands.get(0); COSNumber y = (COSNumber) operands.get(1); COSNumber w = (COSNumber) operands.get(2); COSNumber h = (COSNumber) operands.get(3); float x1 = x.floatValue(); float y1 = y.floatValue(); // create a pair of coordinates for the transformation float x2 = w.floatValue() + x1; float y2 = h.floatValue() + y1; Point2D p0 = getContext().transformedPoint(x1, y1); Point2D p1 = getContext().transformedPoint(x2, y1); Point2D p2 = getContext().transformedPoint(x2, y2); Point2D p3 = getContext().transformedPoint(x1, y2); getContext().appendRectangle(p0, p1, p2, p3); } @Override public String getName() { return "re"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/BeginInlineImage.java000066400000000000000000000034051320103431700332040ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import static java.util.Objects.nonNull; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.pdmodel.graphics.image.PDImage; import org.sejda.sambox.pdmodel.graphics.image.PDInlineImage; /** * BI Begins an inline image. * * @author Ben Litchfield */ public final class BeginInlineImage extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { if (nonNull(operator.getImageData()) && operator.getImageData().length > 0) { PDImage image = new PDInlineImage(operator.getImageParameters(), operator.getImageData(), getContext().getResources()); getContext().drawImage(image); } } @Override public String getName() { return "BI"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/ClipEvenOddRule.java000066400000000000000000000026371320103431700330500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * W* Set clipping path using even odd rule. * * @author Daniel Wilson */ public final class ClipEvenOddRule extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().clip(GeneralPath.WIND_EVEN_ODD); } @Override public String getName() { return "W*"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/ClipNonZeroRule.java000066400000000000000000000026431320103431700331130ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * W Set the clipping path using non zero winding rule. * * @author Daniel Wilson */ public class ClipNonZeroRule extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().clip(GeneralPath.WIND_NON_ZERO); } @Override public String getName() { return "W"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/CloseAndStrokePath.java000066400000000000000000000026451320103431700335600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * s: close and stroke the path. * * @author Ben Litchfield */ public class CloseAndStrokePath extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { getContext().processOperator("h", arguments); getContext().processOperator("S", arguments); } @Override public String getName() { return "s"; } } CloseFillEvenOddAndStrokePath.java000066400000000000000000000027411320103431700355520ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * b* Close, fill and stroke the path with even-odd winding rule. * */ public final class CloseFillEvenOddAndStrokePath extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().processOperator("h", operands); // ClosePath getContext().processOperator("B*", operands); // FillEvenOddAndStroke } @Override public String getName() { return "b*"; } } CloseFillNonZeroAndStrokePath.java000066400000000000000000000027701320103431700356220ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * b Close, fill and stroke the path with non-zero winding rule. * * @author Ben Litchfield */ public final class CloseFillNonZeroAndStrokePath extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().processOperator("h", operands); // ClosePath getContext().processOperator("B", operands); // FillNonZeroAndStroke } @Override public String getName() { return "b"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/ClosePath.java000066400000000000000000000031571320103431700317440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * h Close the path. * * @author Ben Litchfield */ public final class ClosePath extends GraphicsOperatorProcessor { private static final Logger LOG = LoggerFactory.getLogger(ClosePath.class); @Override public void process(Operator operator, List operands) throws IOException { if (getContext().getCurrentPoint() == null) { LOG.warn("ClosePath without initial MoveTo"); return; } getContext().closePath(); } @Override public String getName() { return "h"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/CurveTo.java000066400000000000000000000053251320103431700314500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.Point2D; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * c Append curved segment to path. * * @author Ben Litchfield */ public class CurveTo extends GraphicsOperatorProcessor { private static final Logger LOG = LoggerFactory.getLogger(CurveTo.class); @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 6) { throw new MissingOperandException(operator, operands); } if (!checkArrayTypesClass(operands, COSNumber.class)) { return; } COSNumber x1 = (COSNumber) operands.get(0); COSNumber y1 = (COSNumber) operands.get(1); COSNumber x2 = (COSNumber) operands.get(2); COSNumber y2 = (COSNumber) operands.get(3); COSNumber x3 = (COSNumber) operands.get(4); COSNumber y3 = (COSNumber) operands.get(5); Point2D.Float point1 = getContext().transformedPoint(x1.floatValue(), y1.floatValue()); Point2D.Float point2 = getContext().transformedPoint(x2.floatValue(), y2.floatValue()); Point2D.Float point3 = getContext().transformedPoint(x3.floatValue(), y3.floatValue()); if (getContext().getCurrentPoint() == null) { LOG.warn("curveTo (" + point3.x + "," + point3.y + ") without initial MoveTo"); getContext().moveTo(point3.x, point3.y); } else { getContext().curveTo(point1.x, point1.y, point2.x, point2.y, point3.x, point3.y); } } @Override public String getName() { return "c"; } } CurveToReplicateFinalPoint.java000066400000000000000000000042741320103431700352100ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.Point2D; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * y Append curved segment to path with final point replicated. * * @author Ben Litchfield */ public final class CurveToReplicateFinalPoint extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 4) { throw new MissingOperandException(operator, operands); } if (!checkArrayTypesClass(operands, COSNumber.class)) { return; } COSNumber x1 = (COSNumber) operands.get(0); COSNumber y1 = (COSNumber) operands.get(1); COSNumber x3 = (COSNumber) operands.get(2); COSNumber y3 = (COSNumber) operands.get(3); Point2D.Float point1 = getContext().transformedPoint(x1.floatValue(), y1.floatValue()); Point2D.Float point3 = getContext().transformedPoint(x3.floatValue(), y3.floatValue()); getContext().curveTo(point1.x, point1.y, point3.x, point3.y, point3.x, point3.y); } @Override public String getName() { return "y"; } } CurveToReplicateInitialPoint.java000066400000000000000000000053011320103431700355400ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.Point2D; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * v Append curved segment to path with the initial point replicated. * * @author Ben Litchfield */ public class CurveToReplicateInitialPoint extends GraphicsOperatorProcessor { private static final Logger LOG = LoggerFactory.getLogger(CurveToReplicateInitialPoint.class); @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 4) { throw new MissingOperandException(operator, operands); } if (!checkArrayTypesClass(operands, COSNumber.class)) { return; } COSNumber x2 = (COSNumber) operands.get(0); COSNumber y2 = (COSNumber) operands.get(1); COSNumber x3 = (COSNumber) operands.get(2); COSNumber y3 = (COSNumber) operands.get(3); Point2D currentPoint = getContext().getCurrentPoint(); Point2D.Float point2 = getContext().transformedPoint(x2.floatValue(), y2.floatValue()); Point2D.Float point3 = getContext().transformedPoint(x3.floatValue(), y3.floatValue()); if (currentPoint == null) { LOG.warn("curveTo (" + point3.x + "," + point3.y + ") without initial MoveTo"); getContext().moveTo(point3.x, point3.y); } else { getContext().curveTo((float) currentPoint.getX(), (float) currentPoint.getY(), point2.x, point2.y, point3.x, point3.y); } } @Override public String getName() { return "v"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/DrawObject.java000066400000000000000000000052551320103431700321070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.MissingResourceException; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; import org.sejda.sambox.pdmodel.graphics.form.PDTransparencyGroup; import org.sejda.sambox.pdmodel.graphics.image.PDImageXObject; /** * Do: Draws an XObject. * * @author Ben Litchfield * @author John Hewson */ public final class DrawObject extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 1) { throw new MissingOperandException(operator, operands); } COSBase base0 = operands.get(0); if (!(base0 instanceof COSName)) { return; } COSName objectName = (COSName) base0; PDXObject xobject = getContext().getResources().getXObject(objectName); if (xobject == null) { throw new MissingResourceException("Missing XObject: " + objectName.getName()); } else if (xobject instanceof PDImageXObject) { PDImageXObject image = (PDImageXObject) xobject; getContext().drawImage(image); } else if (xobject instanceof PDTransparencyGroup) { getContext().showTransparencyGroup((PDTransparencyGroup) xobject); } else if (xobject instanceof PDFormXObject) { getContext().showForm((PDFormXObject) xobject); } } @Override public String getName() { return "Do"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/EndPath.java000066400000000000000000000025051320103431700314010ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * n End the path. * * @author Ben Litchfield */ public final class EndPath extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().endPath(); } @Override public String getName() { return "n"; } } FillEvenOddAndStrokePath.java000066400000000000000000000027151320103431700345650ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * B* Fill and then stroke the path, using the even-odd rule to determine the region to fill. * */ public final class FillEvenOddAndStrokePath extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().fillAndStrokePath(GeneralPath.WIND_EVEN_ODD); } @Override public String getName() { return "B*"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/FillEvenOddRule.java000066400000000000000000000026341320103431700330440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * f* Fill path using even odd rule. * * @author Ben Litchfield */ public final class FillEvenOddRule extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().fillPath(GeneralPath.WIND_EVEN_ODD); } @Override public String getName() { return "f*"; } } FillNonZeroAndStrokePath.java000066400000000000000000000027601320103431700346330ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * B Fill and then stroke the path, using the nonzero winding number rule to determine the region * to fill. * * @author Ben Litchfield */ public class FillNonZeroAndStrokePath extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().fillAndStrokePath(GeneralPath.WIND_NON_ZERO); } @Override public String getName() { return "B"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/FillNonZeroRule.java000066400000000000000000000026421320103431700331110ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * f Fill path using non zero winding rule. * * @author Ben Litchfield */ public class FillNonZeroRule extends GraphicsOperatorProcessor { @Override public final void process(Operator operator, List operands) throws IOException { getContext().fillPath(GeneralPath.WIND_NON_ZERO); } @Override public String getName() { return "f"; } } GraphicsOperatorProcessor.java000066400000000000000000000023741320103431700351570ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import org.sejda.sambox.contentstream.PDFGraphicsStreamEngine; import org.sejda.sambox.contentstream.operator.OperatorProcessor; /** * Base class for graphics operators. * * @author John Hewson */ public abstract class GraphicsOperatorProcessor extends OperatorProcessor { @Override public PDFGraphicsStreamEngine getContext() { return (PDFGraphicsStreamEngine)super.getContext(); } } LegacyFillNonZeroRule.java000066400000000000000000000021521320103431700341530ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; /** * F Fill path using non zero winding rule. Included only for compatibility with Acrobat. * * @author John Hewson */ public final class LegacyFillNonZeroRule extends FillNonZeroRule { @Override public String getName() { return "F"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/LineTo.java000066400000000000000000000047211320103431700312520ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.Point2D; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * l Append straight line segment to path. * * @author Ben Litchfield */ public class LineTo extends GraphicsOperatorProcessor { private static final Logger LOG = LoggerFactory.getLogger(LineTo.class); @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 2) { throw new MissingOperandException(operator, operands); } COSBase base0 = operands.get(0); if (!(base0 instanceof COSNumber)) { return; } COSBase base1 = operands.get(1); if (!(base1 instanceof COSNumber)) { return; } // append straight line segment from the current point to the point COSNumber x = (COSNumber) base0; COSNumber y = (COSNumber) base1; Point2D.Float pos = getContext().transformedPoint(x.floatValue(), y.floatValue()); if (getContext().getCurrentPoint() == null) { LOG.warn("LineTo (" + pos.x + "," + pos.y + ") without initial MoveTo"); getContext().moveTo(pos.x, pos.y); } else { getContext().lineTo(pos.x, pos.y); } } @Override public String getName() { return "l"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/MoveTo.java000066400000000000000000000040011320103431700312600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.awt.geom.Point2D; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * m Begins a new subpath. * * @author Ben Litchfield */ public final class MoveTo extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 2) { throw new MissingOperandException(operator, operands); } COSBase base0 = operands.get(0); if (!(base0 instanceof COSNumber)) { return; } COSBase base1 = operands.get(1); if (!(base1 instanceof COSNumber)) { return; } COSNumber x = (COSNumber) base0; COSNumber y = (COSNumber) base1; Point2D.Float pos = getContext().transformedPoint(x.floatValue(), y.floatValue()); getContext().moveTo(pos.x, pos.y); } @Override public String getName() { return "m"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/ShadingFill.java000066400000000000000000000031661320103431700322460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; /** * sh Fills the clipping area with the given shading pattern. * * @author Daniel Wilson */ public final class ShadingFill extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 1) { throw new MissingOperandException(operator, operands); } getContext().shadingFill((COSName) operands.get(0)); } @Override public String getName() { return "sh"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/StrokePath.java000066400000000000000000000025161320103431700321440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.graphics; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; /** * S Stroke the path. * * @author Ben Litchfield */ public final class StrokePath extends GraphicsOperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { getContext().strokePath(); } @Override public String getName() { return "S"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/graphics/package.html000066400000000000000000000017311320103431700314740ustar00rootroot00000000000000 This package contains implementations of all of the PDF graphics operators. sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/markedcontent/000077500000000000000000000000001320103431700302475ustar00rootroot00000000000000BeginMarkedContentSequence.java000066400000000000000000000035021320103431700362270ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/markedcontent/* * 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. */ package org.sejda.sambox.contentstream.operator.markedcontent; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.text.PDFMarkedContentExtractor; /** * BMC : Begins a marked-content sequence. * * @author Johannes Koch */ public class BeginMarkedContentSequence extends OperatorProcessor { @Override public void process(Operator operator, List arguments) { COSName tag = null; for (COSBase argument : arguments) { if (argument instanceof COSName) { tag = (COSName) argument; } } if (this.getContext() instanceof PDFMarkedContentExtractor) { ((PDFMarkedContentExtractor) this.getContext()).beginMarkedContentSequence(tag, null); } } @Override public String getName() { return "BMC"; } } BeginMarkedContentSequenceWithProperties.java000066400000000000000000000041341320103431700411420ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/markedcontent/* * 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. */ package org.sejda.sambox.contentstream.operator.markedcontent; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.text.PDFMarkedContentExtractor; /** * BDC : Begins a marked-content sequence with property list. * * @author Johannes Koch */ public class BeginMarkedContentSequenceWithProperties extends OperatorProcessor { @Override public void process(Operator operator, List arguments) { COSName tag = null; COSDictionary properties = null; for (COSBase argument : arguments) { if (argument instanceof COSName) { tag = (COSName) argument; } else if (argument instanceof COSDictionary) { properties = (COSDictionary) argument; } } if (this.getContext() instanceof PDFMarkedContentExtractor) { ((PDFMarkedContentExtractor) this.getContext()).beginMarkedContentSequence(tag, properties); } } @Override public String getName() { return "BDC"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/markedcontent/DrawObject.java000066400000000000000000000047141320103431700331440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.markedcontent; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; import org.sejda.sambox.pdmodel.graphics.form.PDTransparencyGroup; import org.sejda.sambox.text.PDFMarkedContentExtractor; /** * Do: Draws an XObject. * * @author Ben Litchfield * @author Mario Ivankovits */ public class DrawObject extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } COSBase base0 = arguments.get(0); if (!(base0 instanceof COSName)) { return; } COSName name = (COSName) base0; PDXObject xobject = getContext().getResources().getXObject(name); ((PDFMarkedContentExtractor) getContext()).xobject(xobject); if (xobject instanceof PDTransparencyGroup) { getContext().showTransparencyGroup((PDTransparencyGroup) xobject); } else if (xobject instanceof PDFormXObject) { PDFormXObject form = (PDFormXObject) xobject; getContext().showForm(form); } } @Override public String getName() { return "Do"; } } EndMarkedContentSequence.java000066400000000000000000000031241320103431700357110ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/markedcontent/* * 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. */ package org.sejda.sambox.contentstream.operator.markedcontent; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.text.PDFMarkedContentExtractor; /** * EMC : Ends a marked-content sequence begun by BMC or BDC. * * @author Johannes Koch */ public class EndMarkedContentSequence extends OperatorProcessor { @Override public void process(Operator operator, List arguments) { if (this.getContext() instanceof PDFMarkedContentExtractor) { ((PDFMarkedContentExtractor) this.getContext()).endMarkedContentSequence(); } } @Override public String getName() { return "EMC"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/000077500000000000000000000000001320103431700265315ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/Concatenate.java000066400000000000000000000045701320103431700316260ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.util.Matrix; /** * cm: Concatenate matrix to current transformation matrix. * * @author Laurent Huault */ public class Concatenate extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 6) { throw new MissingOperandException(operator, arguments); } if (!checkArrayTypesClass(arguments, COSNumber.class)) { return; } // concatenate matrix to current transformation matrix COSNumber a = (COSNumber) arguments.get(0); COSNumber b = (COSNumber) arguments.get(1); COSNumber c = (COSNumber) arguments.get(2); COSNumber d = (COSNumber) arguments.get(3); COSNumber e = (COSNumber) arguments.get(4); COSNumber f = (COSNumber) arguments.get(5); Matrix matrix = new Matrix(a.floatValue(), b.floatValue(), c.floatValue(), d.floatValue(), e.floatValue(), f.floatValue()); getContext().getGraphicsState().getCurrentTransformationMatrix().concatenate(matrix); } @Override public String getName() { return "cm"; } } EmptyGraphicsStackException.java000066400000000000000000000023751320103431700347500ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; /** * Throw when restore is executed when the graphics stack is empty. */ public final class EmptyGraphicsStackException extends IOException { /** * See https://stackoverflow.com/questions/285793/ */ private static final long serialVersionUID = 1L; EmptyGraphicsStackException() { super("Cannot execute restore, the graphics stack is empty"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/Restore.java000066400000000000000000000031701320103431700310200ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; /** * Q: Restore the graphics state. * * @author Laurent Huault */ public class Restore extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (getContext().getGraphicsStackSize() > 1) { getContext().restoreGraphicsState(); } else { // this shouldn't happen but it does, see PDFBOX-161 throw new EmptyGraphicsStackException(); } } @Override public String getName() { return "Q"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/Save.java000066400000000000000000000025331320103431700302750ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; /** * q: Save the graphics state. * * @author Laurent Huault */ public class Save extends OperatorProcessor { @Override public void process(Operator operator, List arguments) { getContext().saveGraphicsState(); } @Override public String getName() { return "q"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/SetFlatness.java000066400000000000000000000034441320103431700316340ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * i: Set the flatness tolerance. * * @author John Hewson */ public class SetFlatness extends OperatorProcessor { @Override public void process(Operator operator, List operands) throws MissingOperandException { if (operands.size() < 1) { throw new MissingOperandException(operator, operands); } if (!checkArrayTypesClass(operands, COSNumber.class)) { return; } COSNumber value = (COSNumber) operands.get(0); getContext().getGraphicsState().setFlatness(value.floatValue()); } @Override public String getName() { return "i"; } } SetGraphicsStateParameters.java000066400000000000000000000046261320103431700345660ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * gs: Set parameters from graphics state parameter dictionary. * * @author Ben Litchfield */ public class SetGraphicsStateParameters extends OperatorProcessor { private static final Logger LOG = LoggerFactory.getLogger(SetGraphicsStateParameters.class); @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } COSBase base0 = arguments.get(0); if (!(base0 instanceof COSName)) { return; } // set parameters from graphics state parameter dictionary COSName graphicsName = (COSName) base0; PDExtendedGraphicsState gs = getContext().getResources().getExtGState(graphicsName); if (gs == null) { LOG.warn("name for 'gs' operator not found in resources: /" + graphicsName.getName()); } else { gs.copyIntoGraphicsState(getContext().getGraphicsState()); } } @Override public String getName() { return "gs"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/SetLineCapStyle.java000066400000000000000000000033011320103431700324010ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * J: Set the line cap style. * */ public class SetLineCapStyle extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } int lineCapStyle = ((COSNumber) arguments.get(0)).intValue(); getContext().getGraphicsState().setLineCap(lineCapStyle); } @Override public String getName() { return "J"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/SetLineDashPattern.java000066400000000000000000000055531320103431700331050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * d: Set the line dash pattern. * * @author Ben Litchfield */ public class SetLineDashPattern extends OperatorProcessor { private static final Logger LOG = LoggerFactory.getLogger(SetLineDashPattern.class); @Override public void process(Operator operator, List arguments) throws MissingOperandException { if (arguments.size() < 2) { throw new MissingOperandException(operator, arguments); } COSBase base0 = arguments.get(0); if (!(base0 instanceof COSArray)) { return; } COSBase base1 = arguments.get(1); if (!(base1 instanceof COSNumber)) { return; } COSArray dashArray = (COSArray) base0; int dashPhase = ((COSNumber) base1).intValue(); boolean allZero = true; for (COSBase base : dashArray) { if (base instanceof COSNumber) { COSNumber num = (COSNumber) base; if (num.floatValue() != 0) { allZero = false; break; } } else { LOG.warn("dash array has non number element " + base + ", ignored"); dashArray = new COSArray(); break; } } if (dashArray.size() > 0 && allZero) { LOG.warn("dash lengths all zero, ignored"); dashArray = new COSArray(); } getContext().setLineDashPattern(dashArray, dashPhase); } @Override public String getName() { return "d"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/SetLineJoinStyle.java000066400000000000000000000033061320103431700326020ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * j: Set the line join style. * */ public class SetLineJoinStyle extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } int lineJoinStyle = ((COSNumber) arguments.get(0)).intValue(); getContext().getGraphicsState().setLineJoin(lineJoinStyle); } @Override public String getName() { return "j"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/SetLineMiterLimit.java000066400000000000000000000033011320103431700327340ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * M: Set miter limit. * */ public class SetLineMiterLimit extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } COSNumber miterLimit = (COSNumber) arguments.get(0); getContext().getGraphicsState().setMiterLimit(miterLimit.floatValue()); } @Override public String getName() { return "M"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/SetLineWidth.java000066400000000000000000000033121320103431700317360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * w: Set line width. * * @author Ben Litchfield */ public class SetLineWidth extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } COSNumber width = (COSNumber) arguments.get(0); getContext().getGraphicsState().setLineWidth(width.floatValue()); } @Override public String getName() { return "w"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/SetMatrix.java000066400000000000000000000044471320103431700313250ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.util.Matrix; /** * Tm: Set text matrix and text line matrix. * * @author Laurent Huault */ public class SetMatrix extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws MissingOperandException { if (arguments.size() < 6) { throw new MissingOperandException(operator, arguments); } if (checkArrayTypesClass(arguments, COSNumber.class)) { COSNumber a = (COSNumber) arguments.get(0); COSNumber b = (COSNumber) arguments.get(1); COSNumber c = (COSNumber) arguments.get(2); COSNumber d = (COSNumber) arguments.get(3); COSNumber e = (COSNumber) arguments.get(4); COSNumber f = (COSNumber) arguments.get(5); Matrix matrix = new Matrix(a.floatValue(), b.floatValue(), c.floatValue(), d.floatValue(), e.floatValue(), f.floatValue()); getContext().setTextMatrix(matrix); getContext().setTextLineMatrix(matrix.clone()); } } @Override public String getName() { return "Tm"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/state/SetRenderingIntent.java000066400000000000000000000036011320103431700331470ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.state; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.state.RenderingIntent; /** * ri: Set the rendering intent. * * @author John Hewson */ public class SetRenderingIntent extends OperatorProcessor { @Override public void process(Operator operator, List operands) throws IOException { if (operands.size() < 1) { throw new MissingOperandException(operator, operands); } COSBase base = operands.get(0); if (base instanceof COSName) { getContext().getGraphicsState() .setRenderingIntent(RenderingIntent.fromString(((COSName) base).getName())); } } @Override public String getName() { return "ri"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/000077500000000000000000000000001320103431700263755ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/BeginText.java000066400000000000000000000030421320103431700311300ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.util.Matrix; /** * BT: Begin text. * * @author Ben Litchfield * @author Laurent Huault */ public class BeginText extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { getContext().setTextMatrix(new Matrix()); getContext().setTextLineMatrix(new Matrix()); getContext().beginText(); } @Override public String getName() { return "BT"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/EndText.java000066400000000000000000000027151320103431700306200ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; /** * ET: End text. * * @author Laurent Huault */ public class EndText extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { getContext().setTextMatrix(null); getContext().setTextLineMatrix(null); getContext().endText(); } @Override public String getName() { return "ET"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/MoveText.java000066400000000000000000000047371320103431700310260ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Td: Move text position. * * @author Laurent Huault */ public class MoveText extends OperatorProcessor { private static final Logger LOG = LoggerFactory.getLogger(MoveText.class); @Override public void process(Operator operator, List arguments) throws MissingOperandException { if (arguments.size() < 2) { throw new MissingOperandException(operator, arguments); } Matrix textLineMatrix = getContext().getTextLineMatrix(); if (textLineMatrix == null) { LOG.warn("TextLineMatrix is null, " + getName() + " operator will be ignored"); return; } COSBase base0 = arguments.get(0); COSBase base1 = arguments.get(1); if (!(base0 instanceof COSNumber)) { return; } if (!(base1 instanceof COSNumber)) { return; } COSNumber x = (COSNumber) base0; COSNumber y = (COSNumber) base1; Matrix matrix = new Matrix(1, 0, 0, 1, x.floatValue(), y.floatValue()); textLineMatrix.concatenate(matrix); getContext().setTextMatrix(textLineMatrix.clone()); } @Override public String getName() { return "Td"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/MoveTextSetLeading.java000066400000000000000000000041211320103431700327510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSNumber; /** * TD: Move text position and set leading. * * @author Laurent Huault */ public class MoveTextSetLeading extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 2) { throw new MissingOperandException(operator, arguments); } //move text position and set leading COSBase base1 = arguments.get(1); if (base1 instanceof COSNumber) { COSNumber y = (COSNumber) base1; ArrayList args = new ArrayList<>(); args.add(new COSFloat(-1 * y.floatValue())); getContext().processOperator("TL", args); getContext().processOperator("Td", arguments); } } @Override public String getName() { return "TD"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/NextLine.java000066400000000000000000000036241320103431700307730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSFloat; /** * T*: Move to start of next text line. * * @author Laurent Huault */ public class NextLine extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { //move to start of next text line ArrayList args = new ArrayList<>(); args.add(new COSFloat(0f)); // this must be -leading instead of just leading as written in the // specification (p.369) the acrobat reader seems to implement it the same way args.add(new COSFloat(-1 * getContext().getGraphicsState().getTextState().getLeading())); // use Td instead of repeating code getContext().processOperator("Td", args); } @Override public String getName() { return "T*"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/SetCharSpacing.java000066400000000000000000000041231320103431700320760ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * Tc: Set character spacing. * * @author Laurent Huault */ public class SetCharSpacing extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.isEmpty()) { throw new MissingOperandException(operator, arguments); } // there are some documents which are incorrectly structured, and have // a wrong number of arguments to this, so we will assume the last argument // in the list Object charSpacing = arguments.get(arguments.size()-1); if (charSpacing instanceof COSNumber) { COSNumber characterSpacing = (COSNumber)charSpacing; getContext().getGraphicsState().getTextState() .setCharacterSpacing(characterSpacing.floatValue()); } } @Override public String getName() { return "Tc"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/SetFontAndSize.java000066400000000000000000000043371320103431700321070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.pdmodel.font.PDFont; /** * Tf: Set text font and size. * * @author Laurent Huault */ public class SetFontAndSize extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 2) { throw new MissingOperandException(operator, arguments); } COSBase base0 = arguments.get(0); COSBase base1 = arguments.get(1); if (!(base0 instanceof COSName)) { return; } if (!(base1 instanceof COSNumber)) { return; } COSName fontName = (COSName) base0; float fontSize = ((COSNumber) base1).floatValue(); getContext().getGraphicsState().getTextState().setFontSize(fontSize); PDFont font = getContext().getResources().getFont(fontName); getContext().getGraphicsState().getTextState().setFont(font); } @Override public String getName() { return "Tf"; } } SetTextHorizontalScaling.java000066400000000000000000000034071320103431700341400ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * Tz: Set horizontal text scaling. * * @author Ben Litchfield */ public class SetTextHorizontalScaling extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } COSNumber scaling = (COSNumber)arguments.get(0); getContext().getGraphicsState().getTextState().setHorizontalScaling(scaling.floatValue()); } @Override public String getName() { return "Tz"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/SetTextLeading.java000066400000000000000000000027601320103431700321310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * TL: Set text leading. * * @author Laurent Huault */ public class SetTextLeading extends OperatorProcessor { @Override public void process(Operator operator, List arguments) { COSNumber leading = (COSNumber)arguments.get( 0 ); getContext().getGraphicsState().getTextState().setLeading(leading.floatValue()); } @Override public String getName() { return "TL"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/SetTextRenderingMode.java000066400000000000000000000040711320103431700333050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.pdmodel.graphics.state.RenderingMode; /** * Tr: Set text rendering mode. * * @author Ben Litchfield */ public class SetTextRenderingMode extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 1) { throw new MissingOperandException(operator, arguments); } COSBase base0 = arguments.get(0); if (base0 instanceof COSNumber) { COSNumber mode = (COSNumber) base0; int val = mode.intValue(); if (val >= 0 && val < RenderingMode.values().length) { getContext().getGraphicsState().getTextState() .setRenderingMode(RenderingMode.fromInt(val)); } } } @Override public String getName() { return "Tr"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/SetTextRise.java000066400000000000000000000032031320103431700314610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * Ts: Set text rise. * * @author Ben Litchfield */ public class SetTextRise extends OperatorProcessor { @Override public void process(Operator operator, List arguments) { if (!arguments.isEmpty()) { COSBase base = arguments.get(0); if (base instanceof COSNumber) { getContext().getGraphicsState().getTextState() .setRise(((COSNumber) base).floatValue()); } } } @Override public String getName() { return "Ts"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/SetWordSpacing.java000066400000000000000000000032201320103431700321310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; /** * Tw: Set word spacing. * * @author Laurent Huault */ public class SetWordSpacing extends OperatorProcessor { @Override public void process(Operator operator, List arguments) { if (!arguments.isEmpty()) { COSBase base = arguments.get(0); if (base instanceof COSNumber) { getContext().getGraphicsState().getTextState() .setWordSpacing(((COSNumber) base).floatValue()); } } } @Override public String getName() { return "Tw"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/ShowText.java000066400000000000000000000033101320103431700310220ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import static java.util.Objects.nonNull; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSString; /** * Tj: Show text. * * @author Laurent Huault */ public class ShowText extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (!arguments.isEmpty()) { COSBase base = arguments.get(0); if (base instanceof COSString && nonNull(getContext().getTextMatrix())) { getContext().showTextString(((COSString) base).getBytes()); } } } @Override public String getName() { return "Tj"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/ShowTextAdjusted.java000066400000000000000000000033331320103431700325130ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import static java.util.Objects.nonNull; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; /** * TJ: Show text, with position adjustments. * * @author Laurent Huault */ public class ShowTextAdjusted extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (!arguments.isEmpty()) { COSBase base = arguments.get(0); if (base instanceof COSArray && nonNull(getContext().getTextMatrix())) { getContext().showTextStrings((COSArray) base); } } } @Override public String getName() { return "TJ"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/ShowTextLine.java000066400000000000000000000027341320103431700316430ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; /** * ': Move to the next line and show text. * * @author Laurent Huault */ public class ShowTextLine extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { getContext().processOperator("T*", null); getContext().processOperator("Tj", arguments); } @Override public String getName() { return "'"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/contentstream/operator/text/ShowTextLineAndSpace.java000066400000000000000000000034511320103431700332370ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.contentstream.operator.text; import java.io.IOException; import java.util.List; import org.sejda.sambox.contentstream.operator.MissingOperandException; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.contentstream.operator.OperatorProcessor; import org.sejda.sambox.cos.COSBase; /** * ": Set word and character spacing, move to next line, and show text. * * @author Laurent Huault */ public class ShowTextLineAndSpace extends OperatorProcessor { @Override public void process(Operator operator, List arguments) throws IOException { if (arguments.size() < 3) { throw new MissingOperandException(operator, arguments); } getContext().processOperator("Tw", arguments.subList(0, 1)); getContext().processOperator("Tc", arguments.subList(1, 2)); getContext().processOperator("'", arguments.subList(2, 3)); } @Override public String getName() { return "\""; } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/000077500000000000000000000000001320103431700214545ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSArray.java000066400000000000000000000327311320103431700237500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static java.util.Optional.ofNullable; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Optional; /** * An array of PDFBase objects as part of the PDF document. * * @author Ben Litchfield */ public class COSArray extends COSBase implements List { private final List objects = new ArrayList<>(); public COSArray() { // default constructor } public COSArray(COSBase... items) { Arrays.stream(items).forEach(objects::add); } /** * Add an object to the array * * @param object The object to add to the array. * @see List#add(Object) */ public boolean add(COSObjectable object) { return add(object.getCOSObject()); } @Override public boolean add(COSBase object) { return objects.add(object); } /** * Add an object at the index location and push the rest to the right. * * @param index The index to add at. * @param object The object to add at that index. * @see List#add(int, Object) */ public void add(int index, COSObjectable object) { add(index, object.getCOSObject()); } @Override public void add(int index, COSBase object) { objects.add(index, object); } @Override public void clear() { objects.clear(); } @Override public boolean removeAll(Collection objectsList) { return objects.removeAll(objectsList); } @Override public boolean retainAll(Collection objectsList) { return objects.retainAll(objectsList); } @Override public boolean addAll(Collection objectsList) { return objects.addAll(objectsList); } /** * This will add all objects to this array. * * @param objectList The objects to add. */ public boolean addAll(COSArray objectList) { if (objectList != null) { return objects.addAll(objectList.objects); } return false; } @Override public boolean addAll(int i, Collection objectList) { return objects.addAll(i, objectList); } @Override public COSBase set(int index, COSBase object) { return objects.set(index, object); } /** * Set an object at a specific index. * * @param index zero based index into array. * @param object The object to set. */ public void set(int index, COSObjectable object) { COSBase base = null; if (object != null) { base = object.getCOSObject(); } set(index, base); } /** * This will get an object from the array. This will dereference the object. If the object is COSNull then null will * be returned. * * @param index The index into the array to get the object. * @return The object at the requested index. */ public COSBase getObject(int index) { return Optional.of(objects.get(index)).map(COSBase::getCOSObject) .filter(i -> i != COSNull.NULL).orElse(null); } /** * This will get an object from the array. This will dereference the object. If the type is not compatible, null is * returned * * @param index * @param clazz * @return The object that matches the key and the type or null. */ public T getObject(int index, Class clazz) { return ofNullable(objects.get(index)).map(COSBase::getCOSObject) .filter(i -> clazz.isInstance(i)).map(clazz::cast).orElse(null); } /** * Get an object from the array. This will NOT derefernce the COS object. * * @param index The index into the array to get the object. * @return The object at the requested index. * @see List#get(int) */ @Override public COSBase get(int index) { return objects.get(index); } /** * Get the value of the array as an integer. * * @param index The index into the list. * @return The value at that index or -1 if it is null. */ public int getInt(int index) { return getInt(index, -1); } /** * Get the value of the array as an integer, return the default if it does not exist. * * @param index The value of the array. * @param defaultValue The value to return if the value is null. * @return The value at the index or the defaultValue. */ public int getInt(int index, int defaultValue) { if (index < size()) { COSBase obj = objects.get(index); if (obj instanceof COSNumber) { return ((COSNumber) obj).intValue(); } } return defaultValue; } /** * Get the value of the array as a string. * * @param index The index into the array. * @return The name converted to a string or null if it does not exist. */ public String getName(int index) { return getName(index, null); } /** * Get an entry in the array that is expected to be a COSName. * * @param index The index into the array. * @param defaultValue The value to return if it is null. * @return The value at the index or defaultValue if none is found. */ public String getName(int index, String defaultValue) { if (index < size()) { COSBase obj = objects.get(index); if (obj instanceof COSName) { return ((COSName) obj).getName(); } } return defaultValue; } /** * Set the value in the array as a string. * * @param index The index into the array. * @param string The string to set in the array. */ public void setString(int index, String string) { if (string != null) { set(index, COSString.parseLiteral(string)); } else { set(index, null); } } /** * Get the value of the array as a string. * * @param index The index into the array. * @return The string or null if it does not exist. */ public String getString(int index) { return getString(index, null); } /** * Get an entry in the array that is expected to be a COSName. * * @param index The index into the array. * @param defaultValue The value to return if it is null. * @return The value at the index or defaultValue if none is found. */ public String getString(int index, String defaultValue) { if (index < size()) { Object obj = objects.get(index); if (obj instanceof COSString) { return ((COSString) obj).getString(); } } return defaultValue; } @Override public int size() { return objects.size(); } @Override public COSBase remove(int i) { return objects.remove(i); } /** * Removes the last object of the array * * @return the removed object or null if the array was empty */ public COSBase removeLast() { if (!objects.isEmpty()) { return objects.remove(objects.size() - 1); } return null; } @Override public boolean remove(Object o) { return objects.remove(o); } /** * This will remove an element from the array. This method will also remove a reference to the object. * * @param o The object to remove. * @return true if the object was removed, false otherwise */ public boolean removeObject(COSBase o) { boolean removed = this.remove(o); if (!removed) { for (int i = 0; i < this.size(); i++) { COSBase entry = this.get(i); if (entry.getCOSObject().equals(o)) { return this.remove(entry); } } } return removed; } @Override public Iterator iterator() { return objects.iterator(); } @Override public ListIterator listIterator() { return objects.listIterator(); } @Override public ListIterator listIterator(int index) { return objects.listIterator(index); } @Override public int lastIndexOf(Object o) { return objects.lastIndexOf(o); } @Override public int indexOf(Object object) { return objects.indexOf(object); } /** * This will return the index of the entry or -1 if it is not found. This method will also find references to * indirect objects. * * @param object The object to search for. * @return The index of the object or -1. */ public int indexOfObject(COSBase object) { for (int i = 0; i < this.size(); i++) { if (this.get(i).getCOSObject().equals(object)) { return i; } } return -1; } /** * This will add null values until the size of the array is at least as large as the parameter. If the array is * already larger than the parameter then nothing is done. * * @param size The desired size of the array. */ public COSArray growToSize(int size) { return growToSize(size, null); } /** * This will add the object until the size of the array is at least as large as the parameter. If the array is * already larger than the parameter then nothing is done. * * @param size The desired size of the array. * @param object The object to fill the array with. */ public COSArray growToSize(int size, COSBase object) { while (size() < size) { add(object); } return this; } /** * trims the array to the given size * * @param size */ public COSArray trimToSize(int size) { if (size() > size) { objects.subList(size, size()).clear(); } return this; } /** * This will take an COSArray of numbers and convert it to a float[]. * * @return This COSArray as an array of float numbers. */ public float[] toFloatArray() { float[] retval = new float[size()]; for (int i = 0; i < size(); i++) { retval[i] = ((COSNumber) getObject(i)).floatValue(); } return retval; } /** * Clear the current contents of the COSArray and set it with the float[]. * * @param value The new value of the float array. */ public void setFloatArray(float[] value) { this.clear(); for (float aValue : value) { add(new COSFloat(aValue)); } } /** * @return the COSArray as List */ public List toList() { ArrayList retList = new ArrayList<>(size()); Collections.copy(retList, objects); return retList; } @Override public boolean isEmpty() { return objects.isEmpty(); } @Override public Object[] toArray() { return objects.toArray(); } @Override public T[] toArray(T[] a) { return objects.toArray(a); } @Override public boolean contains(Object o) { return objects.contains(o); } @Override public boolean containsAll(Collection c) { return objects.containsAll(c); } @Override public List subList(int fromIndex, int toIndex) { return objects.subList(fromIndex, toIndex); } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } /** * @return a new {@link COSArray} that is a duplicate of this */ public COSArray duplicate() { COSArray ret = new COSArray(); ret.addAll(this); return ret; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof COSArray)) { return false; } return objects.equals(((COSArray) o).objects); } @Override public int hashCode() { return objects.hashCode(); } @Override public String toString() { return "COSArray{" + objects + "}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSArrayList.java000066400000000000000000000414471320103431700246100ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; /** * This is an implementation of a List that will sync its contents to a COSArray. * * @author Ben Litchfield */ public class COSArrayList implements List { private final COSArray array; private final List actual; private COSDictionary parentDict; private COSName dictKey; /** * Default constructor. */ public COSArrayList() { array = new COSArray(); actual = new ArrayList<>(); } /** * Constructor. * * @param actualList The list of standard java objects * @param cosArray The COS array object to sync to. */ public COSArrayList(List actualList, COSArray cosArray) { actual = actualList; array = cosArray; } /** * This is a really special constructor. Sometimes the PDF spec says that a dictionary entry can either be a single * item or an array of those items. But in the PDModel interface we really just want to always return a * java.util.List. In the case were we get the list and never modify it we don't want to convert to COSArray and put * one element, unless we append to the list. So here we are going to create this object with a single item instead * of a list, but allow more items to be added and then converted to an array. * * @param actualObject The PDModel object. * @param item The COS Model object. * @param dictionary The dictionary that holds the item, and will hold the array if an item is added. * @param dictionaryKey The key into the dictionary to set the item. */ public COSArrayList(E actualObject, COSBase item, COSDictionary dictionary, COSName dictionaryKey) { array = new COSArray(); array.add(item); actual = new ArrayList(); actual.add(actualObject); parentDict = dictionary; dictKey = dictionaryKey; } /** * This constructor is to be used if the array doesn't exist, but is to be created and added to the parent * dictionary as soon as the first element is added to the array. * * @param dictionary The dictionary that holds the item, and will hold the array if an item is added. * @param dictionaryKey The key into the dictionary to set the item. */ public COSArrayList(COSDictionary dictionary, COSName dictionaryKey) { array = new COSArray(); actual = new ArrayList<>(); parentDict = dictionary; dictKey = dictionaryKey; } /** * {@inheritDoc} */ @Override public int size() { return actual.size(); } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return actual.isEmpty(); } /** * {@inheritDoc} */ @Override public boolean contains(Object o) { return actual.contains(o); } /** * {@inheritDoc} */ @Override public Iterator iterator() { return actual.iterator(); } /** * {@inheritDoc} */ @Override public Object[] toArray() { return actual.toArray(); } /** * {@inheritDoc} */ @Override public X[] toArray(X[] a) { return actual.toArray(a); } /** * {@inheritDoc} */ @Override public boolean add(E o) { // when adding if there is a parentDict then change the item // in the dictionary from a single item to an array. if (parentDict != null) { parentDict.setItem(dictKey, array); // clear the parent dict so it doesn't happen again, there might be // a usecase for keeping the parentDict around but not now. parentDict = null; } // string is a special case because we can't subclass to be COSObjectable if (o instanceof String) { array.add(COSString.parseLiteral((String) o)); } else { if (array != null) { array.add(((COSObjectable) o).getCOSObject()); } } return actual.add(o); } /** * {@inheritDoc} */ @Override public boolean remove(Object o) { boolean retval = true; int index = actual.indexOf(o); if (index >= 0) { actual.remove(index); array.remove(index); } else { retval = false; } return retval; } /** * {@inheritDoc} */ @Override public boolean containsAll(Collection c) { return actual.containsAll(c); } /** * {@inheritDoc} */ @Override public boolean addAll(Collection c) { // when adding if there is a parentDict then change the item // in the dictionary from a single item to an array. if (parentDict != null && c.size() > 0) { parentDict.setItem(dictKey, array); // clear the parent dict so it doesn't happen again, there might be // a usecase for keeping the parentDict around but not now. parentDict = null; } array.addAll(toCOSObjectList(c)); return actual.addAll(c); } /** * {@inheritDoc} */ @Override public boolean addAll(int index, Collection c) { // when adding if there is a parentDict then change the item // in the dictionary from a single item to an array. if (parentDict != null && c.size() > 0) { parentDict.setItem(dictKey, array); // clear the parent dict so it doesn't happen again, there might be // a usecase for keeping the parentDict around but not now. parentDict = null; } array.addAll(index, toCOSObjectList(c)); return actual.addAll(index, c); } /** * This will take an array of COSNumbers and return a COSArrayList of java.lang.Integer values. * * @param intArray The existing integer Array. * * @return A list that is part of the core Java collections. */ public static List convertIntegerCOSArrayToList(COSArray intArray) { List retval = null; if (intArray != null) { List numbers = new ArrayList<>(); for (int i = 0; i < intArray.size(); i++) { COSNumber num = (COSNumber) intArray.get(i).getCOSObject(); numbers.add(num.intValue()); } retval = new COSArrayList<>(numbers, intArray); } return retval; } /** * This will take an array of COSNumbers and return a COSArrayList of java.lang.Float values. * * @param floatArray The existing float Array. * * @return The list of Float objects. */ public static List convertFloatCOSArrayToList(COSArray floatArray) { if (nonNull(floatArray)) { List numbers = new ArrayList<>(floatArray.size()); for (int i = 0; i < floatArray.size(); i++) { numbers.add(ofNullable(floatArray.getObject(i)).filter(v -> v instanceof COSNumber) .map(v -> ((COSNumber) v).floatValue()).orElse(null)); } return new COSArrayList<>(numbers, floatArray); } return null; } /** * This will take an array of COSName and return a COSArrayList of java.lang.String values. * * @param nameArray The existing name Array. * * @return The list of String objects. */ public static List convertCOSNameCOSArrayToList(COSArray nameArray) { List retval = null; if (nameArray != null) { List names = new ArrayList<>(); for (int i = 0; i < nameArray.size(); i++) { names.add(((COSName) nameArray.getObject(i)).getName()); } retval = new COSArrayList<>(names, nameArray); } return retval; } /** * This will take an array of COSString and return a COSArrayList of java.lang.String values. * * @param stringArray The existing name Array. * * @return The list of String objects. */ public static List convertCOSStringCOSArrayToList(COSArray stringArray) { List retval = null; if (stringArray != null) { List string = new ArrayList<>(); for (int i = 0; i < stringArray.size(); i++) { string.add(((COSString) stringArray.getObject(i)).getString()); } retval = new COSArrayList<>(string, stringArray); } return retval; } /** * This will take an list of string objects and return a COSArray of COSName objects. * * @param strings A list of strings * * @return An array of COSName objects */ public static COSArray convertStringListToCOSNameCOSArray(List strings) { COSArray retval = new COSArray(); for (String string : strings) { retval.add(COSName.getPDFName(string)); } return retval; } /** * This will take an list of string objects and return a COSArray of COSName objects. * * @param strings A list of strings * * @return An array of COSName objects */ public static COSArray convertStringListToCOSStringCOSArray(List strings) { COSArray retval = new COSArray(); for (String string : strings) { retval.add(COSString.parseLiteral(string)); } return retval; } /** * This will convert a list of COSObjectables to an array list of COSBase objects. * * @param cosObjectableList A list of COSObjectable. * * @return An array of COSBase or null if the input list is null or empty. */ public static COSArray converterToCOSArray(List cosObjectableList) { if (cosObjectableList != null && !cosObjectableList.isEmpty()) { if (cosObjectableList instanceof COSArrayList) { // if it is already a COSArrayList then we don't want to recreate the array, we want to reuse it. return ((COSArrayList) cosObjectableList).array; } COSArray array = new COSArray(); for (Object current : cosObjectableList) { if (current instanceof String) { array.add(COSString.parseLiteral((String) current)); } else if (current instanceof Integer || current instanceof Long) { array.add(COSInteger.get(((Number) current).longValue())); } else if (current instanceof Float || current instanceof Double) { array.add(new COSFloat(((Number) current).floatValue())); } else if (current instanceof COSObjectable) { array.add(((COSObjectable) current).getCOSObject()); } else if (current == null) { array.add(COSNull.NULL); } else { throw new RuntimeException( "Unable to convert '" + current.getClass().getName() + "' to COSBase "); } } return array; } return null; } private List toCOSObjectList(Collection list) { List cosObjects = new ArrayList<>(); for (Object next : list) { if (next instanceof String) { cosObjects.add(COSString.parseLiteral((String) next)); } else { COSObjectable cos = (COSObjectable) next; cosObjects.add(cos.getCOSObject()); } } return cosObjects; } /** * {@inheritDoc} */ @Override public boolean removeAll(Collection c) { array.removeAll(toCOSObjectList(c)); return actual.removeAll(c); } /** * {@inheritDoc} */ @Override public boolean retainAll(Collection c) { array.retainAll(toCOSObjectList(c)); return actual.retainAll(c); } /** * {@inheritDoc} */ @Override public void clear() { // when adding if there is a parentDict then change the item // in the dictionary from a single item to an array. if (parentDict != null) { parentDict.setItem(dictKey, (COSBase) null); } actual.clear(); array.clear(); } /** * {@inheritDoc} */ @Override public boolean equals(Object o) { return actual.equals(o); } /** * {@inheritDoc} */ @Override public int hashCode() { return actual.hashCode(); } /** * {@inheritDoc} */ @Override public E get(int index) { return actual.get(index); } /** * {@inheritDoc} */ @Override public E set(int index, E element) { if (element instanceof String) { COSString item = COSString.parseLiteral((String) element); if (parentDict != null && index == 0) { parentDict.setItem(dictKey, item); } array.set(index, item); } else { if (parentDict != null && index == 0) { parentDict.setItem(dictKey, ((COSObjectable) element).getCOSObject()); } array.set(index, ((COSObjectable) element).getCOSObject()); } return actual.set(index, element); } /** * {@inheritDoc} */ @Override public void add(int index, E element) { // when adding if there is a parentDict then change the item // in the dictionary from a single item to an array. if (parentDict != null) { parentDict.setItem(dictKey, array); // clear the parent dict so it doesn't happen again, there might be // a usecase for keeping the parentDict around but not now. parentDict = null; } actual.add(index, element); if (element instanceof String) { array.add(index, COSString.parseLiteral((String) element)); } else { array.add(index, ((COSObjectable) element).getCOSObject()); } } /** * {@inheritDoc} */ @Override public E remove(int index) { array.remove(index); return actual.remove(index); } /** * {@inheritDoc} */ @Override public int indexOf(Object o) { return actual.indexOf(o); } /** * {@inheritDoc} */ @Override public int lastIndexOf(Object o) { return actual.indexOf(o); } /** * {@inheritDoc} */ @Override public ListIterator listIterator() { return actual.listIterator(); } /** * {@inheritDoc} */ @Override public ListIterator listIterator(int index) { return actual.listIterator(index); } /** * {@inheritDoc} */ @Override public List subList(int fromIndex, int toIndex) { return actual.subList(fromIndex, toIndex); } /** * {@inheritDoc} */ @Override public String toString() { return "COSArrayList{" + array.toString() + "}"; } /** * This will return then underlying COSArray. * * @return the COSArray */ public COSArray toList() { return array; } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSBase.java000066400000000000000000000040501320103431700235350ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; /** * The base object that all objects in the PDF document will extend. * * @author Ben Litchfield */ public abstract class COSBase implements COSObjectable { private IndirectCOSObjectIdentifier id; /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSBase getCOSObject() { return this; } /** * Visitor pattern for the COS model objects * * @param visitor * @throws IOException */ public abstract void accept(COSVisitor visitor) throws IOException; /** * @return an identifier that can be used to identify this {@link COSBase}. */ public IndirectCOSObjectIdentifier id() { return id; } /** * Assign an id to this {@link COSBase} if it doesn't have one already. * * @param id */ public void idIfAbsent(IndirectCOSObjectIdentifier id) { if (!hasId() && id != null) { this.id = id; } } /** * @return true if the {@link COSBase} has an id assigned to it */ public boolean hasId() { return id() != null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSBoolean.java000066400000000000000000000035051320103431700242460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; /** * This class represents a boolean value in the PDF document. * * @author Ben Litchfield */ public final class COSBoolean extends COSBase { public static final COSBoolean TRUE = new COSBoolean(true); public static final COSBoolean FALSE = new COSBoolean(false); private final boolean value; private COSBoolean(boolean value) { this.value = value; } /** * @return The boolean value of this object. */ public boolean getValue() { return value; } /** * @param value Parameter telling which boolean value to get. * * @return The single boolean instance that matches the parameter. */ public static COSBoolean valueOf(boolean value) { return value ? TRUE : FALSE; } @Override public String toString() { return Boolean.toString(value); } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSDictionary.java000066400000000000000000001241051320103431700247740ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import java.io.IOException; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import org.sejda.sambox.util.DateConverter; /** * This class represents a dictionary where name/value pairs reside. * * @author Ben Litchfield * */ public class COSDictionary extends COSBase { /** * The name-value pairs of this dictionary. The pairs are kept in the order they were added to the dictionary. */ private Map items = new LinkedHashMap<>(); public COSDictionary() { // default constructor } /** * Copy Constructor. This will make a shallow copy of this dictionary. * * @param dict The dictionary to copy. */ public COSDictionary(COSDictionary dict) { items.putAll(dict.items); } /** * Search in the map for the value that matches the parameter and return the first key that maps to that value. * * @param value The value to search for in the map. * @return The key for the value in the map or null if it does not exist. */ public COSName getKeyForValue(COSBase value) { for (Map.Entry entry : items.entrySet()) { if (entry.getValue().getCOSObject().equals(value.getCOSObject())) { return entry.getKey(); } } return null; } /** * @return The number of elements in the dictionary. */ public int size() { return items.size(); } public void clear() { items.clear(); } /** * This will get an object from this dictionary. If the object is a reference then it will dereference it. If the * object is COSNull then null will be returned. * * @param key The key to the object that we are getting. * @return The object that matches the key. */ public COSBase getDictionaryObject(String key) { return getDictionaryObject(COSName.getPDFName(key)); } /** * Get an object of the expected type from this dictionary. If the type is not compatible, null is returned * * @param key * @param clazz * @return The object that matches the key and the type or null. */ public T getDictionaryObject(String key, Class clazz) { return getDictionaryObject(COSName.getPDFName(key), clazz); } /** * This is a special case of getDictionaryObject that takes multiple keys, it will handle the situation where * multiple keys could get the same value, ie if either CS or ColorSpace is used to get the colorspace. This will * get an object from this dictionary. If the object is a reference then it will dereference it and get it from the * document. If the object is COSNull then null will be returned. * * @param firstKey The first key to try. * @param secondKey The second key to try. * @return The object that matches the key. */ public COSBase getDictionaryObject(COSName firstKey, COSName secondKey) { COSBase retval = getDictionaryObject(firstKey); if (retval == null && secondKey != null) { return getDictionaryObject(secondKey); } return retval; } /** * Get an object from this dictionary. If the object is a reference then it will dereference it. If the object is * COSNull then null will be returned. * * @param key The key to the object that we are getting. * @return The object that matches the key or null. */ public COSBase getDictionaryObject(COSName key) { return ofNullable(items.get(key)).map(COSBase::getCOSObject) .filter(i -> !COSNull.NULL.equals(i)).orElse(null); } /** * Get an object of the expected type from this dictionary. If the type is not compatible, null is returned * * @param key * @param clazz * @return The object that matches the key and the type or null. */ public T getDictionaryObject(COSName key, Class clazz) { return ofNullable(items.get(key)).map(COSBase::getCOSObject) .filter(i -> clazz.isInstance(i)).map(clazz::cast).orElse(null); } /** * Set an item in the dictionary. If value is null then the result will be the same as removeItem( key ). * * @param key The key to the dictionary object. * @param value The value to the dictionary object. */ public void setItem(COSName key, COSBase value) { if (value == null) { removeItem(key); } else { items.put(key, value); } } public void putIfAbsent(COSName key, COSBase value) { items.putIfAbsent(key, value); } /** * Set the wrapped {@link COSBase} as item in the dictionary. If value is null then the result will be the same as * removeItem( key ). * * @param key The key to the dictionary object. * @param value The value to the dictionary object. */ public void setItem(COSName key, COSObjectable value) { COSBase base = null; if (value != null) { base = value.getCOSObject(); } setItem(key, base); } public void putIfAbsent(COSName key, COSObjectable value) { if (nonNull(value)) { items.putIfAbsent(key, value.getCOSObject()); } } /** * Set the wrapped {@link COSBase} as item in the dictionary. If value is null then the result will be the same as * removeItem( key ). * * @param key The key to the dictionary object. * @param value The value to the dictionary object. */ public void setItem(String key, COSObjectable value) { setItem(COSName.getPDFName(key), value); } /** * Set a boolean item in the dictionary. * * @param key The key to the dictionary object. * @param value The value to the dictionary object. */ public void setBoolean(String key, boolean value) { setItem(COSName.getPDFName(key), COSBoolean.valueOf(value)); } /** * Set a boolean item in the dictionary. * * @param key The key to the dictionary object. * @param value The value to the dictionary object. */ public void setBoolean(COSName key, boolean value) { setItem(key, COSBoolean.valueOf(value)); } public void putIfAbsent(COSName key, boolean value) { items.putIfAbsent(key, COSBoolean.valueOf(value)); } /** * Set an item in the dictionary. If value is null then the result will be the same as removeItem( key ). * * @param key The key to the dictionary object. * @param value The value to the dictionary object. */ public void setItem(String key, COSBase value) { setItem(COSName.getPDFName(key), value); } /** * Convenience method that will convert the value to a COSName object. If it is null then the object will be * removed. * * @param key The key to the object, * @param value The string value for the name. */ public void setName(String key, String value) { setName(COSName.getPDFName(key), value); } /** * Convenience method that will convert the value to a COSName object. If it is null then the object will be * removed. * * @param key The key to the object, * @param value The string value for the name. */ public void setName(COSName key, String value) { setItem(key, COSName.getPDFName(value)); } /** * Set the value of a date entry in the dictionary. * * @param key The key to the date value. * @param date The date value. */ public void setDate(String key, Calendar date) { setDate(COSName.getPDFName(key), date); } /** * Set the date object. * * @param key The key to the date. * @param date The date to set. */ public void setDate(COSName key, Calendar date) { setString(key, DateConverter.toString(date)); } /** * Set the value of a date entry in the dictionary. * * @param embedded The embedded dictionary. * @param key The key to the date value. * @param date The date value. */ public void setEmbeddedDate(String embedded, String key, Calendar date) { setEmbeddedDate(embedded, COSName.getPDFName(key), date); } /** * Set the date object. * * @param embedded The embedded dictionary. * @param key The key to the date. * @param date The date to set. */ public void setEmbeddedDate(String embedded, COSName key, Calendar date) { COSDictionary dic = getDictionaryObject(embedded, COSDictionary.class); if (dic == null && date != null) { dic = new COSDictionary(); setItem(embedded, dic); } if (dic != null) { dic.setDate(key, date); } } /** * Convenience method that will convert the value to a COSString object. If it is null then the object will be * removed. * * @param key The key to the object, * @param value The string value for the name. */ public void setString(String key, String value) { setString(COSName.getPDFName(key), value); } /** * Convenience method that will convert the value to a COSString object. If it is null then the object will be * removed. * * @param key The key to the object, * @param value The string value for the name. */ public void setString(COSName key, String value) { COSString name = null; if (value != null) { name = COSString.parseLiteral(value); } setItem(key, name); } public void putIfAbsent(COSName key, String value) { items.putIfAbsent(key, COSString.parseLiteral(value)); } /** * Convenience method that will convert the value to a COSString object. If it is null then the object will be * removed. * * @param embedded The embedded dictionary to set the item in. * @param key The key to the object, * @param value The string value for the name. */ public void setEmbeddedString(String embedded, String key, String value) { setEmbeddedString(embedded, COSName.getPDFName(key), value); } /** * Convenience method that will convert the value to a COSString object. If it is null then the object will be * removed. * * @param embedded The embedded dictionary to set the item in. * @param key The key to the object, * @param value The string value for the name. */ public void setEmbeddedString(String embedded, COSName key, String value) { COSDictionary dic = getDictionaryObject(embedded, COSDictionary.class); if (dic == null && value != null) { dic = new COSDictionary(); setItem(embedded, dic); } if (dic != null) { dic.setString(key, value); } } /** * Convenience method that will convert the value to a COSInteger object. * * @param key The key to the object, * @param value The int value for the name. */ public void setInt(String key, int value) { setInt(COSName.getPDFName(key), value); } /** * Convenience method that will convert the value to a COSInteger object. * * @param key The key to the object, * @param value The int value for the name. */ public void setInt(COSName key, int value) { setItem(key, COSInteger.get(value)); } public void putIfAbsent(COSName key, int value) { items.putIfAbsent(key, COSInteger.get(value)); } /** * Convenience method that will convert the value to a COSInteger object. * * @param key The key to the object, * @param value The int value for the name. */ public void setLong(String key, long value) { setLong(COSName.getPDFName(key), value); } /** * Convenience method that will convert the value to a COSInteger object. * * @param key The key to the object, * @param value The int value for the name. */ public void setLong(COSName key, long value) { setItem(key, COSInteger.get(value)); } public void putIfAbsent(COSName key, long value) { items.putIfAbsent(key, COSInteger.get(value)); } /** * Convenience method that will convert the value to a COSInteger object. * * @param embeddedDictionary The embedded dictionary. * @param key The key to the object, * @param value The int value for the name. */ public void setEmbeddedInt(String embeddedDictionary, String key, long value) { setEmbeddedInt(embeddedDictionary, COSName.getPDFName(key), value); } /** * Convenience method that will convert the value to a COSInteger object. * * @param embeddedDictionary The embedded dictionary. * @param key The key to the object, * @param value The int value for the name. */ public void setEmbeddedInt(String embeddedDictionary, COSName key, long value) { COSDictionary embedded = getDictionaryObject(embeddedDictionary, COSDictionary.class); if (embedded == null) { embedded = new COSDictionary(); setItem(embeddedDictionary, embedded); } embedded.setLong(key, value); } /** * Convenience method that will convert the value to a COSFloat object. * * @param key The key to the object, * @param value The int value for the name. */ public void setFloat(String key, float value) { setFloat(COSName.getPDFName(key), value); } /** * Convenience method that will convert the value to a COSFloat object. * * @param key The key to the object, * @param value The int value for the name. */ public void setFloat(COSName key, float value) { setItem(key, new COSFloat(value)); } /** * Sets the given boolean value at bitPos in the flags. * * @param field The COSName of the field to set the value into. * @param bitFlag the bit position to set the value in. * @param value the value the bit position should have. */ public void setFlag(COSName field, int bitFlag, boolean value) { int currentFlags = getInt(field, 0); if (value) { currentFlags = currentFlags | bitFlag; } else { currentFlags &= ~bitFlag; } setInt(field, currentFlags); } /** * Convenience method that will get the dictionary object that is expected to be a name. Null is returned if the * entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @return The COS name. */ public COSName getCOSName(COSName key) { return getCOSName(key, null); } /** * Convenience method that will get the dictionary object that is expected to be a name. Default is returned if the * entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The COS name. */ public COSName getCOSName(COSName key, COSName defaultValue) { return ofNullable(getDictionaryObject(key, COSName.class)).orElse(defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @return The name converted to a string. */ public String getNameAsString(String key) { return getNameAsString(COSName.getPDFName(key)); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @return The name converted to a string. */ public String getNameAsString(COSName key) { COSBase name = getDictionaryObject(key); if (name instanceof COSName) { return ((COSName) name).getName(); } else if (name instanceof COSString) { return ((COSString) name).getString(); } return null; } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The name converted to a string. */ public String getNameAsString(String key, String defaultValue) { return getNameAsString(COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The name converted to a string. */ public String getNameAsString(COSName key, String defaultValue) { return Optional.ofNullable(getNameAsString(key)).orElse(defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @return The name converted to a string. */ public String getString(String key) { return getString(COSName.getPDFName(key)); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @return The name converted to a string. */ public String getString(COSName key) { return ofNullable(getDictionaryObject(key, COSString.class)).map(COSString::getString) .orElse(null); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @param defaultValue The default value to return. * @return The name converted to a string. */ public String getString(String key, String defaultValue) { return getString(COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param key The key to the item in the dictionary. * @param defaultValue The default value to return. * @return The name converted to a string. */ public String getString(COSName key, String defaultValue) { return ofNullable(getString(key)).orElse(defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param embedded The embedded dictionary. * @param key The key to the item in the dictionary. * @return The name converted to a string. */ public String getEmbeddedString(String embedded, String key) { return getEmbeddedString(embedded, COSName.getPDFName(key), null); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param embedded The embedded dictionary. * @param key The key to the item in the dictionary. * @return The name converted to a string. */ public String getEmbeddedString(String embedded, COSName key) { return getEmbeddedString(embedded, key, null); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param embedded The embedded dictionary. * @param key The key to the item in the dictionary. * @param defaultValue The default value to return. * @return The name converted to a string. */ public String getEmbeddedString(String embedded, String key, String defaultValue) { return getEmbeddedString(embedded, COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param embedded The embedded dictionary. * @param key The key to the item in the dictionary. * @param defaultValue The default value to return. * @return The name converted to a string. */ public String getEmbeddedString(String embedded, COSName key, String defaultValue) { return ofNullable(getDictionaryObject(embedded, COSDictionary.class)) .map(d -> d.getString(key, defaultValue)).orElse(defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary or if the date was invalid. * * @param key The key to the item in the dictionary. * @return The name converted to a date. */ public Calendar getDate(String key) { return getDate(COSName.getPDFName(key)); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary or if the date was invalid. * * @param key The key to the item in the dictionary. * @return The name converted to a date. */ public Calendar getDate(COSName key) { return DateConverter.toCalendar(getDictionaryObject(key, COSString.class)); } /** * Convenience method that will get the dictionary object that is expected to be a date. Null is returned if the * entry does not exist in the dictionary or if the date was invalid. * * @param key The key to the item in the dictionary. * @param defaultValue The default value to return. * @return The name converted to a date. */ public Calendar getDate(String key, Calendar defaultValue) { return getDate(COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a date. Null is returned if the * entry does not exist in the dictionary or if the date was invalid. * * @param key The key to the item in the dictionary. * @param defaultValue The default value to return. * @return The name converted to a date. */ public Calendar getDate(COSName key, Calendar defaultValue) { return Optional.ofNullable(getDate(key)).orElse(defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param embedded The embedded dictionary to get. * @param key The key to the item in the dictionary. * @return The name converted to a string. */ public Calendar getEmbeddedDate(String embedded, String key) { return getEmbeddedDate(embedded, COSName.getPDFName(key), null); } /** * Convenience method that will get the dictionary object that is expected to be a name and convert it to a string. * Null is returned if the entry does not exist in the dictionary. * * @param embedded The embedded dictionary to get. * @param key The key to the item in the dictionary. * @return The name converted to a string. */ public Calendar getEmbeddedDate(String embedded, COSName key) { return getEmbeddedDate(embedded, key, null); } /** * Convenience method that will get the dictionary object that is expected to be a date. Null is returned if the * entry does not exist in the dictionary. * * @param embedded The embedded dictionary to get. * @param key The key to the item in the dictionary. * @param defaultValue The default value to return. * @return The name converted to a string. */ public Calendar getEmbeddedDate(String embedded, String key, Calendar defaultValue) { return getEmbeddedDate(embedded, COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a date. Null is returned if the * entry does not exist in the dictionary. * * @param embedded The embedded dictionary to get. * @param key The key to the item in the dictionary. * @param defaultValue The default value to return. * @return The name converted to a string. */ public Calendar getEmbeddedDate(String embedded, COSName key, Calendar defaultValue) { return ofNullable(getDictionaryObject(embedded, COSDictionary.class)) .map(d -> d.getDate(key, defaultValue)).orElse(defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a cos boolean and convert it to a * primitive boolean. * * @param key The key to the item in the dictionary. * @param defaultValue The value returned if the entry is null. * * @return The value converted to a boolean. */ public boolean getBoolean(String key, boolean defaultValue) { return getBoolean(COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a COSBoolean and convert it to a * primitive boolean. * * @param key The key to the item in the dictionary. * @param defaultValue The value returned if the entry is null. * * @return The entry converted to a boolean. */ public boolean getBoolean(COSName key, boolean defaultValue) { return getBoolean(key, null, defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be a COSBoolean and convert it to a * primitive boolean. * * @param firstKey The first key to the item in the dictionary. * @param secondKey The second key to the item in the dictionary. * @param defaultValue The value returned if the entry is null. * * @return The entry converted to a boolean. */ public boolean getBoolean(COSName firstKey, COSName secondKey, boolean defaultValue) { COSBase bool = getDictionaryObject(firstKey, secondKey); if (bool instanceof COSBoolean) { return ((COSBoolean) bool).getValue(); } return defaultValue; } /** * Get an integer from an embedded dictionary. Useful for 1-1 mappings. default:-1 * * @param embeddedDictionary The name of the embedded dictionary. * @param key The key in the embedded dictionary. * * @return The value of the embedded integer. */ public int getEmbeddedInt(String embeddedDictionary, String key) { return getEmbeddedInt(embeddedDictionary, COSName.getPDFName(key)); } /** * Get an integer from an embedded dictionary. Useful for 1-1 mappings. default:-1 * * @param embeddedDictionary The name of the embedded dictionary. * @param key The key in the embedded dictionary. * * @return The value of the embedded integer. */ public int getEmbeddedInt(String embeddedDictionary, COSName key) { return getEmbeddedInt(embeddedDictionary, key, -1); } /** * Get an integer from an embedded dictionary. Useful for 1-1 mappings. * * @param embeddedDictionary The name of the embedded dictionary. * @param key The key in the embedded dictionary. * @param defaultValue The value if there is no embedded dictionary or it does not contain the key. * * @return The value of the embedded integer. */ public int getEmbeddedInt(String embeddedDictionary, String key, int defaultValue) { return getEmbeddedInt(embeddedDictionary, COSName.getPDFName(key), defaultValue); } /** * Get an integer from an embedded dictionary. Useful for 1-1 mappings. * * @param embeddedDictionary The name of the embedded dictionary. * @param key The key in the embedded dictionary. * @param defaultValue The value if there is no embedded dictionary or it does not contain the key. * * @return The value of the embedded integer. */ public int getEmbeddedInt(String embeddedDictionary, COSName key, int defaultValue) { return ofNullable(getDictionaryObject(embeddedDictionary, COSDictionary.class)) .map(d -> d.getInt(key, defaultValue)).orElse(defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be an int. -1 is returned if there is * no value. * * @param key The key to the item in the dictionary. * @return The integer value. */ public int getInt(String key) { return getInt(COSName.getPDFName(key), -1); } /** * Convenience method that will get the dictionary object that is expected to be an int. -1 is returned if there is * no value. * * @param key The key to the item in the dictionary. * @return The integer value.. */ public int getInt(COSName key) { return getInt(key, -1); } /** * Convenience method that will get the dictionary object that is expected to be an integer. If the dictionary value * is null then the default Value will be returned. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The integer value. */ public int getInt(String key, int defaultValue) { return getInt(COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be an integer. If the dictionary value * is null then the default Value will be returned. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The integer value. */ public int getInt(COSName key, int defaultValue) { return getInt(key, null, defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be an integer. If the dictionary value * is null then the default Value -1 will be returned. * * @param firstKey The first key to the item in the dictionary. * @param secondKey The second key to the item in the dictionary. * @return The integer value. */ public int getInt(COSName firstKey, COSName secondKey) { return getInt(firstKey, secondKey, -1); } /** * Convenience method that will get the dictionary object that is expected to be an integer. If the dictionary value * is null then the default Value will be returned. * * @param firstKey The first key to the item in the dictionary. * @param secondKey The second key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The integer value. */ public int getInt(COSName firstKey, COSName secondKey, int defaultValue) { COSBase obj = getDictionaryObject(firstKey, secondKey); if (obj instanceof COSNumber) { return ((COSNumber) obj).intValue(); } return defaultValue; } /** * Convenience method that will get the dictionary object that is expected to be an long. -1 is returned if there is * no value. * * @param key The key to the item in the dictionary. * * @return The long value. */ public long getLong(String key) { return getLong(COSName.getPDFName(key), -1L); } /** * Convenience method that will get the dictionary object that is expected to be an long. -1 is returned if there is * no value. * * @param key The key to the item in the dictionary. * @return The long value. */ public long getLong(COSName key) { return getLong(key, -1L); } /** * Convenience method that will get the dictionary object that is expected to be an integer. If the dictionary value * is null then the default Value will be returned. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The integer value. */ public long getLong(String key, long defaultValue) { return getLong(COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be an integer. If the dictionary value * is null then the default Value will be returned. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The integer value. */ public long getLong(COSName key, long defaultValue) { return ofNullable(getDictionaryObject(key, COSNumber.class)).map(COSNumber::longValue) .orElse(defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be an float. -1 is returned if there * is no value. * * @param key The key to the item in the dictionary. * @return The float value. */ public float getFloat(String key) { return getFloat(COSName.getPDFName(key), -1); } /** * Convenience method that will get the dictionary object that is expected to be an float. -1 is returned if there * is no value. * * @param key The key to the item in the dictionary. * @return The float value. */ public float getFloat(COSName key) { return getFloat(key, -1); } /** * Convenience method that will get the dictionary object that is expected to be a float. If the dictionary value is * null then the default Value will be returned. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The float value. */ public float getFloat(String key, float defaultValue) { return getFloat(COSName.getPDFName(key), defaultValue); } /** * Convenience method that will get the dictionary object that is expected to be an float. If the dictionary value * is null then the default Value will be returned. * * @param key The key to the item in the dictionary. * @param defaultValue The value to return if the dictionary item is null. * @return The float value. */ public float getFloat(COSName key, float defaultValue) { return ofNullable(getDictionaryObject(key, COSNumber.class)).map(COSNumber::floatValue) .orElse(defaultValue); } /** * Gets the boolean value from the flags at the given bit position. * * @param field The COSName of the field to get the flag from. * @param bitFlag the bitPosition to get the value from. * * @return true if the number at bitPos is '1' */ public boolean getFlag(COSName field, int bitFlag) { int ff = getInt(field, 0); return (ff & bitFlag) == bitFlag; } /** * Remove an item for the dictionary. This will do nothing of the object does not exist. * * @param key The key to the item to remove from the dictionary. */ public void removeItem(COSName key) { items.remove(key); } /** * Remove the items for the dictionary. This will do nothing of the object does not exist. * * @param keys The keys to the item to remove from the dictionary. */ public void removeItems(COSName... keys) { Arrays.stream(keys).forEach(items::remove); } /** * @param key The key to the object. * @return The item that matches the key. */ public COSBase getItem(COSName key) { return items.get(key); } /** * @param key The key to the object. * @return The item that matches the key. */ public COSBase getItem(String key) { return getItem(COSName.getPDFName(key)); } /** * @return names of the entries in this dictionary. The returned set is in the order the entries were added to the * dictionary. */ public Set keySet() { return items.keySet(); } /** * @return name-value entries in this dictionary. The returned set is in the order the entries were added to the * dictionary. */ public Set> entrySet() { return items.entrySet(); } /** * @return All the values for the dictionary. */ public Collection getValues() { return items.values(); } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } /** * This will add all of the dictionaries keys/values to this dictionary. * * @param dic The dic to get the keys from. */ public void addAll(COSDictionary dic) { for (Map.Entry entry : dic.entrySet()) { setItem(entry.getKey(), entry.getValue()); } } /** * @see java.util.Map#containsKey(Object) * * @param name The key to find in the map. * @return true if the map contains this key. */ public boolean containsKey(COSName name) { return this.items.containsKey(name); } /** * @see java.util.Map#containsKey(Object) * * @param name The key to find in the map. * @return true if the map contains this key. */ public boolean containsKey(String name) { return containsKey(COSName.getPDFName(name)); } /** * Adds all of the dictionaries keys/values to this dictionary, but only if they don't already exist. If a key * already exists in this dictionary then nothing is changed. * * @param dic The {@link COSDictionary} to get the keys from. */ public void mergeWithoutOverwriting(COSDictionary dic) { for (Map.Entry entry : dic.entrySet()) { if (getItem(entry.getKey()) == null) { setItem(entry.getKey(), entry.getValue()); } } } /** * Adds all of the dictionaries keys/values to this dictionary. If a key already exists in this dictionary the value * is overridden. * * @param dic The {@link COSDictionary} to get the keys from. */ public void merge(COSDictionary dic) { for (Map.Entry entry : dic.entrySet()) { setItem(entry.getKey(), entry.getValue()); } } /** * @return an unmodifiable view of this dictionary */ public COSDictionary asUnmodifiableDictionary() { return new UnmodifiableCOSDictionary(this); } /** * @return a new {@link COSDictionary} that is a duplicate of this */ public COSDictionary duplicate() { return new COSDictionary(this); } @Override public String toString() { StringBuilder retVal = new StringBuilder(getClass().getSimpleName()); retVal.append("{"); for (COSName key : items.keySet()) { retVal.append("("); retVal.append(key); retVal.append(":"); retVal.append(Optional.ofNullable(getItem(key)).map(v -> { if (v instanceof COSDictionary) { return "COSDictionary{.." + ((COSDictionary) v).size() + " items ..}"; } else if (v instanceof COSArray) { return "COSArray{.." + ((COSArray) v).size() + " items ..}"; } return v.toString(); }).orElse("null")); retVal.append(") "); } retVal.append("}"); return retVal.toString(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSDocument.java000066400000000000000000000105651320103431700244510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static java.util.Optional.ofNullable; import static org.sejda.util.RequireUtils.requireNotBlank; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.IOException; import java.util.Optional; import org.sejda.sambox.util.SpecVersionUtils; import org.sejda.sambox.xref.FileTrailer; /** * This is the in-memory representation of the PDF document. * * @author Ben Litchfield * */ public class COSDocument extends COSBase { private String headerVersion; private FileTrailer trailer; public COSDocument() { this(new FileTrailer(), SpecVersionUtils.V1_4); COSDictionary catalog = new COSDictionary(); catalog.setItem(COSName.TYPE, COSName.CATALOG); trailer.getCOSObject().setItem(COSName.ROOT, catalog); } public COSDocument(FileTrailer trailer, String headerVersion) { requireNotNullArg(trailer, "Trailer cannot be null"); requireNotBlank(headerVersion, "Header version cannot be blank"); this.trailer = trailer; this.headerVersion = headerVersion; ofNullable( this.trailer.getCOSObject().getDictionaryObject(COSName.ROOT, COSDictionary.class)) .ifPresent(d -> d.setItem(COSName.TYPE, COSName.CATALOG)); } /** * Sets the version of the PDF specification to write to the file header. File header is defined in Chap 7.5.2 of * PDF 32000-1:2008 * * @param headerVersion */ public void setHeaderVersion(String headerVersion) { requireNotBlank(headerVersion, "Header version cannot be null"); this.headerVersion = headerVersion; } /** * @return the version of the PDF specification retrieved from the file header. File header is defined in Chap 7.5.2 * of PDF 32000-1:2008 */ public String getHeaderVersion() { return headerVersion; } /** * @return true If this document has an encryption dictionary */ public boolean isEncrypted() { return trailer.getCOSObject().getDictionaryObject(COSName.ENCRYPT) != null; } /** * Get the encryption dictionary if the document is encrypted or null if the document is not encrypted. * * @return The encryption dictionary. */ public COSDictionary getEncryptionDictionary() { return trailer.getCOSObject().getDictionaryObject(COSName.ENCRYPT, COSDictionary.class); } /** * Set the encryption dictionary, this should only be called when encrypting the document. * * @param dictionary The encryption dictionary. */ public void setEncryptionDictionary(COSDictionary dictionary) { trailer.getCOSObject().setItem(COSName.ENCRYPT, dictionary); } public COSArray getDocumentID() { return trailer.getCOSObject().getDictionaryObject(COSName.ID, COSArray.class); } public void setDocumentID(COSArray id) { trailer.getCOSObject().setItem(COSName.ID, id); } /** * @return the catalog for this document * @throws IllegalStateException If no catalog can be found. */ public COSDictionary getCatalog() { return Optional .ofNullable(trailer.getCOSObject().getDictionaryObject(COSName.ROOT, COSDictionary.class)) .orElseThrow(() -> new IllegalStateException("Catalog cannot be found")); } public FileTrailer getTrailer() { return trailer; } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSFloat.java000066400000000000000000000122221320103431700237300ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static org.sejda.util.RequireUtils.requireIOCondition; import java.io.IOException; import java.math.BigDecimal; /** * This class represents a floating point number in a PDF document. * * @author Ben Litchfield * */ public class COSFloat extends COSNumber { private BigDecimal value; /** * @param aFloat The primitive float object that this object wraps. */ public COSFloat(float aFloat) { // use a BigDecimal as intermediate state to avoid // a floating point string representation of the float value value = new BigDecimal(String.valueOf(aFloat)); } /** * @param aFloat The primitive float object that this object wraps. * @throws IOException If aFloat is not a float. */ public COSFloat(String aFloat) throws IOException { try { int dot = aFloat.indexOf('.'); if (dot != aFloat.lastIndexOf('.')) { // 415.75.795 we replace additional dots with 0 aFloat = aFloat.substring(0, dot + 1) + aFloat.substring(dot + 1).replaceAll("\\.", "0"); } aFloat = aFloat.replaceAll("[e|E]$", ""); value = new BigDecimal(aFloat); checkMinMaxValues(); } catch (NumberFormatException e) { try { if (aFloat.matches("^(-)([-|+]+)\\d+\\.\\d+")) { // PDFBOX-3589 --242.0 value = new BigDecimal(aFloat.replaceFirst("^(-)([\\-|\\+]+)", "-")); } else if (aFloat.matches("^0\\-(\\.|\\d+)*")) { // SAMBox 75 value = BigDecimal.ZERO; } else { // PDFBOX-2990 has 0.00000-33917698 // PDFBOX-3369 has 0.00-35095424 // PDFBOX-3500 has 0.-262 requireIOCondition(aFloat.matches("^0\\.0*\\-\\d+"), "Expected floating point number but found '" + aFloat + "'"); value = new BigDecimal("-" + aFloat.replaceFirst("\\-", "")); } checkMinMaxValues(); } catch (NumberFormatException e2) { throw new IOException( "Error expected floating point number actual='" + aFloat + "'", e2); } } } private void checkMinMaxValues() { float floatValue = value.floatValue(); double doubleValue = value.doubleValue(); boolean valueReplaced = false; // check for huge values if (floatValue == Float.NEGATIVE_INFINITY || floatValue == Float.POSITIVE_INFINITY) { if (Math.abs(doubleValue) > Float.MAX_VALUE) { floatValue = Float.MAX_VALUE * (floatValue == Float.POSITIVE_INFINITY ? 1 : -1); valueReplaced = true; } } // check for very small values else if (floatValue == 0 && doubleValue != 0) { if (Math.abs(doubleValue) < Float.MIN_NORMAL) { floatValue = Float.MIN_NORMAL; floatValue *= doubleValue >= 0 ? 1 : -1; valueReplaced = true; } } if (valueReplaced) { value = new BigDecimal(floatValue); } } @Override public float floatValue() { return value.floatValue(); } @Override public double doubleValue() { return value.doubleValue(); } @Override public long longValue() { return value.longValue(); } @Override public int intValue() { return value.intValue(); } @Override public boolean equals(Object o) { return o instanceof COSFloat && Float.floatToIntBits(((COSFloat) o).value.floatValue()) == Float .floatToIntBits(value.floatValue()); } @Override public int hashCode() { return value.hashCode(); } @Override public String toString() { return value.stripTrailingZeros().toPlainString(); } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSInteger.java000066400000000000000000000054201320103431700242620ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * This class represents an integer number in a PDF document. * * @author Ben Litchfield */ public final class COSInteger extends COSNumber { private static final Map CACHE = new ConcurrentHashMap<>(); public static final COSInteger ZERO = get(0); public static final COSInteger ONE = get(1); public static final COSInteger TWO = get(2); public static final COSInteger THREE = get(3); /** * Factory method for a COSInteger instance with the given value. * * @param val integer value * @return COSInteger instance */ public static COSInteger get(long key) { COSInteger value = CACHE.get(key); if (value == null) { final COSInteger newVal = new COSInteger(key); value = CACHE.putIfAbsent(key, newVal); if (value == null) { value = newVal; } } return value; } private final long value; /** * creates a COSInteger that is not cached */ public COSInteger(long value) { this.value = value; } @Override public boolean equals(Object o) { return o instanceof COSInteger && ((COSInteger) o).intValue() == intValue(); } @Override public int hashCode() { return Long.hashCode(value); } @Override public String toString() { return Long.toString(value); } @Override public float floatValue() { return value; } @Override public double doubleValue() { return value; } @Override public int intValue() { return (int) value; } @Override public long longValue() { return value; } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSName.java000066400000000000000000001131401320103431700235440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * A PDF Name object. * * @author Ben Litchfield */ public final class COSName extends COSBase implements Comparable { private static Map CUSTOM_NAMES = new ConcurrentHashMap<>(8192); private static Map COMMON_NAMES = new HashMap<>(); // A public static final COSName A = newCommonInstance("A"); public static final COSName AA = newCommonInstance("AA"); public static final COSName ACRO_FORM = newCommonInstance("AcroForm"); public static final COSName ACTUAL_TEXT = newCommonInstance("ActualText"); public static final COSName ADBE_PKCS7_DETACHED = newCommonInstance("adbe.pkcs7.detached"); public static final COSName ADBE_PKCS7_SHA1 = newCommonInstance("adbe.pkcs7.sha1"); public static final COSName ADBE_X509_RSA_SHA1 = newCommonInstance("adbe.x509.rsa_sha1"); public static final COSName ADOBE_PPKLITE = newCommonInstance("Adobe.PPKLite"); public static final COSName AESV2 = newCommonInstance("AESV2"); public static final COSName AESV3 = newCommonInstance("AESV3"); public static final COSName AF = newCommonInstance("AF"); public static final COSName AFTER = newCommonInstance("After"); public static final COSName AIS = newCommonInstance("AIS"); public static final COSName ALT = newCommonInstance("Alt"); public static final COSName ALPHA = newCommonInstance("Alpha"); public static final COSName ALTERNATE = newCommonInstance("Alternate"); public static final COSName ANNOT = newCommonInstance("Annot"); public static final COSName ANNOTS = newCommonInstance("Annots"); public static final COSName ANTI_ALIAS = newCommonInstance("AntiAlias"); public static final COSName AP = newCommonInstance("AP"); public static final COSName AP_REF = newCommonInstance("APRef"); public static final COSName APP = newCommonInstance("App"); public static final COSName ART_BOX = newCommonInstance("ArtBox"); public static final COSName ARTIFACT = newCommonInstance("Artifact"); public static final COSName AS = newCommonInstance("AS"); public static final COSName ASCENT = newCommonInstance("Ascent"); public static final COSName ASCII_HEX_DECODE = newCommonInstance("ASCIIHexDecode"); public static final COSName ASCII_HEX_DECODE_ABBREVIATION = newCommonInstance("AHx"); public static final COSName ASCII85_DECODE = newCommonInstance("ASCII85Decode"); public static final COSName ASCII85_DECODE_ABBREVIATION = newCommonInstance("A85"); public static final COSName ATTACHED = newCommonInstance("Attached"); public static final COSName AUTHOR = newCommonInstance("Author"); public static final COSName AUTEVENT = newCommonInstance("AuthEvent"); public static final COSName AVG_WIDTH = newCommonInstance("AvgWidth"); // B public static final COSName B = newCommonInstance("B"); public static final COSName BACKGROUND = newCommonInstance("Background"); public static final COSName BASE_ENCODING = newCommonInstance("BaseEncoding"); public static final COSName BASE_FONT = newCommonInstance("BaseFont"); public static final COSName BASE_STATE = newCommonInstance("BaseState"); public static final COSName BBOX = newCommonInstance("BBox"); public static final COSName BC = newCommonInstance("BC"); public static final COSName BE = newCommonInstance("BE"); public static final COSName BEFORE = newCommonInstance("Before"); public static final COSName BG = newCommonInstance("BG"); public static final COSName BITS_PER_COMPONENT = newCommonInstance("BitsPerComponent"); public static final COSName BITS_PER_COORDINATE = newCommonInstance("BitsPerCoordinate"); public static final COSName BITS_PER_FLAG = newCommonInstance("BitsPerFlag"); public static final COSName BITS_PER_SAMPLE = newCommonInstance("BitsPerSample"); public static final COSName BLACK_IS_1 = newCommonInstance("BlackIs1"); public static final COSName BLACK_POINT = newCommonInstance("BlackPoint"); public static final COSName BLEED_BOX = newCommonInstance("BleedBox"); public static final COSName BM = newCommonInstance("BM"); public static final COSName BORDER = newCommonInstance("Border"); public static final COSName BOUNDS = newCommonInstance("Bounds"); public static final COSName BPC = newCommonInstance("BPC"); public static final COSName BS = newCommonInstance("BS"); public static final COSName BTN = newCommonInstance("Btn"); public static final COSName BYTERANGE = newCommonInstance("ByteRange"); // C public static final COSName C = newCommonInstance("C"); public static final COSName C0 = newCommonInstance("C0"); public static final COSName C1 = newCommonInstance("C1"); public static final COSName CA = newCommonInstance("CA"); public static final COSName CA_NS = newCommonInstance("ca"); public static final COSName CALGRAY = newCommonInstance("CalGray"); public static final COSName CALRGB = newCommonInstance("CalRGB"); public static final COSName CAP = newCommonInstance("Cap"); public static final COSName CAP_HEIGHT = newCommonInstance("CapHeight"); public static final COSName CATALOG = newCommonInstance("Catalog"); public static final COSName CCITTFAX_DECODE = newCommonInstance("CCITTFaxDecode"); public static final COSName CCITTFAX_DECODE_ABBREVIATION = newCommonInstance("CCF"); public static final COSName CENTER_WINDOW = newCommonInstance("CenterWindow"); public static final COSName CF = newCommonInstance("CF"); public static final COSName CFM = newCommonInstance("CFM"); public static final COSName CH = newCommonInstance("Ch"); public static final COSName CHAR_PROCS = newCommonInstance("CharProcs"); public static final COSName CHAR_SET = newCommonInstance("CharSet"); public static final COSName CI = newCommonInstance("CI"); public static final COSName CICI_SIGNIT = newCommonInstance("CICI.SignIt"); public static final COSName CID_FONT_TYPE0 = newCommonInstance("CIDFontType0"); public static final COSName CID_FONT_TYPE2 = newCommonInstance("CIDFontType2"); public static final COSName CID_TO_GID_MAP = newCommonInstance("CIDToGIDMap"); public static final COSName CID_SET = newCommonInstance("CIDSet"); public static final COSName CIDSYSTEMINFO = newCommonInstance("CIDSystemInfo"); public static final COSName CL = newCommonInstance("CL"); public static final COSName CLR_F = newCommonInstance("ClrF"); public static final COSName CLR_FF = newCommonInstance("ClrFf"); public static final COSName CMAP = newCommonInstance("CMap"); public static final COSName CMAPNAME = newCommonInstance("CMapName"); public static final COSName CMYK = newCommonInstance("CMYK"); public static final COSName CO = newCommonInstance("CO"); public static final COSName COLOR_BURN = newCommonInstance("ColorBurn"); public static final COSName COLOR_DODGE = newCommonInstance("ColorDodge"); public static final COSName COLORANTS = newCommonInstance("Colorants"); public static final COSName COLORS = newCommonInstance("Colors"); public static final COSName COLORSPACE = newCommonInstance("ColorSpace"); public static final COSName COLUMNS = newCommonInstance("Columns"); public static final COSName COMPATIBLE = newCommonInstance("Compatible"); public static final COSName COMPONENTS = newCommonInstance("Components"); public static final COSName CONTACT_INFO = newCommonInstance("ContactInfo"); public static final COSName CONTENTS = newCommonInstance("Contents"); public static final COSName COORDS = newCommonInstance("Coords"); public static final COSName COUNT = newCommonInstance("Count"); public static final COSName CP = newCommonInstance("CP"); public static final COSName CREATION_DATE = newCommonInstance("CreationDate"); public static final COSName CREATOR = newCommonInstance("Creator"); public static final COSName CROP_BOX = newCommonInstance("CropBox"); public static final COSName CRYPT = newCommonInstance("Crypt"); public static final COSName CS = newCommonInstance("CS"); // D public static final COSName D = newCommonInstance("D"); public static final COSName DA = newCommonInstance("DA"); public static final COSName DARKEN = newCommonInstance("Darken"); public static final COSName DATAPREP = newCommonInstance("DataPrep"); public static final COSName DATE = newCommonInstance("Date"); public static final COSName DCT_DECODE = newCommonInstance("DCTDecode"); public static final COSName DCT_DECODE_ABBREVIATION = newCommonInstance("DCT"); public static final COSName DECODE = newCommonInstance("Decode"); public static final COSName DECODE_PARMS = newCommonInstance("DecodeParms"); public static final COSName DEFAULT = newCommonInstance("default"); public static final COSName DEFAULT_CMYK = newCommonInstance("DefaultCMYK"); public static final COSName DEFAULT_GRAY = newCommonInstance("DefaultGray"); public static final COSName DEFAULT_RGB = newCommonInstance("DefaultRGB"); public static final COSName DESC = newCommonInstance("Desc"); public static final COSName DESCENDANT_FONTS = newCommonInstance("DescendantFonts"); public static final COSName DESCENT = newCommonInstance("Descent"); public static final COSName DEST = newCommonInstance("Dest"); public static final COSName DEST_OUTPUT_PROFILE = newCommonInstance("DestOutputProfile"); public static final COSName DESTS = newCommonInstance("Dests"); public static final COSName DEVICECMYK = newCommonInstance("DeviceCMYK"); public static final COSName DEVICEGRAY = newCommonInstance("DeviceGray"); public static final COSName DEVICEN = newCommonInstance("DeviceN"); public static final COSName DEVICERGB = newCommonInstance("DeviceRGB"); public static final COSName DI = newCommonInstance("Di"); public static final COSName DIFFERENCE = newCommonInstance("Difference"); public static final COSName DIFFERENCES = newCommonInstance("Differences"); public static final COSName DIGEST_METHOD = newCommonInstance("DigestMethod"); public static final COSName DIGEST_RIPEMD160 = newCommonInstance("RIPEMD160"); public static final COSName DIGEST_SHA1 = newCommonInstance("SHA1"); public static final COSName DIGEST_SHA256 = newCommonInstance("SHA256"); public static final COSName DIGEST_SHA384 = newCommonInstance("SHA384"); public static final COSName DIGEST_SHA512 = newCommonInstance("SHA512"); public static final COSName DIRECTION = newCommonInstance("Direction"); public static final COSName DISPLAY_DOC_TITLE = newCommonInstance("DisplayDocTitle"); public static final COSName DL = newCommonInstance("DL"); public static final COSName DM = newCommonInstance("Dm"); public static final COSName DOC = newCommonInstance("Doc"); public static final COSName DOC_CHECKSUM = newCommonInstance("DocChecksum"); public static final COSName DOC_OPEN = newCommonInstance("DocOpen"); public static final COSName DOC_TIME_STAMP = newCommonInstance("DocTimeStamp"); public static final COSName DOCMDP = newCommonInstance("DocMDP"); public static final COSName DOMAIN = newCommonInstance("Domain"); public static final COSName DOS = newCommonInstance("DOS"); public static final COSName DP = newCommonInstance("DP"); public static final COSName DR = newCommonInstance("DR"); public static final COSName DS = newCommonInstance("DS"); public static final COSName DUPLEX = newCommonInstance("Duplex"); public static final COSName DUR = newCommonInstance("Dur"); public static final COSName DV = newCommonInstance("DV"); public static final COSName DW = newCommonInstance("DW"); public static final COSName DW2 = newCommonInstance("DW2"); // E public static final COSName E = newCommonInstance("E"); public static final COSName EARLY_CHANGE = newCommonInstance("EarlyChange"); public static final COSName EF = newCommonInstance("EF"); public static final COSName EMBEDDED_FDFS = newCommonInstance("EmbeddedFDFs"); public static final COSName EMBEDDED_FILE = newCommonInstance("EmbeddedFile"); public static final COSName EMBEDDED_FILES = newCommonInstance("EmbeddedFiles"); public static final COSName EMPTY = newCommonInstance(""); public static final COSName ENCODE = newCommonInstance("Encode"); public static final COSName ENCODED_BYTE_ALIGN = newCommonInstance("EncodedByteAlign"); public static final COSName ENCODING = newCommonInstance("Encoding"); public static final COSName ENCODING_90MS_RKSJ_H = newCommonInstance("90ms-RKSJ-H"); public static final COSName ENCODING_90MS_RKSJ_V = newCommonInstance("90ms-RKSJ-V"); public static final COSName ENCODING_ETEN_B5_H = newCommonInstance("ETen-B5-H"); public static final COSName ENCODING_ETEN_B5_V = newCommonInstance("ETen-B5-V"); public static final COSName ENCRYPT = newCommonInstance("Encrypt"); public static final COSName ENCRYPT_META_DATA = newCommonInstance("EncryptMetadata"); public static final COSName END_OF_LINE = newCommonInstance("EndOfLine"); public static final COSName ENTRUST_PPKEF = newCommonInstance("Entrust.PPKEF"); public static final COSName EXCLUSION = newCommonInstance("Exclusion"); public static final COSName EXT_G_STATE = newCommonInstance("ExtGState"); public static final COSName EXTEND = newCommonInstance("Extend"); public static final COSName EXTENDS = newCommonInstance("Extends"); // F public static final COSName F = newCommonInstance("F"); public static final COSName F_DECODE_PARMS = newCommonInstance("FDecodeParms"); public static final COSName F_FILTER = newCommonInstance("FFilter"); public static final COSName FB = newCommonInstance("FB"); public static final COSName FDF = newCommonInstance("FDF"); public static final COSName FF = newCommonInstance("Ff"); public static final COSName FIELDS = newCommonInstance("Fields"); public static final COSName FILESPEC = newCommonInstance("Filespec"); public static final COSName FILTER = newCommonInstance("Filter"); public static final COSName FIRST = newCommonInstance("First"); public static final COSName FIRST_CHAR = newCommonInstance("FirstChar"); public static final COSName FIT_WINDOW = newCommonInstance("FitWindow"); public static final COSName FL = newCommonInstance("FL"); public static final COSName FLAGS = newCommonInstance("Flags"); public static final COSName FLATE_DECODE = newCommonInstance("FlateDecode"); public static final COSName FLATE_DECODE_ABBREVIATION = newCommonInstance("Fl"); public static final COSName FONT = newCommonInstance("Font"); public static final COSName FONT_BBOX = newCommonInstance("FontBBox"); public static final COSName FONT_DESC = newCommonInstance("FontDescriptor"); public static final COSName FONT_FAMILY = newCommonInstance("FontFamily"); public static final COSName FONT_FILE = newCommonInstance("FontFile"); public static final COSName FONT_FILE2 = newCommonInstance("FontFile2"); public static final COSName FONT_FILE3 = newCommonInstance("FontFile3"); public static final COSName FONT_MATRIX = newCommonInstance("FontMatrix"); public static final COSName FONT_NAME = newCommonInstance("FontName"); public static final COSName FONT_STRETCH = newCommonInstance("FontStretch"); public static final COSName FONT_WEIGHT = newCommonInstance("FontWeight"); public static final COSName FORM = newCommonInstance("Form"); public static final COSName FORMTYPE = newCommonInstance("FormType"); public static final COSName FRM = newCommonInstance("FRM"); public static final COSName FT = newCommonInstance("FT"); public static final COSName FUNCTION = newCommonInstance("Function"); public static final COSName FUNCTION_TYPE = newCommonInstance("FunctionType"); public static final COSName FUNCTIONS = newCommonInstance("Functions"); // G public static final COSName G = newCommonInstance("G"); public static final COSName GAMMA = newCommonInstance("Gamma"); public static final COSName GROUP = newCommonInstance("Group"); public static final COSName GTS_PDFA1 = newCommonInstance("GTS_PDFA1"); // H public static final COSName H = newCommonInstance("H"); public static final COSName HARD_LIGHT = newCommonInstance("HardLight"); public static final COSName HEIGHT = newCommonInstance("Height"); public static final COSName HIDE_MENUBAR = newCommonInstance("HideMenubar"); public static final COSName HIDE_TOOLBAR = newCommonInstance("HideToolbar"); public static final COSName HIDE_WINDOWUI = newCommonInstance("HideWindowUI"); // I public static final COSName I = newCommonInstance("I"); public static final COSName IC = newCommonInstance("IC"); public static final COSName ICCBASED = newCommonInstance("ICCBased"); public static final COSName ID = newCommonInstance("ID"); public static final COSName ID_TREE = newCommonInstance("IDTree"); public static final COSName IDENTITY = newCommonInstance("Identity"); public static final COSName IDENTITY_H = newCommonInstance("Identity-H"); public static final COSName IDENTITY_V = newCommonInstance("Identity-V"); public static final COSName IF = newCommonInstance("IF"); public static final COSName IM = newCommonInstance("IM"); public static final COSName IMAGE = newCommonInstance("Image"); public static final COSName IMAGE_MASK = newCommonInstance("ImageMask"); public static final COSName INDEX = newCommonInstance("Index"); public static final COSName INDEXED = newCommonInstance("Indexed"); public static final COSName INFO = newCommonInstance("Info"); public static final COSName INKLIST = newCommonInstance("InkList"); public static final COSName INTERPOLATE = newCommonInstance("Interpolate"); public static final COSName IT = newCommonInstance("IT"); public static final COSName ITALIC_ANGLE = newCommonInstance("ItalicAngle"); // J public static final COSName JAVA_SCRIPT = newCommonInstance("JavaScript"); public static final COSName JBIG2_DECODE = newCommonInstance("JBIG2Decode"); public static final COSName JBIG2_GLOBALS = newCommonInstance("JBIG2Globals"); public static final COSName JPX_DECODE = newCommonInstance("JPXDecode"); public static final COSName JS = newCommonInstance("JS"); // K public static final COSName K = newCommonInstance("K"); public static final COSName KEYWORDS = newCommonInstance("Keywords"); public static final COSName KIDS = newCommonInstance("Kids"); // L public static final COSName L = newCommonInstance("L"); public static final COSName LAB = newCommonInstance("Lab"); public static final COSName LANG = newCommonInstance("Lang"); public static final COSName LAST = newCommonInstance("Last"); public static final COSName LAST_CHAR = newCommonInstance("LastChar"); public static final COSName LAST_MODIFIED = newCommonInstance("LastModified"); public static final COSName LC = newCommonInstance("LC"); public static final COSName LE = newCommonInstance("LE"); public static final COSName LEADING = newCommonInstance("Leading"); public static final COSName LEGAL_ATTESTATION = newCommonInstance("LegalAttestation"); public static final COSName LENGTH = newCommonInstance("Length"); public static final COSName LENGTH1 = newCommonInstance("Length1"); public static final COSName LENGTH2 = newCommonInstance("Length2"); public static final COSName LIGHTEN = newCommonInstance("Lighten"); public static final COSName LIMITS = newCommonInstance("Limits"); public static final COSName LJ = newCommonInstance("LJ"); public static final COSName LL = newCommonInstance("LL"); public static final COSName LLE = newCommonInstance("LLE"); public static final COSName LLO = newCommonInstance("LLO"); public static final COSName LOCATION = newCommonInstance("Location"); public static final COSName LOCK = newCommonInstance("Lock"); public static final COSName LUMINOSITY = newCommonInstance("Luminosity"); public static final COSName LW = newCommonInstance("LW"); public static final COSName LZW_DECODE = newCommonInstance("LZWDecode"); public static final COSName LZW_DECODE_ABBREVIATION = newCommonInstance("LZW"); // M public static final COSName M = newCommonInstance("M"); public static final COSName MAC = newCommonInstance("Mac"); public static final COSName MAC_EXPERT_ENCODING = newCommonInstance("MacExpertEncoding"); public static final COSName MAC_ROMAN_ENCODING = newCommonInstance("MacRomanEncoding"); public static final COSName MARK_INFO = newCommonInstance("MarkInfo"); public static final COSName MASK = newCommonInstance("Mask"); public static final COSName MATRIX = newCommonInstance("Matrix"); public static final COSName MATTE = newCommonInstance("Matte"); public static final COSName MAX_LEN = newCommonInstance("MaxLen"); public static final COSName MAX_WIDTH = newCommonInstance("MaxWidth"); public static final COSName MCID = newCommonInstance("MCID"); public static final COSName MDP = newCommonInstance("MDP"); public static final COSName MEDIA_BOX = newCommonInstance("MediaBox"); public static final COSName METADATA = newCommonInstance("Metadata"); public static final COSName MISSING_WIDTH = newCommonInstance("MissingWidth"); public static final COSName MK = newCommonInstance("MK"); public static final COSName ML = newCommonInstance("ML"); public static final COSName MM_TYPE1 = newCommonInstance("MMType1"); public static final COSName MOD_DATE = newCommonInstance("ModDate"); public static final COSName MULTIPLY = newCommonInstance("Multiply"); // N public static final COSName N = newCommonInstance("N"); public static final COSName NAME = newCommonInstance("Name"); public static final COSName NAMES = newCommonInstance("Names"); public static final COSName NEED_APPEARANCES = newCommonInstance("NeedAppearances"); public static final COSName NEXT = newCommonInstance("Next"); public static final COSName NM = newCommonInstance("NM"); public static final COSName NON_EFONT_NO_WARN = newCommonInstance("NonEFontNoWarn"); public static final COSName NON_FULL_SCREEN_PAGE_MODE = newCommonInstance( "NonFullScreenPageMode"); public static final COSName NONE = newCommonInstance("None"); public static final COSName NORMAL = newCommonInstance("Normal"); public static final COSName NUMS = newCommonInstance("Nums"); // O public static final COSName O = newCommonInstance("O"); public static final COSName OBJ = newCommonInstance("Obj"); public static final COSName OBJ_STM = newCommonInstance("ObjStm"); public static final COSName OC = newCommonInstance("OC"); public static final COSName OCG = newCommonInstance("OCG"); public static final COSName OCGS = newCommonInstance("OCGs"); public static final COSName OCPROPERTIES = newCommonInstance("OCProperties"); public static final COSName OE = newCommonInstance("OE"); public static final COSName OFF = newCommonInstance("OFF"); public static final COSName Off = newCommonInstance("Off"); public static final COSName ON = newCommonInstance("ON"); public static final COSName OP = newCommonInstance("OP"); public static final COSName OP_NS = newCommonInstance("op"); public static final COSName OPEN_ACTION = newCommonInstance("OpenAction"); public static final COSName OPEN_TYPE = newCommonInstance("OpenType"); public static final COSName OPM = newCommonInstance("OPM"); public static final COSName OPT = newCommonInstance("Opt"); public static final COSName ORDER = newCommonInstance("Order"); public static final COSName ORDERING = newCommonInstance("Ordering"); public static final COSName OS = newCommonInstance("OS"); public static final COSName OUTLINES = newCommonInstance("Outlines"); public static final COSName OUTPUT_CONDITION = newCommonInstance("OutputCondition"); public static final COSName OUTPUT_CONDITION_IDENTIFIER = newCommonInstance( "OutputConditionIdentifier"); public static final COSName OUTPUT_INTENT = newCommonInstance("OutputIntent"); public static final COSName OUTPUT_INTENTS = newCommonInstance("OutputIntents"); public static final COSName OVERLAY = newCommonInstance("Overlay"); // P public static final COSName P = newCommonInstance("P"); public static final COSName PAGE = newCommonInstance("Page"); public static final COSName PAGE_LABELS = newCommonInstance("PageLabels"); public static final COSName PAGE_LAYOUT = newCommonInstance("PageLayout"); public static final COSName PAGE_MODE = newCommonInstance("PageMode"); public static final COSName PAGES = newCommonInstance("Pages"); public static final COSName PAINT_TYPE = newCommonInstance("PaintType"); public static final COSName PANOSE = newCommonInstance("Panose"); public static final COSName PARAMS = newCommonInstance("Params"); public static final COSName PARENT = newCommonInstance("Parent"); public static final COSName PARENT_TREE = newCommonInstance("ParentTree"); public static final COSName PARENT_TREE_NEXT_KEY = newCommonInstance("ParentTreeNextKey"); public static final COSName PATTERN = newCommonInstance("Pattern"); public static final COSName PATTERN_TYPE = newCommonInstance("PatternType"); public static final COSName PDF_DOC_ENCODING = newCommonInstance("PDFDocEncoding"); public static final COSName PERMS = newCommonInstance("Perms"); public static final COSName PG = newCommonInstance("Pg"); public static final COSName PIECE_INFO = newCommonInstance("PieceInfo"); public static final COSName PMD = newCommonInstance("PMD"); public static final COSName POPUP = newCommonInstance("Popup"); public static final COSName PRE_RELEASE = newCommonInstance("PreRelease"); public static final COSName PREDICTOR = newCommonInstance("Predictor"); public static final COSName PREV = newCommonInstance("Prev"); public static final COSName PRINT_AREA = newCommonInstance("PrintArea"); public static final COSName PRINT_CLIP = newCommonInstance("PrintClip"); public static final COSName PRINT_SCALING = newCommonInstance("PrintScaling"); public static final COSName PROC_SET = newCommonInstance("ProcSet"); public static final COSName PROCESS = newCommonInstance("Process"); public static final COSName PRODUCER = newCommonInstance("Producer"); public static final COSName PROP_BUILD = newCommonInstance("Prop_Build"); public static final COSName PROPERTIES = newCommonInstance("Properties"); public static final COSName PS = newCommonInstance("PS"); public static final COSName PUB_SEC = newCommonInstance("PubSec"); // Q public static final COSName Q = newCommonInstance("Q"); public static final COSName QUADPOINTS = newCommonInstance("QuadPoints"); // R public static final COSName R = newCommonInstance("R"); public static final COSName RANGE = newCommonInstance("Range"); public static final COSName RC = newCommonInstance("RC"); public static final COSName RD = newCommonInstance("RD"); public static final COSName REASON = newCommonInstance("Reason"); public static final COSName REASONS = newCommonInstance("Reasons"); public static final COSName RECIPIENTS = newCommonInstance("Recipients"); public static final COSName RECT = newCommonInstance("Rect"); public static final COSName REGISTRY = newCommonInstance("Registry"); public static final COSName REGISTRY_NAME = newCommonInstance("RegistryName"); public static final COSName RENAME = newCommonInstance("Rename"); public static final COSName RESOURCES = newCommonInstance("Resources"); public static final COSName RGB = newCommonInstance("RGB"); public static final COSName RI = newCommonInstance("RI"); public static final COSName ROLE_MAP = newCommonInstance("RoleMap"); public static final COSName ROOT = newCommonInstance("Root"); public static final COSName ROTATE = newCommonInstance("Rotate"); public static final COSName ROWS = newCommonInstance("Rows"); public static final COSName RUN_LENGTH_DECODE = newCommonInstance("RunLengthDecode"); public static final COSName RUN_LENGTH_DECODE_ABBREVIATION = newCommonInstance("RL"); public static final COSName RV = newCommonInstance("RV"); // S public static final COSName S = newCommonInstance("S"); public static final COSName SA = newCommonInstance("SA"); public static final COSName SCREEN = newCommonInstance("Screen"); public static final COSName SE = newCommonInstance("SE"); public static final COSName SEPARATION = newCommonInstance("Separation"); public static final COSName SET_F = newCommonInstance("SetF"); public static final COSName SET_FF = newCommonInstance("SetFf"); public static final COSName SHADING = newCommonInstance("Shading"); public static final COSName SHADING_TYPE = newCommonInstance("ShadingType"); public static final COSName SIG = newCommonInstance("Sig"); public static final COSName SIG_FLAGS = newCommonInstance("SigFlags"); public static final COSName SIZE = newCommonInstance("Size"); public static final COSName SM = newCommonInstance("SM"); public static final COSName SMASK = newCommonInstance("SMask"); public static final COSName SOFT_LIGHT = newCommonInstance("SoftLight"); public static final COSName SOUND = newCommonInstance("Sound"); public static final COSName SS = newCommonInstance("SS"); public static final COSName ST = newCommonInstance("St"); public static final COSName STANDARD = newCommonInstance("Standard"); public static final COSName STANDARD_ENCODING = newCommonInstance("StandardEncoding"); public static final COSName STATE = newCommonInstance("State"); public static final COSName STATE_MODEL = newCommonInstance("StateModel"); public static final COSName STATUS = newCommonInstance("Status"); public static final COSName STD_CF = newCommonInstance("StdCF"); public static final COSName STEM_H = newCommonInstance("StemH"); public static final COSName STEM_V = newCommonInstance("StemV"); public static final COSName STM_F = newCommonInstance("StmF"); public static final COSName STR_F = newCommonInstance("StrF"); public static final COSName STRUCT_PARENT = newCommonInstance("StructParent"); public static final COSName STRUCT_PARENTS = newCommonInstance("StructParents"); public static final COSName STRUCT_TREE_ROOT = newCommonInstance("StructTreeRoot"); public static final COSName STYLE = newCommonInstance("Style"); public static final COSName SUB_FILTER = newCommonInstance("SubFilter"); public static final COSName SUBJ = newCommonInstance("Subj"); public static final COSName SUBJECT = newCommonInstance("Subject"); public static final COSName SUBTYPE = newCommonInstance("Subtype"); public static final COSName SUPPLEMENT = newCommonInstance("Supplement"); public static final COSName SV = newCommonInstance("SV"); public static final COSName SW = newCommonInstance("SW"); // T public static final COSName T = newCommonInstance("T"); public static final COSName TARGET = newCommonInstance("Target"); public static final COSName TEMPLATES = newCommonInstance("Templates"); public static final COSName THREADS = newCommonInstance("Threads"); public static final COSName TI = newCommonInstance("TI"); public static final COSName TILING_TYPE = newCommonInstance("TilingType"); public static final COSName TIME_STAMP = newCommonInstance("TimeStamp"); public static final COSName TITLE = newCommonInstance("Title"); public static final COSName TK = newCommonInstance("TK"); public static final COSName TM = newCommonInstance("TM"); public static final COSName TO_UNICODE = newCommonInstance("ToUnicode"); public static final COSName TR = newCommonInstance("TR"); public static final COSName TR2 = newCommonInstance("TR2"); public static final COSName TRAPPED = newCommonInstance("Trapped"); public static final COSName TRANS = newCommonInstance("Trans"); public static final COSName TRANSPARENCY = newCommonInstance("Transparency"); public static final COSName TREF = newCommonInstance("TRef"); public static final COSName TRIM_BOX = newCommonInstance("TrimBox"); public static final COSName TRUE_TYPE = newCommonInstance("TrueType"); public static final COSName TRUSTED_MODE = newCommonInstance("TrustedMode"); public static final COSName TU = newCommonInstance("TU"); /** Acro form field type for text field. */ public static final COSName TX = newCommonInstance("Tx"); public static final COSName TYPE = newCommonInstance("Type"); public static final COSName TYPE0 = newCommonInstance("Type0"); public static final COSName TYPE1 = newCommonInstance("Type1"); public static final COSName TYPE3 = newCommonInstance("Type3"); // U public static final COSName U = newCommonInstance("U"); public static final COSName UE = newCommonInstance("UE"); public static final COSName UF = newCommonInstance("UF"); public static final COSName UNCHANGED = newCommonInstance("Unchanged"); public static final COSName UNIX = newCommonInstance("Unix"); public static final COSName URI = newCommonInstance("URI"); public static final COSName URL = newCommonInstance("URL"); // V public static final COSName V = newCommonInstance("V"); public static final COSName V2 = newCommonInstance("V2"); public static final COSName VERISIGN_PPKVS = newCommonInstance("VeriSign.PPKVS"); public static final COSName VERSION = newCommonInstance("Version"); public static final COSName VERTICES = newCommonInstance("Vertices"); public static final COSName VERTICES_PER_ROW = newCommonInstance("VerticesPerRow"); public static final COSName VIEW_AREA = newCommonInstance("ViewArea"); public static final COSName VIEW_CLIP = newCommonInstance("ViewClip"); public static final COSName VIEWER_PREFERENCES = newCommonInstance("ViewerPreferences"); // W public static final COSName W = newCommonInstance("W"); public static final COSName W2 = newCommonInstance("W2"); public static final COSName WHITE_POINT = newCommonInstance("WhitePoint"); public static final COSName WIDGET = newCommonInstance("Widget"); public static final COSName WIDTH = newCommonInstance("Width"); public static final COSName WIDTHS = newCommonInstance("Widths"); public static final COSName WIN_ANSI_ENCODING = newCommonInstance("WinAnsiEncoding"); // X public static final COSName XFA = newCommonInstance("XFA"); public static final COSName X_STEP = newCommonInstance("XStep"); public static final COSName XHEIGHT = newCommonInstance("XHeight"); public static final COSName XOBJECT = newCommonInstance("XObject"); public static final COSName XREF = newCommonInstance("XRef"); public static final COSName XREF_STM = newCommonInstance("XRefStm"); // Y public static final COSName Y_STEP = newCommonInstance("YStep"); public static final COSName YES = newCommonInstance("Yes"); private final String name; /** * This will get a COSName object with that name. * * @param aName The name of the object. * * @return A COSName with the specified name. */ public static COSName getPDFName(String aName) { if (aName != null) { COSName cosName = COMMON_NAMES.get(aName); if (cosName != null) { return cosName; } return getCustom(aName); } return null; } private static COSName getCustom(String customName) { COSName cosName = CUSTOM_NAMES.get(customName); if (cosName == null) { final COSName value = new COSName(customName); cosName = CUSTOM_NAMES.putIfAbsent(customName, value); if (cosName == null) { cosName = value; } } return cosName; } private static COSName newCommonInstance(String commonName) { final COSName value = new COSName(commonName); COMMON_NAMES.put(commonName, value); return value; } private COSName(String name) { this.name = name; } /** * @return The name of the object. */ public String getName() { return name; } @Override public String toString() { return "COSName{" + name + "}"; } @Override public boolean equals(Object object) { return object instanceof COSName && name.equals(((COSName) object).name); } @Override public int hashCode() { return name.hashCode(); } @Override public int compareTo(COSName other) { return name.compareTo(other.name); } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSNull.java000066400000000000000000000025451320103431700236040ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; /** * This class represents a null PDF object. * * @author Ben Litchfield */ public final class COSNull extends COSBase { /** * The one null object in the system. */ public static final COSNull NULL = new COSNull(); private COSNull() { // limit creation to one instance. } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } @Override public String toString() { return "COSNull"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSNumber.java000066400000000000000000000052451320103431700241220ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static org.sejda.util.RequireUtils.requireArg; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.IOException; /** * This class represents an abstract number in a PDF document. * * @author Ben Litchfield */ public abstract class COSNumber extends COSBase { /** * @return The float value of this object. */ public abstract float floatValue(); /** * @return The double value of this number. */ public abstract double doubleValue(); /** * @return The integer value of this number. */ public abstract int intValue(); /** * @return The long value of this number. */ public abstract long longValue(); /** * This factory method will get the appropriate number object. * * @param number The string representation of the number. * @return A number object, either float or int. * @throws IOException If the string is not a number. */ public static COSNumber get(String number) throws IOException { requireNotNullArg(number, "Number cannot be null"); requireArg(number.matches("(E|e|\\+|\\-|\\.|\\d)+"), "Invalid number " + number); if (number.length() == 1) { char digit = number.charAt(0); if ('0' <= digit && digit <= '9') { return COSInteger.get(digit - '0'); } // PDFBOX-592 return COSInteger.ZERO; } else if (number.indexOf('.') == -1 && (number.toLowerCase().indexOf('e') == -1)) { try { return COSInteger.get(Long.parseLong(number)); } catch (NumberFormatException e) { // PDFBOX-3589 --242 return COSInteger.ZERO; } } return new COSFloat(number); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSObjectKey.java000066400000000000000000000045541320103431700245530ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static java.util.Comparator.comparing; import java.util.Objects; /** * Object representing a key to identify a PDF object * * @author Michael Traut */ public final class COSObjectKey implements Comparable { private final long number; private final int generation; /** * @param number The object number. * @param generation The object generation number. */ public COSObjectKey(long number, int generation) { this.number = number; this.generation = generation; } /** * @return The objects generation number. */ public int generation() { return generation; } /** * @return The object number */ public long objectNumber() { return number; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof COSObjectKey)) { return false; } COSObjectKey other = (COSObjectKey) obj; return (number == other.number && generation == other.generation); } @Override public int hashCode() { return Long.hashCode(number + generation); } @Override public String toString() { return String.format("on=%d, gn=%d", number, generation); } @Override public int compareTo(COSObjectKey other) { return Objects.compare(this, other, comparing(COSObjectKey::objectNumber).thenComparingInt(k -> k.generation())); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSObjectable.java000066400000000000000000000021631320103431700247200ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; /** * This is an interface used to wrap {@link COSBase} objects. * * @author Ben Litchfield */ // TODO rename this to COSObject once you feel it's time to public interface COSObjectable { /** * @return The {@link COSBase} that matches this Java object. */ COSBase getCOSObject(); } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSStream.java000066400000000000000000000503521320103431700241240ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import static org.sejda.io.SeekableSources.inMemorySeekableSourceFrom; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.io.SeekableSource; import org.sejda.io.SeekableSourceSupplier; import org.sejda.sambox.filter.DecodeResult; import org.sejda.sambox.filter.Filter; import org.sejda.sambox.filter.FilterFactory; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class represents a stream object in a PDF document. * * @author Ben Litchfield */ public class COSStream extends COSDictionary implements Closeable, Encryptable { private static final List CAN_COMPRESS = Arrays.asList(COSName.ASCII_HEX_DECODE, COSName.ASCII_HEX_DECODE_ABBREVIATION, COSName.ASCII85_DECODE, COSName.ASCII85_DECODE_ABBREVIATION); private static final Logger LOG = LoggerFactory.getLogger(COSStream.class); private LazySeekableSourceViewHolder existing; private byte[] filtered; private byte[] unfiltered; private DecodeResult decodeResult; // an encryption function that returns an encrypted view of the filtered stream private Function encryptor; private boolean encryptable = true; // if the writer should write the stream length as indirect object private boolean indirectLength = false; public COSStream() { } /** * @param dictionary The dictionary that is associated with this stream. */ public COSStream(COSDictionary dictionary) { super(dictionary); } /** * Creates a stream with the given dictionary and where filtered data is a view of the given {@link SeekableSource}. * * @param dictionary The dictionary that is associated with this stream. * @param seekableSource the source where filtered data is read from * @param startingPosition starting position of the stream data in the {@link SeekableSource} * @param length the length of the stream data */ public COSStream(COSDictionary dictionary, SeekableSource seekableSource, long startingPosition, long length) { super(dictionary); this.existing = new LazySeekableSourceViewHolder(seekableSource, startingPosition, length); } /** * @return the (encoded) stream with all of the filters applied. * @throws IOException when encoding/decoding causes an exception */ public final InputStream getFilteredStream() throws IOException { if (nonNull(encryptor)) { return encryptor.apply(doGetFilteredStream()); } return doGetFilteredStream(); } protected InputStream doGetFilteredStream() throws IOException { if (nonNull(existing)) { return existing.get().asInputStream(); } encodeIfRequired(); if (nonNull(filtered)) { return new MyByteArrayInputStream(filtered); } return new MyByteArrayInputStream(unfiltered); } /** * @return the (encoded) {@link SeekableSource} with all of the filters applied. * @throws IOException when encoding/decoding causes an exception */ public SeekableSource getFilteredSource() throws IOException { if (existing != null) { return existing.get(); } return inMemorySeekableSourceFrom(getFilteredStream()); } /** * @return the length of the encoded stream as long * @throws IOException */ public long getFilteredLength() throws IOException { if (existing != null) { return existing.length; } encodeIfRequired(); if (nonNull(filtered)) { return filtered.length; } return ofNullable(unfiltered).map(f -> f.length).orElse(0); } private void encodeIfRequired() throws IOException { if (getFilters() != null) { if (filtered == null && unfiltered != null) { doEncode(); } } } /** * @return the (decoded) stream with all of the filters applied. * @throws IOException when encoding/decoding causes an exception */ public InputStream getUnfilteredStream() throws IOException { decodeIfRequired(); if (unfiltered != null) { return new MyByteArrayInputStream(unfiltered); } return getStreamToDecode(); } /** * @return the (decoded) {@link SeekableSource} with all of the filters applied. * @throws IOException when encoding/decoding causes an exception */ public SeekableSource getUnfilteredSource() throws IOException { decodeIfRequired(); if (unfiltered != null) { return inMemorySeekableSourceFrom(unfiltered); } if (existing != null) { return existing.get(); } return inMemorySeekableSourceFrom(filtered); } /** * @return the (decoded) stream in the form of a read only {@link ByteBuffer} with all of the filters applied. * @throws IOException when encoding/decoding causes an exception */ public ByteBuffer getUnfilteredByteBuffer() throws IOException { decodeIfRequired(); if (unfiltered != null) { return ByteBuffer.wrap(unfiltered).asReadOnlyBuffer(); } if (existing != null) { return ByteBuffer.wrap(IOUtils.toByteArray(existing.get().asInputStream())); } return ByteBuffer.wrap(filtered).asReadOnlyBuffer(); } /** * @return the length of the decoded stream as long * @throws IOException */ public long getUnfilteredLength() throws IOException { decodeIfRequired(); if (nonNull(unfiltered)) { return unfiltered.length; } if (nonNull(existing)) { return existing.length; } return ofNullable(filtered).map(f -> f.length).orElse(0); } private void decodeIfRequired() throws IOException { if (nonNull(getFilters()) && isNull(unfiltered)) { doDecode(); } } /** * @return the repaired stream parameters dictionary * @throws IOException when encoding/decoding causes an exception */ public DecodeResult getDecodeResult() throws IOException { decodeIfRequired(); return Optional.ofNullable(decodeResult).orElse(DecodeResult.DEFAULT); } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } /** * This will decode the physical byte stream applying all of the filters to the stream. * * @throws IOException If there is an error applying a filter to the stream. */ private void doDecode() throws IOException { COSBase filters = getFilters(); if (nonNull(filters)) { if (filters instanceof COSName) { unfiltered = decode((COSName) filters, 0, getStreamToDecode()); } else if (filters instanceof COSArray) { unfiltered = decodeChain((COSArray) filters, getStreamToDecode()); } else { throw new IOException("Unknown filter type:" + filters); } } } private InputStream getStreamToDecode() throws IOException { if (existing != null) { return existing.get().asInputStream(); } return new MyByteArrayInputStream(filtered); } private byte[] decodeChain(COSArray filters, InputStream startingFrom) throws IOException { if (filters.size() > 0) { byte[] tmpResult = new byte[0]; InputStream input = startingFrom; for (int i = 0; i < filters.size(); i++) { COSName filterName = (COSName) filters.getObject(i); tmpResult = decode(filterName, i, input); input = new MyByteArrayInputStream(tmpResult); } return tmpResult; } return null; } private byte[] decode(COSName filterName, int filterIndex, InputStream toDecode) throws IOException { if (toDecode.available() > 0) { Filter filter = FilterFactory.INSTANCE.getFilter(filterName); try (MyByteArrayOutputStream out = new MyByteArrayOutputStream()) { decodeResult = filter.decode(toDecode, out, this, filterIndex); return out.toByteArray(); } } return new byte[0]; } /** * This will encode the logical byte stream applying all of the filters to the stream. * * @throws IOException If there is an error applying a filter to the stream. */ private void doEncode() throws IOException { COSBase filters = getFilters(); if (filters instanceof COSName) { filtered = encode((COSName) filters, new MyByteArrayInputStream(unfiltered)); } else if (filters instanceof COSArray) { filtered = encodeChain((COSArray) filters, new MyByteArrayInputStream(unfiltered)); } } private byte[] encode(COSName filterName, InputStream toEncode) throws IOException { Filter filter = FilterFactory.INSTANCE.getFilter(filterName); try (MyByteArrayOutputStream encoded = new MyByteArrayOutputStream()) { filter.encode(toEncode, encoded, this); return encoded.toByteArray(); } } private byte[] encodeChain(COSArray filters, InputStream startingFrom) throws IOException { if (filters.size() > 0) { byte[] tmpResult = new byte[0]; InputStream input = startingFrom; for (int i = filters.size() - 1; i >= 0; i--) { COSName filterName = (COSName) filters.getObject(i); tmpResult = encode(filterName, input); input = new MyByteArrayInputStream(tmpResult); } return tmpResult; } return null; } /** * This will return the filters to apply to the byte stream. The method will return - null if no filters are to be * applied - a COSName if one filter is to be applied - a COSArray containing COSNames if multiple filters are to be * applied * * @return the COSBase object representing the filters */ public COSBase getFilters() { return getDictionaryObject(COSName.FILTER); } /** * @param filter * @return true if the stream has the given filter in the filters list */ public boolean hasFilter(COSName filter) { COSBase filters = getFilters(); if (filters instanceof COSName) { return filters.equals(filter); } else if (filters instanceof COSArray) { return ((COSArray) filters).contains(filter); } return false; } /** * Sets the function to be used to encrypt this stream. * * @param encrypted */ public void setEncryptor(Function encryptor) { this.encryptor = encryptor; } /** * Creates a new stream for which filtered byte should be written to. You probably don't want this but want to use * the createUnfilteredStream, which is used to write raw bytes to. * * @return A stream that can be written to. */ public OutputStream createFilteredStream() { IOUtils.closeQuietly(existing); unfiltered = null; existing = null; filtered = null; return new MyByteArrayOutputStream(bytes -> { this.filtered = bytes; }); } /** * Returns a new OutputStream for writing stream data, using and the given filters. * * @param filters COSArray or COSName of filters to be used. * @return OutputStream for un-encoded stream data. * @throws IOException If the output stream could not be created. */ public OutputStream createFilteredStream(COSBase filters) { if (filters != null) { setItem(COSName.FILTER, filters); } return createUnfilteredStream(); } /** * set the filters to be applied to the stream. * * @param filters The filters to set on this stream. * @throws IOException If there is an error clearing the old filters. */ public void setFilters(COSBase filters) throws IOException { if (unfiltered == null) { try (InputStream in = getUnfilteredStream()) { try (MyByteArrayOutputStream out = new MyByteArrayOutputStream(bytes -> { this.unfiltered = bytes; })) { org.apache.commons.io.IOUtils.copy(in, out); } } } setItem(COSName.FILTER, filters); IOUtils.closeQuietly(existing); existing = null; filtered = null; } /** * Adds Flate decode filter to the current filters list if possible */ public void addCompression() throws IOException { if (canCompress()) { COSArray newFilters = new COSArray(COSName.FLATE_DECODE); COSBase filters = getFilters(); if (filters instanceof COSName) { newFilters.add(filters); setFilters(newFilters); } else if (filters instanceof COSArray) { newFilters.addAll((COSArray) filters); setFilters(newFilters); } else { setFilters(COSName.FLATE_DECODE); } } } /** * @return true if we can add compression to the current filters */ private boolean canCompress() { if (getDictionaryObject(COSName.DECODE_PARMS, COSName.DP) != null) { // we currently don't compress if there's a filter with params. return false; } COSBase filters = getFilters(); if (filters instanceof COSName) { return CAN_COMPRESS.contains(filters); } if (filters instanceof COSArray) { return ((COSArray) filters).stream().allMatch(CAN_COMPRESS::contains); } return true; } @Override public boolean encryptable() { return encryptable; } @Override public void encryptable(boolean encryptable) { this.encryptable = encryptable; } /** * This will create an output stream that can be written to. * * @return An output stream which raw data bytes should be written to. * * @throws IOException If there is an error creating the stream. */ public OutputStream createUnfilteredStream() { filtered = null; IOUtils.closeQuietly(existing); existing = null; unfiltered = null; return new MyByteArrayOutputStream(bytes -> { this.unfiltered = bytes; }); } public boolean isEmpty() throws IOException { if (nonNull(existing)) { return existing.get().size() <= 0; } return ofNullable(filtered).map(f -> (f.length <= 0)).orElseGet(() -> { return ofNullable(unfiltered).map(u -> (u.length <= 0)).orElse(true); }); } /** * @return the contents of the stream as a text string. Text string as defined in Chap 7.9 of PDF 32000-1:2008. */ public String asTextString() { try (FastByteArrayOutputStream out = new FastByteArrayOutputStream()) { org.apache.commons.io.IOUtils.copy(getUnfilteredStream(), out); return COSString.newInstance(out.toByteArray()).getString(); } catch (IOException e) { LOG.warn("Unable to convert the COSStream to a text string", e); return ""; } } @Override public void close() throws IOException { IOUtils.closeQuietly(existing); existing = null; unfiltered = null; filtered = null; } /** * offload decoded/unfiltered data leaving the COSStrem in its filtered state and reducing memory footprint */ public void unDecode() { if (nonNull(existing)) { unfiltered = null; filtered = null; } if (nonNull(filtered)) { unfiltered = null; } } /** * @return true if the writer should write this stream length as indirect */ public boolean indirectLength() { return indirectLength; } /** * @param indirectLength if the writer should write this stream length as indirect */ public void indirectLength(boolean indirectLength) { this.indirectLength = indirectLength; } static class MyByteArrayOutputStream extends FastByteArrayOutputStream { private Optional> onClose; MyByteArrayOutputStream() { this(null); } MyByteArrayOutputStream(Consumer onClose) { super(); this.onClose = Optional.ofNullable(onClose); } @Override public void close() { super.close(); onClose.ifPresent(c -> c.accept(toByteArray())); } } static class MyByteArrayInputStream extends ByteArrayInputStream { MyByteArrayInputStream(byte[] bytes) { super(Optional.ofNullable(bytes).orElse(new byte[0])); } } /** * Holder for a view of a portion of the given {@link SeekableSource} * * @author Andrea Vacondio */ private static class LazySeekableSourceViewHolder implements Closeable { private WeakReference sourceRef; private long length; private SeekableSourceSupplier supplier; private SeekableSource view; public LazySeekableSourceViewHolder(SeekableSource source, long startingPosition, long length) { this.supplier = () -> { return Optional.ofNullable(this.sourceRef.get()).filter(SeekableSource::isOpen) .orElseThrow(() -> new IllegalStateException( "The original SeekableSource has been closed")) .view(startingPosition, length); }; this.sourceRef = new WeakReference<>(source); this.length = length; } SeekableSource get() throws IOException { if (view == null) { view = supplier.get(); } view.position(0); return view; } @Override public void close() throws IOException { IOUtils.close(view); view = null; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSString.java000066400000000000000000000170551320103431700241420ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Optional; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.sambox.util.Hex; /** * A string object, which may be a text string, a PDFDocEncoded string, ASCII string, or byte string. * *

* Text strings are used for character strings that contain information intended to be human-readable, such as text * annotations, bookmark names, article names, document information, and so forth. * *

* PDFDocEncoded strings are used for characters that are represented in a single byte. * *

* ASCII strings are used for characters that are represented in a single byte using ASCII encoding. * *

* Byte strings are used for binary data represented as a series of bytes, but the encoding is not known. The bytes of * the string need not represent characters. * * @author Ben Litchfield * @author John Hewson */ public final class COSString extends COSBase implements Encryptable { private byte[] bytes; private boolean forceHexForm; private boolean encryptable = true; /** * Creates a new PDF string from a byte array. This method can be used to read a string from an existing PDF file, * or to create a new byte string. * * @param bytes The raw bytes of the PDF text string or byte string. */ public COSString(byte[] bytes) { this.bytes = bytes; } /** * Sets the raw value of this string. * * @param value The raw bytes of the PDF text string or byte string. */ public void setValue(byte[] value) { this.bytes = Arrays.copyOf(value, value.length); } /** * Sets whether or not to force the string is to be written in hex form. This is needed when signing PDF files. * * @param value True to force hex. */ public void setForceHexForm(boolean value) { this.forceHexForm = value; } /** * @return true if the string is to be written in hex form. */ public boolean isForceHexForm() { return forceHexForm || !isAscii(); } private boolean isAscii() { for (byte b : bytes) { // if the byte is negative then it is an eight bit byte and is outside the ASCII range // PDFBOX-3107 EOL markers within a string are troublesome if (b < 0 || b == 0x0d || b == 0x0a) { return false; } } return true; } /** * @return the content PDF text string as defined in Chap 7.9 of PDF 32000-1:2008. */ public String getString() { // text string - BOM indicates Unicode if (bytes.length >= 2) { if ((bytes[0] & 0xff) == 0xFE && (bytes[1] & 0xff) == 0xFF) { // UTF-16BE return new String(bytes, 2, bytes.length - 2, StandardCharsets.UTF_16BE); } else if ((bytes[0] & 0xff) == 0xFF && (bytes[1] & 0xff) == 0xFE) { // UTF-16LE - not in the PDF spec! return new String(bytes, 2, bytes.length - 2, StandardCharsets.UTF_16LE); } } // otherwise use PDFDocEncoding return PDFDocEncoding.toString(bytes); } /** * @return the raw bytes of the string. Best used with a PDF byte string. */ public byte[] getBytes() { return bytes; } /** * @return A hex string representing the bytes in this string. */ public String toHexString() { return Hex.getString(bytes); } @Override public boolean encryptable() { return encryptable; } @Override public void encryptable(boolean encryptable) { this.encryptable = encryptable; } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } @Override public boolean equals(Object obj) { if (obj instanceof COSString) { COSString strObj = (COSString) obj; return getString().equals(strObj.getString()) && forceHexForm == strObj.forceHexForm; } return false; } @Override public int hashCode() { return Arrays.hashCode(bytes) + (forceHexForm ? 17 : 0); } @Override public String toString() { return "COSString{" + getString() + "}"; } /** * Factory method for a {@link COSString} from a byte array * * @param value * @return a new instance */ public static COSString newInstance(byte[] value) { return new COSString(value); } /** * Factory method creating a {@link COSString} from a literal string. * * @param literal A literal string. * @return A {@link COSString} encoded with {@link PDFDocEncoding} encoding if possible, with * {@link Charsets#UTF_16BE} otherwise. * @throws IOException If there is an error with the hex string. */ public static COSString parseLiteral(String literal) { return Optional.ofNullable(PDFDocEncoding.getBytes(literal)).map(COSString::new) .orElseGet(() -> { byte[] data = literal.getBytes(StandardCharsets.UTF_16BE); byte[] bytes = new byte[data.length + 2]; bytes[0] = (byte) 0xFE; bytes[1] = (byte) 0xFF; System.arraycopy(data, 0, bytes, 2, data.length); return new COSString(bytes); }); } /** * Factory method creating a {@link COSString} from a string of hex characters. * * @param hex A hex string. * @return A cos string with the hex characters converted to their actual bytes. * @throws IOException If there is an error with the hex string. */ public static COSString parseHex(String hex) throws IOException { StringBuilder hexBuffer = new StringBuilder(hex.trim()); // if odd number then the last hex digit is assumed to be 0 if (hexBuffer.length() % 2 != 0) { hexBuffer.append('0'); } try (FastByteArrayOutputStream bytes = new FastByteArrayOutputStream()) { for (int i = 0; i < hexBuffer.length(); i += 2) { try { bytes.write(Integer.parseInt(hexBuffer.substring(i, i + 2), 16)); } catch (NumberFormatException e) { throw new IOException("Invalid hex string: " + hex, e); } } COSString retVal = new COSString(bytes.toByteArray()); retVal.setForceHexForm(true); return retVal; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/COSVisitor.java000066400000000000000000000037331320103431700243310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; /** * Visitor interface to visit COS objects. * * @author Andrea Vacondio * */ public interface COSVisitor { default void visit(COSDocument value) throws IOException { // nothings } default void visit(COSArray value) throws IOException { // nothings } default void visit(COSBoolean value) throws IOException { // nothings } default void visit(COSDictionary value) throws IOException { // nothings } default void visit(COSFloat value) throws IOException { // nothings } default void visit(COSInteger value) throws IOException { // nothings } default void visit(COSName value) throws IOException { // nothings } default void visit(COSNull value) throws IOException { // nothings } default void visit(COSStream value) throws IOException { // nothings } default void visit(COSString value) throws IOException { // nothings } default void visit(IndirectCOSObjectReference value) throws IOException { // nothings } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/DirectCOSObject.java000066400000000000000000000032641320103431700252320ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; import java.util.Optional; /** * A COSBase that will be written as direct object * * @author Andrea Vacondio */ public final class DirectCOSObject extends COSBase { private COSBase baseObject; private DirectCOSObject(COSBase wrapped) { this.baseObject = wrapped; } @Override public COSBase getCOSObject() { return baseObject; } @Override public void accept(COSVisitor visitor) throws IOException { baseObject.accept(visitor); } /** * Factory method for an object that will be written as a direct object. * * @param wrapped * @return the new instance */ public static DirectCOSObject asDirectObject(COSObjectable wrapped) { return new DirectCOSObject(Optional.ofNullable(wrapped).orElse(COSNull.NULL).getCOSObject()); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/DisposableCOSObject.java000066400000000000000000000024561320103431700261070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; /** * A {@link COSObjectable} that can be disposed once no longer need. What "dispose" actually means depends on the * implementation, the idea is to be able to signal that an object is no longer needed (EX. because it has already been * written to the output) and can be freed. * * @author Andrea Vacondio * */ public interface DisposableCOSObject extends COSObjectable { /** * release the object wrapped by this {@link COSObjectable} */ void releaseCOSObject(); } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/Encryptable.java000066400000000000000000000022061320103431700245670ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; /** * An encryptable object * * @author Andrea Vacondio * */ public interface Encryptable { /** * @return true if the can be encrypted */ boolean encryptable(); /** * Sets if the object can be encrypted * * @param encryptable */ void encryptable(boolean encryptable); } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/IndirectCOSObjectIdentifier.java000066400000000000000000000045321320103431700275630ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import static org.sejda.util.RequireUtils.requireNotBlank; import static org.sejda.util.RequireUtils.requireNotNullArg; /** * Represent and indirect object identifier (as defined by chap. 7.3.10 of PDF 32000-1:2008 spec) with an additional * information to identify the document this object belongs to. * * @author Andrea Vacondio */ public final class IndirectCOSObjectIdentifier { public final COSObjectKey objectIdentifier; public final String ownerIdentifier; public IndirectCOSObjectIdentifier(COSObjectKey objectIdentifier, String ownerIdentifier) { requireNotNullArg(objectIdentifier, "Object identifier cannot be null"); requireNotBlank(ownerIdentifier, "Owning document identifier cannot be blank"); this.objectIdentifier = objectIdentifier; this.ownerIdentifier = ownerIdentifier; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof IndirectCOSObjectIdentifier)) { return false; } IndirectCOSObjectIdentifier other = (IndirectCOSObjectIdentifier) obj; return (objectIdentifier.equals(other.objectIdentifier) && ownerIdentifier.equals(other.ownerIdentifier)); } @Override public int hashCode() { return Long.hashCode(objectIdentifier.hashCode() + ownerIdentifier.hashCode()); } @Override public String toString() { return objectIdentifier + " " + ownerIdentifier; } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/IndirectCOSObjectReference.java000066400000000000000000000044551320103431700274030ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.IOException; import java.util.Optional; import org.sejda.sambox.xref.XrefEntry; /** * A disposable indirect object reference. It holds all the necessary information to write a {@link COSBase} as an * indirect reference. * * @author Andrea Vacondio */ public class IndirectCOSObjectReference extends COSBase implements DisposableCOSObject { private COSBase baseObject; private XrefEntry xrefEntry; public IndirectCOSObjectReference(long objectNumber, int generationNumber, COSBase baseObject) { this.xrefEntry = XrefEntry.unknownOffsetEntry(objectNumber, generationNumber); this.baseObject = baseObject; } @Override public void accept(COSVisitor visitor) throws IOException { visitor.visit(this); } public XrefEntry xrefEntry() { return xrefEntry; } public void setValue(COSBase baseObject) { this.baseObject = baseObject; } @Override public COSBase getCOSObject() { return Optional.ofNullable(baseObject).orElse(COSNull.NULL); } @Override public void releaseCOSObject() { if (baseObject instanceof DisposableCOSObject) { ((DisposableCOSObject) baseObject).releaseCOSObject(); } baseObject = null; } @Override public String toString() { return Long.toString(xrefEntry().key().objectNumber()) + " " + Integer.toString(xrefEntry().key().generation()) + " R"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/NonStorableInObjectStreams.java000066400000000000000000000022671320103431700275310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; /** * An {@link IndirectCOSObjectReference} that cannot be stored into object streams * * @author Andrea Vacondio */ public class NonStorableInObjectStreams extends IndirectCOSObjectReference { public NonStorableInObjectStreams(long objectNumber, int generationNumber, COSBase baseObject) { super(objectNumber, generationNumber, baseObject); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/PDFDocEncoding.java000066400000000000000000000125761320103431700250400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.io.ByteArrayOutputStream; import java.util.HashMap; import java.util.Map; /** * The "PDFDocEncoding" encoding. Note that this is *not* a Type 1 font encoding, it is used only within PDF * "text strings". */ final class PDFDocEncoding { private static final char REPLACEMENT_CHARACTER = '\uFFFD'; private static final int[] CODE_TO_UNI; private static final Map UNI_TO_CODE; static { CODE_TO_UNI = new int[256]; UNI_TO_CODE = new HashMap<>(256); // initialize with basically ISO-8859-1 for (int i = 0; i < 256; i++) { // skip entries not in Unicode column if (i > 0x17 && i < 0x20) { continue; } if (i > 0x7E && i < 0xA1) { continue; } if (i == 0xAD) { continue; } set(i, (char) i); } // then do all deviations (based on the table in ISO 32000-1:2008) // block 1 set(0x18, '\u02D8'); // BREVE set(0x19, '\u02C7'); // CARON set(0x1A, '\u02C6'); // MODIFIER LETTER CIRCUMFLEX ACCENT set(0x1B, '\u02D9'); // DOT ABOVE set(0x1C, '\u02DD'); // DOUBLE ACUTE ACCENT set(0x1D, '\u02DB'); // OGONEK set(0x1E, '\u02DA'); // RING ABOVE set(0x1F, '\u02DC'); // SMALL TILDE // block 2 set(0x7F, REPLACEMENT_CHARACTER); // undefined set(0x80, '\u2022'); // BULLET set(0x81, '\u2020'); // DAGGER set(0x82, '\u2021'); // DOUBLE DAGGER set(0x83, '\u2026'); // HORIZONTAL ELLIPSIS set(0x84, '\u2014'); // EM DASH set(0x85, '\u2013'); // EN DASH set(0x86, '\u0192'); // LATIN SMALL LETTER SCRIPT F set(0x87, '\u2044'); // FRACTION SLASH (solidus) set(0x88, '\u2039'); // SINGLE LEFT-POINTING ANGLE QUOTATION MARK set(0x89, '\u203A'); // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK set(0x8A, '\u2212'); // MINUS SIGN set(0x8B, '\u2030'); // PER MILLE SIGN set(0x8C, '\u201E'); // DOUBLE LOW-9 QUOTATION MARK (quotedblbase) set(0x8D, '\u201C'); // LEFT DOUBLE QUOTATION MARK (quotedblleft) set(0x8E, '\u201D'); // RIGHT DOUBLE QUOTATION MARK (quotedblright) set(0x8F, '\u2018'); // LEFT SINGLE QUOTATION MARK (quoteleft) set(0x90, '\u2019'); // RIGHT SINGLE QUOTATION MARK (quoteright) set(0x91, '\u201A'); // SINGLE LOW-9 QUOTATION MARK (quotesinglbase) set(0x92, '\u2122'); // TRADE MARK SIGN set(0x93, '\uFB01'); // LATIN SMALL LIGATURE FI set(0x94, '\uFB02'); // LATIN SMALL LIGATURE FL set(0x95, '\u0141'); // LATIN CAPITAL LETTER L WITH STROKE set(0x96, '\u0152'); // LATIN CAPITAL LIGATURE OE set(0x97, '\u0160'); // LATIN CAPITAL LETTER S WITH CARON set(0x98, '\u0178'); // LATIN CAPITAL LETTER Y WITH DIAERESIS set(0x99, '\u017D'); // LATIN CAPITAL LETTER Z WITH CARON set(0x9A, '\u0131'); // LATIN SMALL LETTER DOTLESS I set(0x9B, '\u0142'); // LATIN SMALL LETTER L WITH STROKE set(0x9C, '\u0153'); // LATIN SMALL LIGATURE OE set(0x9D, '\u0161'); // LATIN SMALL LETTER S WITH CARON set(0x9E, '\u017E'); // LATIN SMALL LETTER Z WITH CARON set(0x9F, REPLACEMENT_CHARACTER); // undefined set(0xA0, '\u20AC'); // EURO SIGN // end of deviations } private PDFDocEncoding() { } private static void set(int code, char unicode) { CODE_TO_UNI[code] = unicode; UNI_TO_CODE.put(unicode, code); } /** * @return the string representation of the given PDFDocEncoded bytes. */ public static String toString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { if ((b & 0xff) >= CODE_TO_UNI.length) { sb.append('?'); } else { sb.append((char) CODE_TO_UNI[b & 0xff]); } } return sb.toString(); } /** * @return the given string encoded with PDFDocEncoding or null if the string cannot be encoded */ public static byte[] getBytes(String text) { ByteArrayOutputStream out = new ByteArrayOutputStream(); for (char c : text.toCharArray()) { Integer code = UNI_TO_CODE.get(c); if (code == null) { return null; } out.write(code); } return out.toByteArray(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/cos/UnmodifiableCOSDictionary.java000066400000000000000000000115171320103431700273150ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.cos; import java.util.Calendar; /** * An unmodifiable COSDictionary. * * @author John Hewson */ final class UnmodifiableCOSDictionary extends COSDictionary { UnmodifiableCOSDictionary(COSDictionary dict) { super(dict); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public void setItem(COSName key, COSBase value) { throw new UnsupportedOperationException(); } @Override public void setItem(COSName key, COSObjectable value) { throw new UnsupportedOperationException(); } @Override public void setItem(String key, COSObjectable value) { throw new UnsupportedOperationException(); } @Override public void setBoolean(String key, boolean value) { throw new UnsupportedOperationException(); } @Override public void setBoolean(COSName key, boolean value) { throw new UnsupportedOperationException(); } @Override public void setItem(String key, COSBase value) { throw new UnsupportedOperationException(); } @Override public void setName(String key, String value) { throw new UnsupportedOperationException(); } @Override public void setName(COSName key, String value) { throw new UnsupportedOperationException(); } @Override public void setDate(String key, Calendar date) { throw new UnsupportedOperationException(); } @Override public void setDate(COSName key, Calendar date) { throw new UnsupportedOperationException(); } @Override public void setEmbeddedDate(String embedded, String key, Calendar date) { throw new UnsupportedOperationException(); } @Override public void setEmbeddedDate(String embedded, COSName key, Calendar date) { throw new UnsupportedOperationException(); } @Override public void setString(String key, String value) { throw new UnsupportedOperationException(); } @Override public void setString(COSName key, String value) { throw new UnsupportedOperationException(); } @Override public void setEmbeddedString(String embedded, String key, String value) { throw new UnsupportedOperationException(); } @Override public void setEmbeddedString(String embedded, COSName key, String value) { throw new UnsupportedOperationException(); } @Override public void setInt(String key, int value) { throw new UnsupportedOperationException(); } @Override public void setInt(COSName key, int value) { throw new UnsupportedOperationException(); } @Override public void setLong(String key, long value) { throw new UnsupportedOperationException(); } @Override public void setLong(COSName key, long value) { throw new UnsupportedOperationException(); } @Override public void setEmbeddedInt(String embeddedDictionary, String key, long value) { throw new UnsupportedOperationException(); } @Override public void setEmbeddedInt(String embeddedDictionary, COSName key, long value) { throw new UnsupportedOperationException(); } @Override public void setFloat(String key, float value) { throw new UnsupportedOperationException(); } @Override public void setFloat(COSName key, float value) { throw new UnsupportedOperationException(); } @Override public void removeItem(COSName key) { throw new UnsupportedOperationException(); } @Override public void addAll(COSDictionary dic) { throw new UnsupportedOperationException(); } @Override public void mergeWithoutOverwriting(COSDictionary dic) { throw new UnsupportedOperationException(); } @Override public void merge(COSDictionary dic) { throw new UnsupportedOperationException(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/000077500000000000000000000000001320103431700230625ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/AESEncryptionAlgorithmEngine.java000066400000000000000000000027031320103431700314070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import java.io.InputStream; /** * Encryption algorithm with AES engine * * @author Andrea Vacondio * */ interface AESEncryptionAlgorithmEngine extends EncryptionAlgorithmEngine { /** * @param data * @param key * @param iv * @return a stream encrypted with the given key and initialization vector */ InputStream encryptStream(InputStream data, byte[] key, byte[] iv); /** * * @param data * @param key * @param iv * @return the byte array encrypted with the given key and initialization vector */ byte[] encryptBytes(byte[] data, byte[] key, byte[] iv); } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/AESEngineNoPadding.java000066400000000000000000000067211320103431700272550ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static java.util.Objects.nonNull; import static org.bouncycastle.util.Arrays.copyOf; import java.io.InputStream; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.io.CipherInputStream; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; /** * AES implementation of a {@link EncryptionAlgorithmEngine} with no pudding * * @author Andrea Vacondio * */ class AESEngineNoPadding implements AESEncryptionAlgorithmEngine { private BufferedBlockCipher cipher; AESEngineNoPadding(BufferedBlockCipher cipher) { this.cipher = cipher; } @Override public InputStream encryptStream(InputStream data, byte[] key, byte[] iv) { init(key, iv); return new CipherInputStream(data, cipher); } @Override public InputStream encryptStream(InputStream data, byte[] key) { return encryptStream(data, key, null); } @Override public byte[] encryptBytes(byte[] data, byte[] key, byte[] iv) { init(key, iv); try { byte[] buf = new byte[cipher.getOutputSize(data.length)]; int len = cipher.processBytes(data, 0, data.length, buf, 0); len += cipher.doFinal(buf, len); return copyOf(buf, len); } catch (DataLengthException | IllegalStateException | InvalidCipherTextException e) { throw new EncryptionException(e); } } @Override public byte[] encryptBytes(byte[] data, byte[] key) { return encryptBytes(data, key, null); } private void init(byte[] key, byte[] iv) { cipher.reset(); if (nonNull(iv)) { cipher.init(true, new ParametersWithIV(new KeyParameter(key), iv)); } else { cipher.init(true, new KeyParameter(key)); } } /** * @return and instance of EncryptionAlgorithmEngine AES/CBC/NoPadding and no initialization vector */ static AESEngineNoPadding cbc() { return new AESEngineNoPadding( new BufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()))); } /** * @return and instance of EncryptionAlgorithmEngine AES/ECB/NoPadding and no initialization vector */ static AESEngineNoPadding ecb() { return new AESEngineNoPadding(new BufferedBlockCipher(new AESFastEngine())); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/ARC4Engine.java000066400000000000000000000034411320103431700255460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import java.io.InputStream; import org.bouncycastle.crypto.StreamCipher; import org.bouncycastle.crypto.engines.RC4Engine; import org.bouncycastle.crypto.io.CipherInputStream; import org.bouncycastle.crypto.params.KeyParameter; /** * ARC4 implementation of a {@link EncryptionAlgorithmEngine} * * @author Andrea Vacondio * */ class ARC4Engine implements EncryptionAlgorithmEngine { private StreamCipher cipher; public ARC4Engine() { cipher = new RC4Engine(); } @Override public InputStream encryptStream(InputStream data, byte[] key) { init(key); return new CipherInputStream(data, cipher); } @Override public byte[] encryptBytes(byte[] data, byte[] key) { init(key); byte[] out = new byte[data.length]; cipher.processBytes(data, 0, data.length, out, 0); return out; } private void init(byte[] key) { cipher.init(true, new KeyParameter(key)); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm1.java000066400000000000000000000113761320103431700257440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static java.util.Objects.isNull; import static org.bouncycastle.util.Arrays.concatenate; import static org.sejda.util.RequireUtils.requireArg; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.security.MessageDigest; import java.util.Arrays; import java.util.function.Function; import org.bouncycastle.crypto.engines.AESEngine; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; /** * Algorithm 1 defined in Chapter 7.6.2 (General Encryption Algorithm) PDF 32100-1:2008 * * @author Andrea Vacondio * */ class Algorithm1 implements GeneralEncryptionAlgorithm { private static final byte[] AES_SALT = { (byte) 0x73, (byte) 0x41, (byte) 0x6c, (byte) 0x54 }; private EncryptionAlgorithmEngine engine; private MessageDigest digest = MessageDigests.md5(); private Function keyCalculator; private Function md5Initializer; private Function md5ToKey; private COSObjectKey currentCOSObjectKey; private Algorithm1(EncryptionAlgorithmEngine engine, byte[] key) { requireNotNullArg(engine, "Encryption engine cannot be null"); requireArg(key != null && key.length > 0, "Encryption key cannot be blank"); this.engine = engine; keyCalculator = (cosKey) -> { requireNotNullArg(cosKey, "Cannot encrypt a reference with a null key"); byte[] append = new byte[5]; append[0] = (byte) (cosKey.objectNumber() & 0xff); append[1] = (byte) (cosKey.objectNumber() >> 8 & 0xff); append[2] = (byte) (cosKey.objectNumber() >> 16 & 0xff); append[3] = (byte) (cosKey.generation() & 0xff); append[4] = (byte) (cosKey.generation() >> 8 & 0xff); return concatenate(key, append); }; md5Initializer = (newKey) -> { digest.reset(); digest.update(newKey); return newKey; }; md5ToKey = (newKey) -> { return Arrays.copyOf(digest.digest(), Math.min(newKey.length, 16)); }; } @Override public void setCurrentCOSObjectKey(COSObjectKey currentCOSObjectKey) { this.currentCOSObjectKey = currentCOSObjectKey; } @Override public void visit(COSString value) { if (value.encryptable()) { requireObjectKey(); value.setValue(engine.encryptBytes(value.getBytes(), keyCalculator .andThen(md5Initializer).andThen(md5ToKey).apply(currentCOSObjectKey))); } } @Override public void visit(COSStream value) { if (value.encryptable()) { requireObjectKey(); value.setEncryptor((i) -> engine.encryptStream(i, keyCalculator.andThen(md5Initializer) .andThen(md5ToKey).apply(currentCOSObjectKey))); } } private void requireObjectKey() { if (isNull(currentCOSObjectKey)) { throw new EncryptionException( "General encryption algorithm 1 requires object number and generation number"); } } @Override public String toString() { return "Algorithm1 with engine " + engine; } /** * Factory method for an {@link Algorithm1} with an {@link AESEngine} * * @param key * @return */ static Algorithm1 withAESEngine(byte[] key) { Algorithm1 algorithm = new Algorithm1(new ConcatenatingAESEngine(), key); algorithm.md5Initializer = algorithm.md5Initializer.andThen(k -> { algorithm.digest.update(AES_SALT); return k; }); return algorithm; } /** * Factory method for an {@link Algorithm1} with an {@link ARC4Engine} * * @param key * @return */ static Algorithm1 withARC4Engine(byte[] key) { return new Algorithm1(new ARC4Engine(), key); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm10.java000066400000000000000000000034151320103431700260170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; /** * Algorithm 10 as defined in Chap 7.6.3.4.8 of ISO 32000-2 * * @author Andrea Vacondio */ class Algorithm10 { private AESEngineNoPadding engine = AESEngineNoPadding.ecb(); byte[] computePerms(EncryptionContext context) { byte[] perms = EncryptUtils.rnd(16); perms[0] = (byte) context.security.permissions.getPermissionBytes(); perms[1] = (byte) (context.security.permissions.getPermissionBytes() >>> 8); perms[2] = (byte) (context.security.permissions.getPermissionBytes() >>> 16); perms[3] = (byte) (context.security.permissions.getPermissionBytes() >>> 24); perms[4] = (byte) 0xFF; perms[5] = (byte) 0xFF; perms[6] = (byte) 0xFF; perms[7] = (byte) 0xFF; perms[8] = (byte) (context.security.encryptMetadata ? 'T' : 'F'); perms[9] = 'a'; perms[10] = 'd'; perms[11] = 'b'; return engine.encryptBytes(perms, context.key()); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm1A.java000066400000000000000000000045261320103431700260440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static org.sejda.util.RequireUtils.requireArg; import static org.sejda.util.RequireUtils.requireNotNullArg; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; /** * Algorithm 1 defined in Chapter 7.6.2 (General Encryption Algorithm) PDF 32100-1:2008 * * @author Andrea Vacondio * */ class Algorithm1A implements GeneralEncryptionAlgorithm { private AESEncryptionAlgorithmEngine engine = new ConcatenatingAESEngine(); private byte[] key; Algorithm1A(byte[] key, AESEncryptionAlgorithmEngine engine) { this(key); requireNotNullArg(engine, "Enecryption engine cannot be null"); this.engine = engine; } Algorithm1A(byte[] key) { requireNotNullArg(key, "Encryption key cannot be null"); requireArg(key.length == 32, "General encryption algorithm 1.A requires a 32 bytes key"); this.key = key; } @Override public void visit(COSString value) { if (value.encryptable()) { value.setValue(engine.encryptBytes(value.getBytes(), key)); } } @Override public void visit(COSStream value) { if (value.encryptable()) { value.setEncryptor((i) -> engine.encryptStream(i, key)); } } @Override public void setCurrentCOSObjectKey(COSObjectKey key) { // nothing } @Override public String toString() { return "Algorithm1A with engine " + engine; } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm2.java000066400000000000000000000046101320103431700257360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import java.security.MessageDigest; import java.util.Arrays; /** * Algorithm 2 as defined in Chap 7.6.3.3 of PDF 32000-1:2008 * * @author Andrea Vacondio * */ class Algorithm2 { private static final byte[] NO_METADATA = { (byte) 255, (byte) 255, (byte) 255, (byte) 255 }; private MessageDigest digest = MessageDigests.md5(); private Algorithm3 algo = new Algorithm3(); byte[] computeEncryptionKey(EncryptionContext context) { digest.reset(); digest.update(EncryptUtils.padOrTruncate(context.security.getUserPassword())); digest.update(algo.computePassword(context)); int permissions = context.security.permissions.getPermissionBytes(); digest.update((byte) permissions); digest.update((byte) (permissions >>> 8)); digest.update((byte) (permissions >>> 16)); digest.update((byte) (permissions >>> 24)); digest.update(context.documentId()); if (StandardSecurityHandlerRevision.R4.compareTo(context.security.encryption.revision) <= 0 && !context.security.encryptMetadata) { digest.update(NO_METADATA); } byte[] hash = digest.digest(); if (StandardSecurityHandlerRevision.R3.compareTo(context.security.encryption.revision) <= 0) { for (int i = 0; i < 50; i++) { digest.update(hash, 0, context.security.encryption.revision.length); hash = digest.digest(); } } return Arrays.copyOf(hash, context.security.encryption.revision.length); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm2AHash.java000066400000000000000000000020141320103431700266370ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; /** * The algorithm used to compute the hash to be used in Algorithm2A * * @author Andrea Vacondio * */ interface Algorithm2AHash { byte[] computeHash(byte[] input, byte[] password); } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm2B.java000066400000000000000000000050311320103431700260360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static org.bouncycastle.util.Arrays.concatenate; import static org.bouncycastle.util.Arrays.copyOf; import static org.bouncycastle.util.Arrays.copyOfRange; import java.math.BigInteger; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import java.util.Optional; /** * Algorithm 2B as defined in Chap 7.6.3.3.3 of PDF 32000-2 * * @author Andrea Vacondio * */ class Algorithm2B implements Algorithm2AHash { private static final BigInteger THREE = new BigInteger("3"); private static final Map HASHES = new HashMap<>(); static { HASHES.put(0, MessageDigests.sha256()); HASHES.put(1, MessageDigests.sha384()); HASHES.put(2, MessageDigests.sha512()); } private AESEncryptionAlgorithmEngine aes128 = AESEngineNoPadding.cbc(); private final byte[] u; Algorithm2B(byte[] u) { this.u = Optional.ofNullable(u).orElse(new byte[0]); } Algorithm2B() { this.u = new byte[0]; } @Override public byte[] computeHash(byte[] input, byte[] password) { byte[] k = copyOf(HASHES.get(0).digest(input), 32); byte[] e = new byte[0]; for (int round = 0; round < 64 || (e[e.length - 1] & 0xFF) > round - 32; round++) { byte[] k1Element = concatenate(password, k, u); byte[] k1 = new byte[0]; for (int i = 0; i < 64; i++) { k1 = concatenate(k1, k1Element); } e = aes128.encryptBytes(k1, copyOf(k, 16), copyOfRange(k, 16, 32)); k = HASHES.get(new BigInteger(1, copyOf(e, 16)).mod(THREE).intValue()).digest(e); } return copyOf(k, 32); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm2BExtensionLevel3.java000066400000000000000000000025351320103431700310140ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import java.security.MessageDigest; import org.bouncycastle.util.Arrays; /** * The algorithm used to compute hash in Algorithm 2.A for extension level 3. This is used with Version 5 and Revision 5 * of the security handler. * * @author Andrea Vacondio */ class Algorithm2BExtensionLevel3 implements Algorithm2AHash { private MessageDigest digest = MessageDigests.sha256(); @Override public byte[] computeHash(byte[] input, byte[] password) { return Arrays.copyOf(digest.digest(input), 32); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm3.java000066400000000000000000000053141320103431700257410ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static java.util.Optional.of; import static org.sejda.sambox.encryption.EncryptUtils.padOrTruncate; import java.security.MessageDigest; import org.bouncycastle.util.Arrays; /** * Algorithm 3 as defined in Chap 7.6.3.4 of PDF 32000-1:2008 * * @author Andrea Vacondio * */ class Algorithm3 implements PasswordAlgorithm { private MessageDigest digest = MessageDigests.md5(); private ARC4Engine engine = new ARC4Engine(); @Override public byte[] computePassword(EncryptionContext context) { byte[] ownerBytes = context.security.getOwnerPassword(); byte[] userBytes = context.security.getUserPassword(); byte[] padded = padOrTruncate( of(ownerBytes).filter(p -> p.length > 0).orElseGet(() -> userBytes)); byte[] paddedUser = padOrTruncate(userBytes); digest.reset(); byte[] arc4Key = digest.digest(padded); if (StandardSecurityHandlerRevision.R3.compareTo(context.security.encryption.revision) <= 0) { for (int i = 0; i < 50; ++i) { digest.update(arc4Key, 0, context.security.encryption.revision.length); arc4Key = Arrays.copyOf(digest.digest(), context.security.encryption.revision.length); } byte[] encrypted = engine.encryptBytes(paddedUser, arc4Key); byte[] iterationKey = new byte[arc4Key.length]; for (int i = 1; i < 20; i++) { iterationKey = Arrays.copyOf(arc4Key, arc4Key.length); for (int j = 0; j < iterationKey.length; j++) { iterationKey[j] = (byte) (iterationKey[j] ^ (byte) i); } encrypted = engine.encryptBytes(encrypted, iterationKey); } return encrypted; } return engine.encryptBytes(paddedUser, arc4Key); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm4.java000066400000000000000000000025421320103431700257420ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; /** * Algorithm 4 as defined in Chap 7.6.3.4 of PDF 32000-1:2008 * * @author Andrea Vacondio * */ class Algorithm4 implements PasswordAlgorithm { private ARC4Engine engine = new ARC4Engine(); @Override public byte[] computePassword(EncryptionContext context) { context.security.encryption.revision.require(StandardSecurityHandlerRevision.R2, "Algorithm 4 requires a security handler of revision 2"); return engine.encryptBytes(EncryptUtils.ENCRYPT_PADDING, context.key()); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm5.java000066400000000000000000000043051320103431700257420ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static org.sejda.sambox.encryption.EncryptUtils.ENCRYPT_PADDING; import java.security.MessageDigest; import org.bouncycastle.util.Arrays; /** * Algorithm 5 as defined in Chap 7.6.3.4 of PDF 32000-1:2008 * * @author Andrea Vacondio * */ class Algorithm5 implements PasswordAlgorithm { private MessageDigest digest = MessageDigests.md5(); private ARC4Engine engine = new ARC4Engine(); @Override public byte[] computePassword(EncryptionContext context) { context.security.encryption.revision.requireAtLeast(StandardSecurityHandlerRevision.R3, "Algorithm 5 requires a security handler of revision 3 or greater"); digest.reset(); digest.update(ENCRYPT_PADDING); byte[] encrypted = engine.encryptBytes( Arrays.copyOf(digest.digest(context.documentId()), 16), context.key()); byte[] iterationKey = new byte[context.key().length]; for (int i = 1; i < 20; i++) { iterationKey = Arrays.copyOf(context.key(), context.key().length); for (int j = 0; j < iterationKey.length; j++) { iterationKey[j] = (byte) (iterationKey[j] ^ (byte) i); } encrypted = engine.encryptBytes(encrypted, iterationKey); } return Arrays.concatenate(Arrays.copyOf(encrypted, 16), Arrays.copyOf(ENCRYPT_PADDING, 16)); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm8.java000066400000000000000000000052631320103431700257510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static java.util.Objects.requireNonNull; import static org.bouncycastle.util.Arrays.concatenate; import static org.bouncycastle.util.Arrays.copyOf; import static org.sejda.sambox.encryption.EncryptUtils.rnd; /** * Algorithm 8 as defined in Chap 7.6.3.4.6 of ISO 32000-2 * * @author Andrea Vacondio * */ class Algorithm8 implements PasswordAlgorithm { private byte[] userValidationSalt; private byte[] userKeySalt; private Algorithm2AHash hashAlgo; private AESEngineNoPadding engine = AESEngineNoPadding.cbc(); Algorithm8(Algorithm2AHash hashAlgo) { this(hashAlgo, rnd(8), rnd(8)); } Algorithm8(Algorithm2AHash hashAlgo, byte[] userValidationSalt, byte[] userKeySalt) { requireNonNull(hashAlgo); this.hashAlgo = hashAlgo; this.userValidationSalt = userValidationSalt; this.userKeySalt = userKeySalt; } @Override public byte[] computePassword(EncryptionContext context) { context.security.encryption.revision.requireAtLeast(StandardSecurityHandlerRevision.R5, "Algorithm 8 requires a security handler of revision 5 or greater"); byte[] userPassword = context.security.getUserPasswordUTF(); return concatenate( hashAlgo.computeHash(concatenate(userPassword, userValidationSalt), userPassword), userValidationSalt, userKeySalt); } public byte[] computeUE(EncryptionContext context) { context.security.encryption.revision.requireAtLeast(StandardSecurityHandlerRevision.R5, "Algorithm 8 requires a security handler of revision 5 or greater"); byte[] userPassword = context.security.getUserPasswordUTF(); byte[] key = hashAlgo.computeHash(concatenate(userPassword, userKeySalt), userPassword); return copyOf(engine.encryptBytes(context.key(), key), 32); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/Algorithm9.java000066400000000000000000000056401320103431700257510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static java.util.Objects.requireNonNull; import static org.bouncycastle.util.Arrays.concatenate; import static org.bouncycastle.util.Arrays.copyOf; import static org.sejda.sambox.encryption.EncryptUtils.rnd; import static org.sejda.util.RequireUtils.requireArg; /** * Algorithm 9 as defined in Chap 7.6.3.4.7 of ISO 32000-2 * * @author Andrea Vacondio */ public class Algorithm9 implements PasswordAlgorithm { private byte[] ownerValidationSalt; private byte[] ownerKeySalt; private byte[] u; private Algorithm2AHash hashAlgo; private AESEngineNoPadding engine = AESEngineNoPadding.cbc(); Algorithm9(Algorithm2AHash hashAlgo, byte[] u, byte[] ownerValidationSalt, byte[] ownerKeySalt) { requireNonNull(hashAlgo); requireNonNull(u); requireArg(u.length == 48, "Generated U string must be 48 bytes long"); this.hashAlgo = hashAlgo; this.u = u; this.ownerValidationSalt = ownerValidationSalt; this.ownerKeySalt = ownerKeySalt; } Algorithm9(Algorithm2AHash hashAlgo, byte[] u) { this(hashAlgo, u, rnd(8), rnd(8)); } @Override public byte[] computePassword(EncryptionContext context) { context.security.encryption.revision.requireAtLeast(StandardSecurityHandlerRevision.R5, "Algorithm 9 requires a security handler of revision 5 or greater"); byte[] ownerBytes = context.security.getOwnerPasswordUTF(); return concatenate( hashAlgo.computeHash(concatenate(ownerBytes, ownerValidationSalt, u), ownerBytes), ownerValidationSalt, ownerKeySalt); } public byte[] computeOE(EncryptionContext context) { context.security.encryption.revision.requireAtLeast(StandardSecurityHandlerRevision.R5, "Algorithm 8 requires a security handler of revision 5 or greater"); byte[] ownerBytes = context.security.getOwnerPasswordUTF(); byte[] key = hashAlgo.computeHash(concatenate(ownerBytes, ownerKeySalt, u), ownerBytes); return copyOf(engine.encryptBytes(context.key(), key), 32); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/ConcatenatingAESEngine.java000066400000000000000000000050271320103431700301650ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static org.bouncycastle.util.Arrays.concatenate; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.SequenceInputStream; import java.security.SecureRandom; import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; /** * AES implementation of a {@link EncryptionAlgorithmEngine} that concatenates the results to the initialization vectors * as required by Algorithm 1 and 1A. A random IV is created for those methods that don't take one as parameter * * @author Andrea Vacondio * */ public class ConcatenatingAESEngine extends AESEngineNoPadding { private SecureRandom random; ConcatenatingAESEngine() { super(new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()))); random = new SecureRandom(); } @Override public InputStream encryptStream(InputStream data, byte[] key) { return encryptStream(data, key, initializationVector()); } @Override public InputStream encryptStream(InputStream data, byte[] key, byte[] iv) { return new SequenceInputStream(new ByteArrayInputStream(iv), super.encryptStream(data, key, iv)); } @Override public byte[] encryptBytes(byte[] data, byte[] key) { return encryptBytes(data, key, initializationVector()); } @Override public byte[] encryptBytes(byte[] data, byte[] key, byte[] iv) { return concatenate(iv, super.encryptBytes(data, key, iv)); } private byte[] initializationVector() { byte[] iv = new byte[16]; random.nextBytes(iv); return iv; } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/EncryptUtils.java000066400000000000000000000053431320103431700263770ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static org.bouncycastle.util.Arrays.concatenate; import static org.bouncycastle.util.Arrays.copyOf; import static org.sejda.util.RequireUtils.requireArg; import java.security.SecureRandom; /** * Utilities for encryption related tasks * * @author Andrea Vacondio * */ final class EncryptUtils { public static final byte[] ENCRYPT_PADDING = { (byte) 0x28, (byte) 0xBF, (byte) 0x4E, (byte) 0x5E, (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41, (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56, (byte) 0xFF, (byte) 0xFA, (byte) 0x01, (byte) 0x08, (byte) 0x2E, (byte) 0x2E, (byte) 0x00, (byte) 0xB6, (byte) 0xD0, (byte) 0x68, (byte) 0x3E, (byte) 0x80, (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE, (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A }; private EncryptUtils() { // nothing } /** * Performs pad or truncate to a 32 bytes array as specified in Algo2 and Algo3 * * @param input * @return */ public static byte[] padOrTruncate(byte[] input) { byte[] padded = copyOf(input, Math.min(input.length, 32)); if (padded.length < 32) { return concatenate(padded, copyOf(ENCRYPT_PADDING, 32 - padded.length)); } return padded; } /** * @param input * @return the input array truncated to a max length of 127 */ public static byte[] truncate127(byte[] input) { return copyOf(input, Math.min(input.length, 127)); } /** * * @param length * @return an array of the given length with random byte values */ public static byte[] rnd(int length) { requireArg(length > 0, "Cannot generate a negative length byte array"); SecureRandom random = new SecureRandom(); byte[] rnd = new byte[length]; random.nextBytes(rnd); return rnd; } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/EncryptionAlgorithmEngine.java000066400000000000000000000027041320103431700310570ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import java.io.InputStream; /** * Encryption engine to use in general encryption algorithms as defined in Chapter 7.6.2 of the PDF 32000-1 * * @author Andrea Vacondio * */ interface EncryptionAlgorithmEngine { /** * @param data data to encrypt * @param key * @return a stream where encrypted data can be read from * @throws EncryptionException */ InputStream encryptStream(InputStream data, byte[] key); /** * @param data data to encrypt * @param key * @return the encrypted data * @throws EncryptionException */ byte[] encryptBytes(byte[] data, byte[] key); } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/EncryptionContext.java000066400000000000000000000110441320103431700274240ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import static org.sejda.util.RequireUtils.requireNotNullArg; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Context holding the current state of the encryption * * @author Andrea Vacondio * */ public final class EncryptionContext { private static final Logger LOG = LoggerFactory.getLogger(EncryptionContext.class); public final StandardSecurity security; private byte[] documentId; private byte[] key; public EncryptionContext(StandardSecurity security) { requireNotNullArg(security, "Cannot create an encryption context with a null security"); this.security = security; } /** * Sets the document ID to use in those algorithms requiring it. * * @param documentId */ public void documentId(byte[] documentId) { this.documentId = documentId; } byte[] documentId() { return documentId; } /** * Sets the encryption key * * @param key */ void key(byte[] key) { this.key = key; } byte[] key() { return key; } public GeneralEncryptionAlgorithm encryptionAlgorithm() { return security.encryption.encryptionAlgorithm(this); } /** * @param enc * @return a {@link GeneralEncryptionAlgorithm} generated using the given encryption algorithm or null if unable to * generate. */ public static GeneralEncryptionAlgorithm encryptionAlgorithmFromEncryptionDictionary( COSDictionary enc, byte[] encryptionKey) { if (nonNull(enc)) { if (nonNull(encryptionKey)) { COSName filter = enc.getCOSName(COSName.FILTER); if (COSName.STANDARD.equals(filter)) { int revision = enc.getInt(COSName.R); switch (revision) { case 2: case 3: return Algorithm1.withARC4Engine(encryptionKey); case 4: COSName cryptFilterMethod = ofNullable( enc.getDictionaryObject(COSName.CF, COSDictionary.class)) .map(d -> d.getDictionaryObject(COSName.STD_CF, COSDictionary.class)) .map(d -> d.getCOSName(COSName.CFM)).orElse(COSName.NONE); if (COSName.V2.equals(cryptFilterMethod)) { return Algorithm1.withARC4Engine(encryptionKey); } else if (COSName.AESV2.equals(cryptFilterMethod)) { return Algorithm1.withAESEngine(encryptionKey); } LOG.warn("Unable to determine encryption algorithm"); return null; case 5: case 6: return new Algorithm1A(encryptionKey); default: LOG.warn( "Unsupported or invalid standard security handler revision number {}", enc.getDictionaryObject(COSName.R)); return null; } } LOG.warn("Unsupported encryption filter {}", filter); } else { LOG.warn("Empty encryption key"); } } return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/EncryptionException.java000066400000000000000000000024241320103431700277400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; /** * Generic exception signaling that something went wrong during the encryption process * * @author Andrea Vacondio * */ public class EncryptionException extends RuntimeException { public EncryptionException(String message, Throwable cause) { super(message, cause); } public EncryptionException(String message) { super(message); } public EncryptionException(Throwable e) { super(e); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/GeneralEncryptionAlgorithm.java000066400000000000000000000023541320103431700312300ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.cos.COSVisitor; /** * General encryption algorithm as defined in Chapter 7.6.2 of the PDF 32000-1 * * @author Andrea Vacondio * */ public interface GeneralEncryptionAlgorithm extends COSVisitor { /** * Sets the current object and generation numbers * * @param key */ void setCurrentCOSObjectKey(COSObjectKey key); } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/MessageDigests.java000066400000000000000000000037551320103431700266460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Utility class for creating MessageDigest instances. * * @author John Hewson */ public final class MessageDigests { private MessageDigests() { } /** * @return MD5 message digest */ public static MessageDigest md5() { return get("MD5"); } /** * @return SHA-1 message digest */ public static MessageDigest sha1() { return get("SHA-1"); } /** * @return SHA-256 message digest */ public static MessageDigest sha256() { return get("SHA-256"); } /** * @return SHA-384 message digest */ public static MessageDigest sha384() { return get("SHA-384"); } /** * @return SHA-512 message digest */ public static MessageDigest sha512() { return get("SHA-512"); } private static MessageDigest get(String name) { try { return MessageDigest.getInstance(name); } catch (NoSuchAlgorithmException e) { throw new EncryptionException(e); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/PasswordAlgorithm.java000066400000000000000000000022101320103431700273710ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; /** * A password algorithm as defined in PDF spec 32000-1:2008 Chap 7.6.3.4 * * @author Andrea Vacondio * */ interface PasswordAlgorithm { /** * @param context * @return the computed password based on the given input security params */ byte[] computePassword(EncryptionContext context); } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/StandardSecurity.java000066400000000000000000000060601320103431700272170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static java.util.Objects.requireNonNull; import static org.sejda.sambox.encryption.EncryptUtils.truncate127; import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.Optional; import org.sejda.sambox.pdmodel.encryption.AccessPermission; /** * Object holding all the data necessary to specify how to encrypt the document. * * @author Andrea Vacondio * */ public class StandardSecurity { public final String ownerPassword; public final String userPassword; public final AccessPermission permissions; public final StandardSecurityEncryption encryption; public final boolean encryptMetadata; public StandardSecurity(String ownerPassword, String userPassword, StandardSecurityEncryption encryption, boolean encryptMetadata) { this(ownerPassword, userPassword, encryption, new AccessPermission(), encryptMetadata); } public StandardSecurity(String ownerPassword, String userPassword, StandardSecurityEncryption encryption, AccessPermission permissions, boolean encryptMetadata) { requireNonNull(encryption, "Encryption algorithm cannot be null"); this.ownerPassword = Objects.toString(ownerPassword, ""); this.userPassword = Objects.toString(userPassword, ""); this.encryption = encryption; this.permissions = Optional.ofNullable(permissions).orElseGet(AccessPermission::new); // RC4 128 has a version 2 and encryptMetadata is true by default this.encryptMetadata = encryptMetadata || StandardSecurityEncryption.ARC4_128.equals(encryption); } /** * @return the UTF-8 user password bytes truncated to a length o 127 */ byte[] getUserPasswordUTF() { return truncate127(userPassword.getBytes(StandardCharsets.UTF_8)); } /** * the UTF-8 owner password bytes truncated to a length o 127 */ byte[] getOwnerPasswordUTF() { return truncate127(ownerPassword.getBytes(StandardCharsets.UTF_8)); } byte[] getUserPassword() { return userPassword.getBytes(StandardCharsets.ISO_8859_1); } byte[] getOwnerPassword() { return ownerPassword.getBytes(StandardCharsets.ISO_8859_1); } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/StandardSecurityEncryption.java000066400000000000000000000160331320103431700312730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; import static org.sejda.sambox.cos.DirectCOSObject.asDirectObject; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; /** * Available standard security encryptions * * @author Andrea Vacondio * */ public enum StandardSecurityEncryption { ARC4_128(StandardSecurityHandlerRevision.R3, 2) { @Override public COSDictionary generateEncryptionDictionary(EncryptionContext context) { COSDictionary encryptionDictionary = super.generateEncryptionDictionary(context); encryptionDictionary.setItem(COSName.O, pwdString(new Algorithm3().computePassword(context))); encryptionDictionary.setItem(COSName.U, pwdString(new Algorithm5().computePassword(context))); return encryptionDictionary; } @Override public GeneralEncryptionAlgorithm encryptionAlgorithm(EncryptionContext context) { context.key(new Algorithm2().computeEncryptionKey(context)); return Algorithm1.withARC4Engine(context.key()); } }, AES_128(StandardSecurityHandlerRevision.R4, 4) { @Override public COSDictionary generateEncryptionDictionary(EncryptionContext context) { COSDictionary encryptionDictionary = super.generateEncryptionDictionary(context); encryptionDictionary.setItem(COSName.O, pwdString(new Algorithm3().computePassword(context))); encryptionDictionary.setItem(COSName.U, pwdString(new Algorithm5().computePassword(context))); encryptionDictionary.setBoolean(COSName.ENCRYPT_META_DATA, context.security.encryptMetadata); COSDictionary standardCryptFilterDictionary = new COSDictionary(); standardCryptFilterDictionary.setItem(COSName.CFM, COSName.AESV2); standardCryptFilterDictionary.setItem(COSName.AUTEVENT, COSName.DOC_OPEN); standardCryptFilterDictionary.setInt(COSName.LENGTH, revision.length); COSDictionary cryptFilterDictionary = new COSDictionary(); cryptFilterDictionary.setItem(COSName.STD_CF, asDirectObject(standardCryptFilterDictionary)); encryptionDictionary.setItem(COSName.CF, asDirectObject(cryptFilterDictionary)); encryptionDictionary.setItem(COSName.STM_F, COSName.STD_CF); encryptionDictionary.setItem(COSName.STR_F, COSName.STD_CF); return encryptionDictionary; } @Override public GeneralEncryptionAlgorithm encryptionAlgorithm(EncryptionContext context) { context.key(new Algorithm2().computeEncryptionKey(context)); return Algorithm1.withAESEngine(context.key()); } }, AES_256(StandardSecurityHandlerRevision.R6, 5) { @Override public COSDictionary generateEncryptionDictionary(EncryptionContext context) { COSDictionary encryptionDictionary = super.generateEncryptionDictionary(context); encryptionDictionary.setInt(COSName.R, this.revision.revisionNumber); Algorithm8 algo8 = new Algorithm8(new Algorithm2B()); byte[] u = algo8.computePassword(context); encryptionDictionary.setItem(COSName.U, pwdString(u)); encryptionDictionary.setItem(COSName.UE, pwdString(algo8.computeUE(context))); Algorithm9 algo9 = new Algorithm9(new Algorithm2B(u), u); encryptionDictionary.setItem(COSName.O, pwdString(algo9.computePassword(context))); encryptionDictionary.setItem(COSName.OE, pwdString(algo9.computeOE(context))); encryptionDictionary.setBoolean(COSName.ENCRYPT_META_DATA, context.security.encryptMetadata); encryptionDictionary.setItem(COSName.PERMS, pwdString(new Algorithm10().computePerms(context))); COSDictionary standardCryptFilterDictionary = new COSDictionary(); standardCryptFilterDictionary.setItem(COSName.CFM, COSName.AESV3); standardCryptFilterDictionary.setItem(COSName.AUTEVENT, COSName.DOC_OPEN); standardCryptFilterDictionary.setInt(COSName.LENGTH, revision.length); COSDictionary cryptFilterDictionary = new COSDictionary(); cryptFilterDictionary.setItem(COSName.STD_CF, asDirectObject(standardCryptFilterDictionary)); encryptionDictionary.setItem(COSName.CF, asDirectObject(cryptFilterDictionary)); encryptionDictionary.setItem(COSName.STM_F, COSName.STD_CF); encryptionDictionary.setItem(COSName.STR_F, COSName.STD_CF); return encryptionDictionary; } @Override public GeneralEncryptionAlgorithm encryptionAlgorithm(EncryptionContext context) { context.key(EncryptUtils.rnd(32)); return new Algorithm1A(context.key()); } }; public final int version; public final StandardSecurityHandlerRevision revision; private StandardSecurityEncryption(StandardSecurityHandlerRevision revision, int version) { this.revision = revision; this.version = version; } /** * Generates the encryption dictionary for this standard encryption * * @param context * @return */ public COSDictionary generateEncryptionDictionary(EncryptionContext context) { COSDictionary encryptionDictionary = new COSDictionary(); encryptionDictionary.setItem(COSName.FILTER, COSName.STANDARD); encryptionDictionary.setInt(COSName.V, this.version); encryptionDictionary.setInt(COSName.LENGTH, this.revision.length * 8); encryptionDictionary.setInt(COSName.R, this.revision.revisionNumber); encryptionDictionary.setInt(COSName.P, context.security.permissions.getPermissionBytes()); return encryptionDictionary; } public abstract GeneralEncryptionAlgorithm encryptionAlgorithm(EncryptionContext context); private static COSString pwdString(byte[] raw) { COSString string = new COSString(raw); string.encryptable(false); string.setForceHexForm(true); return string; } } sambox-1.1.19/src/main/java/org/sejda/sambox/encryption/StandardSecurityHandlerRevision.java000066400000000000000000000044061320103431700322360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.encryption; /** * Possible Standard Security Handler revisions with bytes length of the encryption key associated to that revision * * @author Andrea Vacondio */ public enum StandardSecurityHandlerRevision { R2(5, 2), R3(16, 3), R4(16, 4), R5(32, 5), R6(32, 6); /** * Length of the encryption key in bytes */ public final int length; public final int revisionNumber; private StandardSecurityHandlerRevision(int length, int revisionNumber) { this.length = length; this.revisionNumber = revisionNumber; } /** * Requires this revision to be the same version as the given one. Throws an {@link EncryptionException} otherwise. * * @param rev * @param message the exception message * @throws EncryptionException if rev is different */ public void require(StandardSecurityHandlerRevision rev, String message) { require(this == rev, message); } /** * Requires this revision to be at least the same version as the given one. Throws an {@link EncryptionException} * otherwise. * * @param rev * @param message */ public void requireAtLeast(StandardSecurityHandlerRevision rev, String message) { require(rev.compareTo(this) <= 0, message); } private static void require(boolean condition, String message) { if (!condition) { throw new EncryptionException(message); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/000077500000000000000000000000001320103431700221555ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/filter/ASCII85Filter.java000066400000000000000000000035251320103431700252000ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.io.IOUtils; import org.sejda.sambox.cos.COSDictionary; /** * Decodes data encoded in an ASCII base-85 representation, reproducing the original binary data. * @author Ben Litchfield */ final class ASCII85Filter extends Filter { @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { try (ASCII85InputStream is = new ASCII85InputStream(encoded)) { IOUtils.copy(is, decoded); } decoded.flush(); return new DecodeResult(parameters); } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException { try (ASCII85OutputStream os = new ASCII85OutputStream(encoded)) { IOUtils.copy(input, os); } encoded.flush(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/ASCII85InputStream.java000066400000000000000000000155741320103431700262350ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** * This class represents an ASCII85 stream. * * @author Ben Litchfield * */ final class ASCII85InputStream extends FilterInputStream { private int index; private int n; private boolean eof; private byte[] ascii; private byte[] b; private static final char TERMINATOR = '~'; private static final char OFFSET = '!'; private static final char NEWLINE = '\n'; private static final char RETURN = '\r'; private static final char SPACE = ' '; private static final char PADDING_U = 'u'; private static final char Z = 'z'; /** * Constructor. * * @param is The input stream to actually read from. */ ASCII85InputStream(InputStream is) { super(is); index = 0; n = 0; eof = false; ascii = new byte[5]; b = new byte[4]; } /** * This will read the next byte from the stream. * * @return The next byte read from the stream. * * @throws IOException If there is an error reading from the wrapped stream. */ @Override public int read() throws IOException { if (index >= n) { if (eof) { return -1; } index = 0; int k; byte z; do { int zz = (byte) in.read(); if (zz == -1) { eof = true; return -1; } z = (byte) zz; } while (z == NEWLINE || z == RETURN || z == SPACE); if (z == TERMINATOR) { eof = true; ascii = b = null; n = 0; return -1; } else if (z == Z) { b[0] = b[1] = b[2] = b[3] = 0; n = 4; } else { ascii[0] = z; // may be EOF here.... for (k = 1; k < 5; ++k) { do { int zz = (byte) in.read(); if (zz == -1) { eof = true; return -1; } z = (byte) zz; } while (z == NEWLINE || z == RETURN || z == SPACE); ascii[k] = z; if (z == TERMINATOR) { // don't include ~ as padding byte ascii[k] = (byte) PADDING_U; break; } } n = k - 1; if (n == 0) { eof = true; ascii = null; b = null; return -1; } if (k < 5) { for (++k; k < 5; ++k) { // use 'u' for padding ascii[k] = (byte) PADDING_U; } eof = true; } // decode stream long t = 0; for (k = 0; k < 5; ++k) { z = (byte) (ascii[k] - OFFSET); if (z < 0 || z > 93) { n = 0; eof = true; ascii = null; b = null; throw new IOException("Invalid data in Ascii85 stream"); } t = (t * 85L) + z; } for (k = 3; k >= 0; --k) { b[k] = (byte) (t & 0xFFL); t >>>= 8; } } } return b[index++] & 0xFF; } /** * This will read a chunk of data. * * @param data The buffer to write data to. * @param offset The offset into the data stream. * @param len The number of byte to attempt to read. * * @return The number of bytes actually read. * * @throws IOException If there is an error reading data from the underlying stream. */ @Override public int read(byte[] data, int offset, int len) throws IOException { if (eof && index >= n) { return -1; } for (int i = 0; i < len; i++) { if (index < n) { data[i + offset] = b[index++]; } else { int t = read(); if (t == -1) { return i; } data[i + offset] = (byte) t; } } return len; } /** * This will close the underlying stream and release any resources. * * @throws IOException If there is an error closing the underlying stream. */ @Override public void close() throws IOException { ascii = null; eof = true; b = null; super.close(); } /** * non supported interface methods. * * @return False always. */ @Override public boolean markSupported() { return false; } /** * Unsupported. * * @param nValue ignored. * * @return Always zero. */ @Override public long skip(long nValue) { return 0; } /** * Unsupported. * * @return Always zero. */ @Override public int available() { return 0; } /** * Unsupported. * * @param readlimit ignored. */ @Override public void mark(int readlimit) { } /** * Unsupported. * * @throws IOException telling that this is an unsupported action. */ @Override public void reset() throws IOException { throw new IOException("Reset is not supported"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/ASCII85OutputStream.java000066400000000000000000000135711320103431700264310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /** * This class represents an ASCII85 output stream. * * @author Ben Litchfield * */ final class ASCII85OutputStream extends FilterOutputStream { private int lineBreak; private int count; private byte[] indata; private byte[] outdata; /** * Function produces five ASCII printing characters from * four bytes of binary data. */ private int maxline; private boolean flushed; private char terminator; private static final char OFFSET = '!'; private static final char NEWLINE = '\n'; private static final char Z = 'z'; /** * Constructor. * * @param out The output stream to write to. */ ASCII85OutputStream(OutputStream out) { super(out); lineBreak = 36 * 2; maxline = 36 * 2; count = 0; indata = new byte[4]; outdata = new byte[5]; flushed = true; terminator = '~'; } /** * This will set the terminating character. * * @param term The terminating character. */ public void setTerminator(char term) { if (term < 118 || term > 126 || term == Z) { throw new IllegalArgumentException("Terminator must be 118-126 excluding z"); } terminator = term; } /** * This will get the terminating character. * * @return The terminating character. */ public char getTerminator() { return terminator; } /** * This will set the line length that will be used. * * @param l The length of the line to use. */ public void setLineLength(int l) { if (lineBreak > l) { lineBreak = l; } maxline = l; } /** * This will get the length of the line. * * @return The line length attribute. */ public int getLineLength() { return maxline; } /** * This will transform the next four ascii bytes. */ private void transformASCII85() { long word = ((((indata[0] << 8) | (indata[1] & 0xFF)) << 16) | ((indata[2] & 0xFF) << 8) | (indata[3] & 0xFF)) & 0xFFFFFFFFL; if (word == 0) { outdata[0] = (byte) Z; outdata[1] = 0; return; } long x; x = word / (85L * 85L * 85L * 85L); outdata[0] = (byte) (x + OFFSET); word -= x * 85L * 85L * 85L * 85L; x = word / (85L * 85L * 85L); outdata[1] = (byte) (x + OFFSET); word -= x * 85L * 85L * 85L; x = word / (85L * 85L); outdata[2] = (byte) (x + OFFSET); word -= x * 85L * 85L; x = word / 85L; outdata[3] = (byte) (x + OFFSET); outdata[4] = (byte) ((word % 85L) + OFFSET); } /** * This will write a single byte. * * @param b The byte to write. * * @throws IOException If there is an error writing to the stream. */ @Override public void write(int b) throws IOException { flushed = false; indata[count++] = (byte) b; if (count < 4) { return; } transformASCII85(); for (int i = 0; i < 5; i++) { if (outdata[i] == 0) { break; } out.write(outdata[i]); if (--lineBreak == 0) { out.write(NEWLINE); lineBreak = maxline; } } count = 0; } /** * This will flush the data to the stream. * * @throws IOException If there is an error writing the data to the stream. */ @Override public void flush() throws IOException { if (flushed) { return; } if (count > 0) { for (int i = count; i < 4; i++) { indata[i] = 0; } transformASCII85(); if (outdata[0] == Z) { for (int i = 0; i < 5; i++) // expand 'z', { outdata[i] = (byte) OFFSET; } } for (int i = 0; i < count + 1; i++) { out.write(outdata[i]); if (--lineBreak == 0) { out.write(NEWLINE); lineBreak = maxline; } } } if (--lineBreak == 0) { out.write(NEWLINE); } out.write(terminator); out.write('>'); out.write(NEWLINE); count = 0; lineBreak = maxline; flushed = true; super.flush(); } /** * This will close the stream. * * @throws IOException If there is an error closing the wrapped stream. */ @Override public void close() throws IOException { try { flush(); super.close(); } finally { indata = outdata = null; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/ASCIIHexFilter.java000066400000000000000000000100601320103431700254600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.util.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Decodes data encoded in an ASCII hexadecimal form, reproducing the original binary data. * * @author Ben Litchfield */ final class ASCIIHexFilter extends Filter { private static final Logger LOG = LoggerFactory.getLogger(ASCIIHexFilter.class); private static final int[] REVERSE_HEX = { /* 0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 30 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 40 */ -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, /* 50 */ 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, /* 60 */ -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, /* 70 */ 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, /* 100 */ 13, 14, 15 }; @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { int value, firstByte, secondByte; while ((firstByte = encoded.read()) != -1) { // always after first char while (isWhitespace(firstByte)) { firstByte = encoded.read(); } if (firstByte == -1 || isEOD(firstByte)) { break; } if (REVERSE_HEX[firstByte] == -1) { LOG.error("Invalid hex, int: " + firstByte + " char: " + (char)firstByte); } value = REVERSE_HEX[firstByte] * 16; secondByte = encoded.read(); if (secondByte == -1 || isEOD(secondByte)) { // second value behaves like 0 in case of EOD decoded.write(value); break; } if (secondByte >= 0) { if (REVERSE_HEX[secondByte] == -1) { LOG.error("Invalid hex, int: " + secondByte + " char: " + (char)secondByte); } value += REVERSE_HEX[secondByte]; } decoded.write(value); } decoded.flush(); return new DecodeResult(parameters); } // whitespace // 0 0x00 Null (NUL) // 9 0x09 Tab (HT) // 10 0x0A Line feed (LF) // 12 0x0C Form feed (FF) // 13 0x0D Carriage return (CR) // 32 0x20 Space (SP) private boolean isWhitespace(int c) { return c == 0 || c == 9 || c == 10 || c == 12 || c == 13 || c == 32; } private boolean isEOD(int c) { return c == '>'; } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException { int byteRead; while ((byteRead = input.read()) != -1) { encoded.write(Hex.getBytes((byte)byteRead)); } encoded.flush(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/CCITTFaxDecoderStream.java000066400000000000000000000614541320103431700270010ustar00rootroot00000000000000/* * Copyright (c) 2012, Harald Kuhr * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name "TwelveMonkeys" nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.sejda.sambox.filter; import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; /** * CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression. * * @author Harald Kuhr * @author Oliver Schmidtmer * @author last modified by $Author: haraldk$ * @version $Id: CCITTFaxDecoderStream.java,v 1.0 23.05.12 15:55 haraldk Exp$ * * Taken from commit fa0341f30237effe523e9905e672d709ffe9c6bd of 7.5.2016 from * twelvemonkeys/imageio/plugins/tiff/CCITTFaxDecoderStream.java * * Initial changes for PDFBox, discussed in PDFBOX-3338: - added optionByteAligned to constructor and to each * decodeRowType() method - removed Validate() usages - catch VALUE_EOL in decode1D() */ final class CCITTFaxDecoderStream extends FilterInputStream { // See TIFF 6.0 Specification, Section 10: "Modified Huffman Compression", page 43. private final int columns; private final byte[] decodedRow; private int decodedLength; private int decodedPos; // Need to take fill order into account (?) (use flip table?) private final int fillOrder; private final int type; private int[] changesReferenceRow; private int[] changesCurrentRow; private int changesReferenceRowCount; private int changesCurrentRowCount; private int lastChangingElement = 0; private boolean optionG32D = false; private boolean optionByteAligned = false; CCITTFaxDecoderStream(final InputStream stream, final int columns, final int type, final int fillOrder, final long options) { super(stream); this.columns = columns; // We know this is only used for b/w (1 bit) this.decodedRow = new byte[(columns + 7) / 8]; this.type = type; this.fillOrder = fillOrder; this.changesReferenceRow = new int[columns + 2]; this.changesCurrentRow = new int[columns + 2]; switch (type) { case TIFFExtension.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE: optionByteAligned = (options & TIFFExtension.GROUP3OPT_BYTEALIGNED) != 0; break; case TIFFExtension.COMPRESSION_CCITT_T4: optionG32D = (options & TIFFExtension.GROUP3OPT_2DENCODING) != 0; optionByteAligned = (options & TIFFExtension.GROUP3OPT_BYTEALIGNED) != 0; break; case TIFFExtension.COMPRESSION_CCITT_T6: optionByteAligned = (options & TIFFExtension.GROUP4OPT_BYTEALIGNED) != 0; break; } } private void fetch() throws IOException { if (decodedPos >= decodedLength) { decodedLength = 0; try { decodeRow(); } catch (EOFException e) { // TODO: Rewrite to avoid throw/catch for normal flow... if (decodedLength != 0) { throw e; } // ..otherwise, just client code trying to read past the end of // stream decodedLength = -1; } decodedPos = 0; } } private void decode1D() throws IOException { int index = 0; boolean white = true; changesCurrentRowCount = 0; do { int completeRun; if (white) { completeRun = decodeRun(whiteRunTree); } else { completeRun = decodeRun(blackRunTree); } if (completeRun == VALUE_EOL) { continue; } index += completeRun; changesCurrentRow[changesCurrentRowCount++] = index; // Flip color for next run white = !white; } while (index < columns); } private void decode2D() throws IOException { changesReferenceRowCount = changesCurrentRowCount; int[] tmp = changesCurrentRow; changesCurrentRow = changesReferenceRow; changesReferenceRow = tmp; boolean white = true; int index = 0; changesCurrentRowCount = 0; mode: while (index < columns) { // read mode Node n = codeTree.root; while (true) { n = n.walk(readBit()); if (n == null) { continue mode; } else if (n.isLeaf) { switch (n.value) { case VALUE_HMODE: int runLength; runLength = decodeRun(white ? whiteRunTree : blackRunTree); index += runLength; changesCurrentRow[changesCurrentRowCount++] = index; runLength = decodeRun(white ? blackRunTree : whiteRunTree); index += runLength; changesCurrentRow[changesCurrentRowCount++] = index; break; case VALUE_PASSMODE: int pChangingElement = getNextChangingElement(index, white) + 1; if (pChangingElement >= changesReferenceRowCount) { index = columns; } else { index = changesReferenceRow[pChangingElement]; } break; default: // Vertical mode (-3 to 3) int vChangingElement = getNextChangingElement(index, white); if (vChangingElement >= changesReferenceRowCount || vChangingElement == -1) { index = columns + n.value; } else { index = changesReferenceRow[vChangingElement] + n.value; } changesCurrentRow[changesCurrentRowCount] = index; changesCurrentRowCount++; white = !white; break; } continue mode; } } } } private int getNextChangingElement(final int a0, final boolean white) { int start = (lastChangingElement & 0xFFFFFFFE) + (white ? 0 : 1); if (start > 2) { start -= 2; } if (a0 == 0) { return start; } for (int i = start; i < changesReferenceRowCount; i += 2) { if (a0 < changesReferenceRow[i]) { lastChangingElement = i; return i; } } return -1; } private void decodeRowType2() throws IOException { if (optionByteAligned) { resetBuffer(); } decode1D(); } private void decodeRowType4() throws IOException { if (optionByteAligned) { bufferPos = -1; // Skip remaining bits and fetch the next byte at row start } eof: while (true) { // read till next EOL code Node n = eolOnlyTree.root; while (true) { n = n.walk(readBit()); if (n == null) { continue eof; } if (n.isLeaf) { break eof; } } } if (!optionG32D || readBit()) { decode1D(); } else { decode2D(); } } private void decodeRowType6() throws IOException { if (optionByteAligned) { bufferPos = -1; // Skip remaining bits and fetch the next byte at row start } decode2D(); } private void decodeRow() throws IOException { switch (type) { case TIFFExtension.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE: decodeRowType2(); break; case TIFFExtension.COMPRESSION_CCITT_T4: decodeRowType4(); break; case TIFFExtension.COMPRESSION_CCITT_T6: decodeRowType6(); break; } int index = 0; boolean white = true; lastChangingElement = 0; for (int i = 0; i <= changesCurrentRowCount; i++) { int nextChange = columns; if (i != changesCurrentRowCount) { nextChange = changesCurrentRow[i]; } if (nextChange > columns) { nextChange = columns; } int byteIndex = index / 8; while (index % 8 != 0 && (nextChange - index) > 0) { decodedRow[byteIndex] |= (white ? 0 : 1 << (7 - ((index) % 8))); index++; } if (index % 8 == 0) { byteIndex = index / 8; final byte value = (byte) (white ? 0x00 : 0xff); while ((nextChange - index) > 7) { decodedRow[byteIndex] = value; index += 8; ++byteIndex; } } while ((nextChange - index) > 0) { if (index % 8 == 0) { decodedRow[byteIndex] = 0; } decodedRow[byteIndex] |= (white ? 0 : 1 << (7 - ((index) % 8))); index++; } white = !white; } if (index != columns) { throw new IOException("Sum of run-lengths does not equal scan line width: " + index + " > " + columns); } decodedLength = (index + 7) / 8; } private int decodeRun(final Tree tree) throws IOException { int total = 0; Node n = tree.root; while (true) { boolean bit = readBit(); n = n.walk(bit); if (n == null) { throw new IOException("Unknown code in Huffman RLE stream"); } if (n.isLeaf) { total += n.value; if (n.value < 64) { return total; } else { n = tree.root; } } } } private void resetBuffer() throws IOException { for (int i = 0; i < decodedRow.length; i++) { decodedRow[i] = 0; } while (true) { if (bufferPos == -1) { return; } readBit(); } } int buffer = -1; int bufferPos = -1; private boolean readBit() throws IOException { if (bufferPos < 0 || bufferPos > 7) { buffer = in.read(); if (buffer == -1) { throw new EOFException("Unexpected end of Huffman RLE stream"); } bufferPos = 0; } boolean isSet; if (fillOrder == TIFFExtension.FILL_LEFT_TO_RIGHT) { isSet = ((buffer >> (7 - bufferPos)) & 1) == 1; } else { isSet = ((buffer >> (bufferPos)) & 1) == 1; } bufferPos++; if (bufferPos > 7) { bufferPos = -1; } return isSet; } @Override public int read() throws IOException { if (decodedLength < 0) { return 0x0; } if (decodedPos >= decodedLength) { fetch(); if (decodedLength < 0) { return 0x0; } } return decodedRow[decodedPos++] & 0xff; } @Override public int read(byte[] b, int off, int len) throws IOException { if (decodedLength < 0) { // TODO better? Math.min(off + len, b.length) Arrays.fill(b, off, off + len, (byte) 0x0); return len; } if (decodedPos >= decodedLength) { fetch(); if (decodedLength < 0) { Arrays.fill(b, off, off + len, (byte) 0x0); return len; } } int read = Math.min(decodedLength - decodedPos, len); System.arraycopy(decodedRow, decodedPos, b, off, read); decodedPos += read; return read; } @Override public long skip(long n) throws IOException { if (decodedLength < 0) { return -1; } if (decodedPos >= decodedLength) { fetch(); if (decodedLength < 0) { return -1; } } int skipped = (int) Math.min(decodedLength - decodedPos, n); decodedPos += skipped; return skipped; } @Override public boolean markSupported() { return false; } @Override public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } private static final class Node { Node left; Node right; int value; // > 63 non term. boolean canBeFill = false; boolean isLeaf = false; void set(final boolean next, final Node node) { if (!next) { left = node; } else { right = node; } } Node walk(final boolean next) { return next ? right : left; } @Override public String toString() { return "[leaf=" + isLeaf + ", value=" + value + ", canBeFill=" + canBeFill + "]"; } } private static final class Tree { final Node root = new Node(); void fill(final int depth, final int path, final int value) throws IOException { Node current = root; for (int i = 0; i < depth; i++) { int bitPos = depth - 1 - i; boolean isSet = ((path >> bitPos) & 1) == 1; Node next = current.walk(isSet); if (next == null) { next = new Node(); if (i == depth - 1) { next.value = value; next.isLeaf = true; } if (path == 0) { next.canBeFill = true; } current.set(isSet, next); } else { if (next.isLeaf) { throw new IOException("node is leaf, no other following"); } } current = next; } } void fill(final int depth, final int path, final Node node) throws IOException { Node current = root; for (int i = 0; i < depth; i++) { int bitPos = depth - 1 - i; boolean isSet = ((path >> bitPos) & 1) == 1; Node next = current.walk(isSet); if (next == null) { if (i == depth - 1) { next = node; } else { next = new Node(); } if (path == 0) { next.canBeFill = true; } current.set(isSet, next); } else { if (next.isLeaf) { throw new IOException("node is leaf, no other following"); } } current = next; } } } static final short[][] BLACK_CODES = { { // 2 bits 0x2, 0x3, }, { // 3 bits 0x2, 0x3, }, { // 4 bits 0x2, 0x3, }, { // 5 bits 0x3, }, { // 6 bits 0x4, 0x5, }, { // 7 bits 0x4, 0x5, 0x7, }, { // 8 bits 0x4, 0x7, }, { // 9 bits 0x18, }, { // 10 bits 0x17, 0x18, 0x37, 0x8, 0xf, }, { // 11 bits 0x17, 0x18, 0x28, 0x37, 0x67, 0x68, 0x6c, 0x8, 0xc, 0xd, }, { // 12 bits 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1c, 0x1d, 0x1e, 0x1f, 0x24, 0x27, 0x28, 0x2b, 0x2c, 0x33, 0x34, 0x35, 0x37, 0x38, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xda, 0xdb, }, { // 13 bits 0x4a, 0x4b, 0x4c, 0x4d, 0x52, 0x53, 0x54, 0x55, 0x5a, 0x5b, 0x64, 0x65, 0x6c, 0x6d, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, } }; static final short[][] BLACK_RUN_LENGTHS = { { // 2 bits 3, 2, }, { // 3 bits 1, 4, }, { // 4 bits 6, 5, }, { // 5 bits 7, }, { // 6 bits 9, 8, }, { // 7 bits 10, 11, 12, }, { // 8 bits 13, 14, }, { // 9 bits 15, }, { // 10 bits 16, 17, 0, 18, 64, }, { // 11 bits 24, 25, 23, 22, 19, 20, 21, 1792, 1856, 1920, }, { // 12 bits 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560, 52, 55, 56, 59, 60, 320, 384, 448, 53, 54, 50, 51, 44, 45, 46, 47, 57, 58, 61, 256, 48, 49, 62, 63, 30, 31, 32, 33, 40, 41, 128, 192, 26, 27, 28, 29, 34, 35, 36, 37, 38, 39, 42, 43, }, { // 13 bits 640, 704, 768, 832, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 512, 576, 896, 960, 1024, 1088, 1152, 1216, } }; public static final short[][] WHITE_CODES = { { // 4 bits 0x7, 0x8, 0xb, 0xc, 0xe, 0xf, }, { // 5 bits 0x12, 0x13, 0x14, 0x1b, 0x7, 0x8, }, { // 6 bits 0x17, 0x18, 0x2a, 0x2b, 0x3, 0x34, 0x35, 0x7, 0x8, }, { // 7 bits 0x13, 0x17, 0x18, 0x24, 0x27, 0x28, 0x2b, 0x3, 0x37, 0x4, 0x8, 0xc, }, { // 8 bits 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1a, 0x1b, 0x2, 0x24, 0x25, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x3, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x4, 0x4a, 0x4b, 0x5, 0x52, 0x53, 0x54, 0x55, 0x58, 0x59, 0x5a, 0x5b, 0x64, 0x65, 0x67, 0x68, 0xa, 0xb, }, { // 9 bits 0x98, 0x99, 0x9a, 0x9b, 0xcc, 0xcd, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, }, { // 10 bits }, { // 11 bits 0x8, 0xc, 0xd, }, { // 12 bits 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1c, 0x1d, 0x1e, 0x1f, } }; public static final short[][] WHITE_RUN_LENGTHS = { { // 4 bits 2, 3, 4, 5, 6, 7, }, { // 5 bits 128, 8, 9, 64, 10, 11, }, { // 6 bits 192, 1664, 16, 17, 13, 14, 15, 1, 12, }, { // 7 bits 26, 21, 28, 27, 18, 24, 25, 22, 256, 23, 20, 19, }, { // 8 bits 33, 34, 35, 36, 37, 38, 31, 32, 29, 53, 54, 39, 40, 41, 42, 43, 44, 30, 61, 62, 63, 0, 320, 384, 45, 59, 60, 46, 49, 50, 51, 52, 55, 56, 57, 58, 448, 512, 640, 576, 47, 48, }, { // 9 bits 1472, 1536, 1600, 1728, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, }, { // 10 bits }, { // 11 bits 1792, 1856, 1920, }, { // 12 bits 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560, } }; final static Node EOL; final static Node FILL; final static Tree blackRunTree; final static Tree whiteRunTree; final static Tree eolOnlyTree; final static Tree codeTree; final static int VALUE_EOL = -2000; final static int VALUE_FILL = -1000; final static int VALUE_PASSMODE = -3000; final static int VALUE_HMODE = -4000; static { EOL = new Node(); EOL.isLeaf = true; EOL.value = VALUE_EOL; FILL = new Node(); FILL.value = VALUE_FILL; FILL.left = FILL; FILL.right = EOL; eolOnlyTree = new Tree(); try { eolOnlyTree.fill(12, 0, FILL); eolOnlyTree.fill(12, 1, EOL); } catch (IOException e) { throw new AssertionError(e); } blackRunTree = new Tree(); try { for (int i = 0; i < BLACK_CODES.length; i++) { for (int j = 0; j < BLACK_CODES[i].length; j++) { blackRunTree.fill(i + 2, BLACK_CODES[i][j], BLACK_RUN_LENGTHS[i][j]); } } blackRunTree.fill(12, 0, FILL); blackRunTree.fill(12, 1, EOL); } catch (IOException e) { throw new AssertionError(e); } whiteRunTree = new Tree(); try { for (int i = 0; i < WHITE_CODES.length; i++) { for (int j = 0; j < WHITE_CODES[i].length; j++) { whiteRunTree.fill(i + 4, WHITE_CODES[i][j], WHITE_RUN_LENGTHS[i][j]); } } whiteRunTree.fill(12, 0, FILL); whiteRunTree.fill(12, 1, EOL); } catch (IOException e) { throw new AssertionError(e); } codeTree = new Tree(); try { codeTree.fill(4, 1, VALUE_PASSMODE); // pass mode codeTree.fill(3, 1, VALUE_HMODE); // H mode codeTree.fill(1, 1, 0); // V(0) codeTree.fill(3, 3, 1); // V_R(1) codeTree.fill(6, 3, 2); // V_R(2) codeTree.fill(7, 3, 3); // V_R(3) codeTree.fill(3, 2, -1); // V_L(1) codeTree.fill(6, 2, -2); // V_L(2) codeTree.fill(7, 2, -3); // V_L(3) } catch (IOException e) { throw new AssertionError(e); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/CCITTFaxEncoderStream.java000066400000000000000000000260331320103431700270050ustar00rootroot00000000000000/* * Copyright (c) 2013, Harald Kuhr * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name "TwelveMonkeys" nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.sejda.sambox.filter; import java.io.IOException; import java.io.OutputStream; /** * CCITT Modified Group 4 (T6) fax compression. * * @author Oliver Schmidtmer * * Taken from commit 047884e3d9e1b30516c79b147ead763303dc9bcb of 21.4.2016 from * twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStream.java * * Initial changes for PDFBox: - removed Validate - G4 compression only - removed options */ final class CCITTFaxEncoderStream extends OutputStream { private int currentBufferLength = 0; private final byte[] inputBuffer; private final int inputBufferLength; private final int columns; private final int rows; private int[] changesCurrentRow; private int[] changesReferenceRow; private int currentRow = 0; private int changesCurrentRowLength = 0; private int changesReferenceRowLength = 0; private byte outputBuffer = 0; private byte outputBufferBitLength = 0; private final int fillOrder; private final OutputStream stream; CCITTFaxEncoderStream(final OutputStream stream, final int columns, final int rows, final int fillOrder) { this.stream = stream; this.columns = columns; this.rows = rows; this.fillOrder = fillOrder; this.changesReferenceRow = new int[columns]; this.changesCurrentRow = new int[columns]; inputBufferLength = (columns + 7) / 8; inputBuffer = new byte[inputBufferLength]; } @Override public void write(int b) throws IOException { inputBuffer[currentBufferLength] = (byte) b; currentBufferLength++; if (currentBufferLength == inputBufferLength) { encodeRow(); currentBufferLength = 0; } } @Override public void flush() throws IOException { stream.flush(); } @Override public void close() throws IOException { stream.close(); } private void encodeRow() throws IOException { currentRow++; int[] tmp = changesReferenceRow; changesReferenceRow = changesCurrentRow; changesCurrentRow = tmp; changesReferenceRowLength = changesCurrentRowLength; changesCurrentRowLength = 0; int index = 0; boolean white = true; while (index < columns) { int byteIndex = index / 8; int bit = index % 8; if ((((inputBuffer[byteIndex] >> (7 - bit)) & 1) == 1) == (white)) { changesCurrentRow[changesCurrentRowLength] = index; changesCurrentRowLength++; white = !white; } index++; } encodeRowType6(); if (currentRow == rows) { writeEOL(); writeEOL(); fill(); } } private void encodeRowType6() throws IOException { encode2D(); } private int[] getNextChanges(int pos, boolean white) { int[] result = new int[] { columns, columns }; for (int i = 0; i < changesCurrentRowLength; i++) { if (pos < changesCurrentRow[i] || (pos == 0 && white)) { result[0] = changesCurrentRow[i]; if ((i + 1) < changesCurrentRowLength) { result[1] = changesCurrentRow[i + 1]; } break; } } return result; } private void writeRun(int runLength, boolean white) throws IOException { int nonterm = runLength / 64; Code[] codes = white ? WHITE_NONTERMINATING_CODES : BLACK_NONTERMINATING_CODES; while (nonterm > 0) { if (nonterm >= codes.length) { write(codes[codes.length - 1].code, codes[codes.length - 1].length); nonterm -= codes.length; } else { write(codes[nonterm - 1].code, codes[nonterm - 1].length); nonterm = 0; } } Code c = white ? WHITE_TERMINATING_CODES[runLength % 64] : BLACK_TERMINATING_CODES[runLength % 64]; write(c.code, c.length); } private void encode2D() throws IOException { boolean white = true; int index = 0; // a0 while (index < columns) { int[] nextChanges = getNextChanges(index, white); // a1, a2 int[] nextRefs = getNextRefChanges(index, white); // b1, b2 int difference = nextChanges[0] - nextRefs[0]; if (nextChanges[0] > nextRefs[1]) { // PMODE write(1, 4); index = nextRefs[1]; } else if (difference > 3 || difference < -3) { // HMODE write(1, 3); writeRun(nextChanges[0] - index, white); writeRun(nextChanges[1] - nextChanges[0], !white); index = nextChanges[1]; } else { // VMODE switch (difference) { case 0: write(1, 1); break; case 1: write(3, 3); break; case 2: write(3, 6); break; case 3: write(3, 7); break; case -1: write(2, 3); break; case -2: write(2, 6); break; case -3: write(2, 7); break; } white = !white; index = nextRefs[0] + difference; } } } private int[] getNextRefChanges(int a0, boolean white) { int[] result = new int[] { columns, columns }; for (int i = (white ? 0 : 1); i < changesReferenceRowLength; i += 2) { if (changesReferenceRow[i] > a0 || (a0 == 0 && i == 0)) { result[0] = changesReferenceRow[i]; if ((i + 1) < changesReferenceRowLength) { result[1] = changesReferenceRow[i + 1]; } break; } } return result; } private void write(int code, int codeLength) throws IOException { for (int i = 0; i < codeLength; i++) { boolean codeBit = ((code >> (codeLength - i - 1)) & 1) == 1; if (fillOrder == TIFFExtension.FILL_LEFT_TO_RIGHT) { outputBuffer |= (codeBit ? 1 << (7 - ((outputBufferBitLength) % 8)) : 0); } else { outputBuffer |= (codeBit ? 1 << (((outputBufferBitLength) % 8)) : 0); } outputBufferBitLength++; if (outputBufferBitLength == 8) { stream.write(outputBuffer); clearOutputBuffer(); } } } private void writeEOL() throws IOException { write(1, 12); } private void fill() throws IOException { if (outputBufferBitLength != 0) { stream.write(outputBuffer); } clearOutputBuffer(); } private void clearOutputBuffer() { outputBuffer = 0; outputBufferBitLength = 0; } private static class Code { private Code(int code, int length) { this.code = code; this.length = length; } final int code; final int length; } private static final Code[] WHITE_TERMINATING_CODES; private static final Code[] WHITE_NONTERMINATING_CODES; private static final Code[] BLACK_TERMINATING_CODES; private static final Code[] BLACK_NONTERMINATING_CODES; static { // Setup HUFFMAN Codes WHITE_TERMINATING_CODES = new Code[64]; WHITE_NONTERMINATING_CODES = new Code[40]; for (int i = 0; i < CCITTFaxDecoderStream.WHITE_CODES.length; i++) { int bitLength = i + 4; for (int j = 0; j < CCITTFaxDecoderStream.WHITE_CODES[i].length; j++) { int value = CCITTFaxDecoderStream.WHITE_RUN_LENGTHS[i][j]; int code = CCITTFaxDecoderStream.WHITE_CODES[i][j]; if (value < 64) { WHITE_TERMINATING_CODES[value] = new Code(code, bitLength); } else { WHITE_NONTERMINATING_CODES[(value / 64) - 1] = new Code(code, bitLength); } } } BLACK_TERMINATING_CODES = new Code[64]; BLACK_NONTERMINATING_CODES = new Code[40]; for (int i = 0; i < CCITTFaxDecoderStream.BLACK_CODES.length; i++) { int bitLength = i + 2; for (int j = 0; j < CCITTFaxDecoderStream.BLACK_CODES[i].length; j++) { int value = CCITTFaxDecoderStream.BLACK_RUN_LENGTHS[i][j]; int code = CCITTFaxDecoderStream.BLACK_CODES[i][j]; if (value < 64) { BLACK_TERMINATING_CODES[value] = new Code(code, bitLength); } else { BLACK_NONTERMINATING_CODES[(value / 64) - 1] = new Code(code, bitLength); } } } } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/CCITTFaxFilter.java000066400000000000000000000122661320103431700255020ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.io.IOUtils; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * Decodes image data that has been encoded using either Group 3 or Group 4 CCITT facsimile (fax) encoding, and encodes * image data to Group 4. * * @author Ben Litchfield * @author Marcel Kammer * @author Paul King */ final class CCITTFaxFilter extends Filter { @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { DecodeResult result = new DecodeResult(new COSDictionary()); result.getParameters().addAll(parameters); // get decode parameters COSDictionary decodeParms = getDecodeParams(parameters, index); // parse dimensions int cols = decodeParms.getInt(COSName.COLUMNS, 1728); int rows = decodeParms.getInt(COSName.ROWS, 0); int height = parameters.getInt(COSName.HEIGHT, COSName.H, 0); if (rows > 0 && height > 0) { // PDFBOX-771, PDFBOX-3727: rows in DecodeParms sometimes contains an incorrect value rows = height; } else { // at least one of the values has to have a valid value rows = Math.max(rows, height); } // decompress data int k = decodeParms.getInt(COSName.K, 0); boolean encodedByteAlign = decodeParms.getBoolean(COSName.ENCODED_BYTE_ALIGN, false); int arraySize = (cols + 7) / 8 * rows; // TODO possible options?? byte[] decompressed = new byte[arraySize]; CCITTFaxDecoderStream s; int type; long tiffOptions; if (k == 0) { tiffOptions = encodedByteAlign ? TIFFExtension.GROUP3OPT_BYTEALIGNED : 0; type = TIFFExtension.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE; } else { if (k > 0) { tiffOptions = encodedByteAlign ? TIFFExtension.GROUP3OPT_BYTEALIGNED : 0; tiffOptions |= TIFFExtension.GROUP3OPT_2DENCODING; type = TIFFExtension.COMPRESSION_CCITT_T4; } else { // k < 0 tiffOptions = encodedByteAlign ? TIFFExtension.GROUP4OPT_BYTEALIGNED : 0; type = TIFFExtension.COMPRESSION_CCITT_T6; } } s = new CCITTFaxDecoderStream(encoded, cols, type, TIFFExtension.FILL_LEFT_TO_RIGHT, tiffOptions); readFromDecoderStream(s, decompressed); // invert bitmap boolean blackIsOne = decodeParms.getBoolean(COSName.BLACK_IS_1, false); if (!blackIsOne) { // Inverting the bitmap // Note the previous approach with starting from an IndexColorModel didn't work // reliably. In some cases the image wouldn't be painted for some reason. // So a safe but slower approach was taken. invertBitmap(decompressed); } // repair missing color space if (!parameters.containsKey(COSName.COLORSPACE)) { result.getParameters().setItem(COSName.COLORSPACE, COSName.DEVICEGRAY); } decoded.write(decompressed); return new DecodeResult(parameters); } static void readFromDecoderStream(CCITTFaxDecoderStream decoderStream, byte[] result) throws IOException { int pos = 0; int read; while ((read = decoderStream.read(result, pos, result.length - pos)) > -1) { pos += read; if (pos >= result.length) { break; } } decoderStream.close(); } private static void invertBitmap(byte[] bufferData) { for (int i = 0, c = bufferData.length; i < c; i++) { bufferData[i] = (byte) (~bufferData[i] & 0xFF); } } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException { int cols = parameters.getInt(COSName.COLUMNS); int rows = parameters.getInt(COSName.ROWS); IOUtils.copy(input, new CCITTFaxEncoderStream(encoded, cols, rows, TIFFExtension.FILL_LEFT_TO_RIGHT)); input.close(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/CryptFilter.java000066400000000000000000000050321320103431700252670ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * Decrypts data encrypted by a security handler, reproducing the data as it was before encryption. * @author Adam Nichols */ final class CryptFilter extends Filter { @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { COSName encryptionName = (COSName) parameters.getDictionaryObject(COSName.NAME); if(encryptionName == null || encryptionName.equals(COSName.IDENTITY)) { // currently the only supported implementation is the Identity crypt filter Filter identityFilter = new IdentityFilter(); identityFilter.decode(encoded, decoded, parameters, index); return new DecodeResult(parameters); } throw new IOException("Unsupported crypt filter " + encryptionName.getName()); } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException { COSName encryptionName = (COSName) parameters.getDictionaryObject(COSName.NAME); if(encryptionName == null || encryptionName.equals(COSName.IDENTITY)) { // currently the only supported implementation is the Identity crypt filter Filter identityFilter = new IdentityFilter(); identityFilter.encode(input, encoded, parameters); } else { throw new IOException("Unsupported crypt filter " + encryptionName.getName()); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/DCTFilter.java000066400000000000000000000273441320103431700246120ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.imageio.IIOException; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.stream.ImageInputStream; import org.sejda.sambox.cos.COSDictionary; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * Decompresses data encoded using a DCT (discrete cosine transform) technique based on the JPEG standard. * * @author John Hewson */ final class DCTFilter extends Filter { private static final Logger LOG = LoggerFactory.getLogger(DCTFilter.class); private static final int POS_TRANSFORM = 11; private static final String ADOBE = "Adobe"; @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { ImageReader reader = findImageReader("JPEG", "a suitable JAI I/O image filter is not installed"); ImageInputStream iis = null; try { iis = ImageIO.createImageInputStream(encoded); // skip one LF if there if (iis.read() != 0x0A) { iis.seek(0); } reader.setInput(iis); String numChannels = getNumChannels(reader); boolean previousUseCacheValue = ImageIO.getUseCache(); // get the raster using horrible JAI workarounds ImageIO.setUseCache(false); try { Raster raster; // Strategy: use read() for RGB or "can't get metadata" // use readRaster() for CMYK and gray and as fallback if read() fails // after "can't get metadata" because "no meta" file was CMYK if ("3".equals(numChannels) || numChannels.isEmpty()) { try { // I'd like to use ImageReader#readRaster but it is buggy and can't read RGB correctly BufferedImage image = reader.read(0); raster = image.getRaster(); } catch (IIOException e) { // JAI can't read CMYK JPEGs using ImageReader#read or ImageIO.read but // fortunately ImageReader#readRaster isn't buggy when reading 4-channel files raster = reader.readRaster(0, null); } } else { // JAI can't read CMYK JPEGs using ImageReader#read or ImageIO.read but // fortunately ImageReader#readRaster isn't buggy when reading 4-channel files raster = reader.readRaster(0, null); } LOG.debug("Raster size is {}x{}", raster.getWidth(), raster.getHeight()); // special handling for 4-component images if (raster.getNumBands() == 4) { // get APP14 marker Integer transform; try { transform = getAdobeTransform(reader.getImageMetadata(0)); } catch (IIOException e) { // we really tried asking nicely, now we're using brute force. transform = getAdobeTransformByBruteForce(iis); } catch (NegativeArraySizeException e) { // we really tried asking nicely, now we're using brute force. transform = getAdobeTransformByBruteForce(iis); } int colorTransform = transform != null ? transform : 0; // 0 = Unknown (RGB or CMYK), 1 = YCbCr, 2 = YCCK switch (colorTransform) { case 0: // already CMYK break; case 1: // TODO YCbCr LOG.warn("YCbCr JPEGs not implemented"); break; case 2: raster = fromYCCKtoCMYK(raster); break; default: throw new IllegalArgumentException("Unknown colorTransform"); } } else if (raster.getNumBands() == 3) { // BGR to RGB raster = fromBGRtoRGB(raster); } DataBufferByte dataBuffer = (DataBufferByte) raster.getDataBuffer(); decoded.write(dataBuffer.getData()); } finally { ImageIO.setUseCache(previousUseCacheValue); } } finally { if (iis != null) { iis.close(); } reader.dispose(); } return new DecodeResult(parameters); } // reads the APP14 Adobe transform tag and returns its value, or 0 if unknown private Integer getAdobeTransform(IIOMetadata metadata) { Element tree = (Element) metadata.getAsTree("javax_imageio_jpeg_image_1.0"); Element markerSequence = (Element) tree.getElementsByTagName("markerSequence").item(0); NodeList app14AdobeNodeList = markerSequence.getElementsByTagName("app14Adobe"); if (app14AdobeNodeList != null && app14AdobeNodeList.getLength() > 0) { Element adobe = (Element) app14AdobeNodeList.item(0); return Integer.parseInt(adobe.getAttribute("transform")); } return 0; } // See in https://github.com/haraldk/TwelveMonkeys // com.twelvemonkeys.imageio.plugins.jpeg.AdobeDCT class for structure of APP14 segment private int getAdobeTransformByBruteForce(ImageInputStream iis) throws IOException { int a = 0; iis.seek(0); int by; while ((by = iis.read()) != -1) { if (ADOBE.charAt(a) == by) { ++a; if (a != ADOBE.length()) { continue; } // match a = 0; long afterAdobePos = iis.getStreamPosition(); iis.seek(iis.getStreamPosition() - 9); int tag = iis.readUnsignedShort(); if (tag != 0xFFEE) { iis.seek(afterAdobePos); continue; } int len = iis.readUnsignedShort(); if (len >= POS_TRANSFORM + 1) { byte[] app14 = new byte[Math.max(len, POS_TRANSFORM + 1)]; if (iis.read(app14) >= POS_TRANSFORM + 1) { return app14[POS_TRANSFORM]; } } } else { a = 0; } } return 0; } // converts YCCK image to CMYK. YCCK is an equivalent encoding for // CMYK data, so no color management code is needed here, nor does the // PDF color space have to be consulted private WritableRaster fromYCCKtoCMYK(Raster raster) { WritableRaster writableRaster = raster.createCompatibleWritableRaster(); int[] value = new int[4]; for (int y = 0, height = raster.getHeight(); y < height; y++) { for (int x = 0, width = raster.getWidth(); x < width; x++) { raster.getPixel(x, y, value); // 4-channels 0..255 float Y = value[0]; float Cb = value[1]; float Cr = value[2]; float K = value[3]; // YCCK to RGB, see http://software.intel.com/en-us/node/442744 int r = clamp(Y + 1.402f * Cr - 179.456f); int g = clamp(Y - 0.34414f * Cb - 0.71414f * Cr + 135.45984f); int b = clamp(Y + 1.772f * Cb - 226.816f); // naive RGB to CMYK int cyan = 255 - r; int magenta = 255 - g; int yellow = 255 - b; // update new raster value[0] = cyan; value[1] = magenta; value[2] = yellow; value[3] = (int) K; writableRaster.setPixel(x, y, value); } } return writableRaster; } // converts from BGR to RGB private WritableRaster fromBGRtoRGB(Raster raster) { WritableRaster writableRaster = raster.createCompatibleWritableRaster(); int width = raster.getWidth(); int height = raster.getHeight(); int w3 = width * 3; int[] tab = new int[w3]; // BEWARE: handling the full image at a time is slower than one line at a time for (int y = 0; y < height; y++) { raster.getPixels(0, y, width, 1, tab); for (int off = 0; off < w3; off += 3) { int tmp = tab[off]; tab[off] = tab[off + 2]; tab[off + 2] = tmp; } writableRaster.setPixels(0, y, width, 1, tab); } return writableRaster; } // returns the number of channels as a string, or an empty string if there is an error getting the meta data private String getNumChannels(ImageReader reader) { try { IIOMetadata imageMetadata = reader.getImageMetadata(0); if (imageMetadata == null) { return ""; } IIOMetadataNode metaTree = (IIOMetadataNode) imageMetadata .getAsTree("javax_imageio_1.0"); Element numChannelsItem = (Element) metaTree.getElementsByTagName("NumChannels") .item(0); if (numChannelsItem == null) { return ""; } return numChannelsItem.getAttribute("value"); } catch (IOException e) { return ""; } catch (NegativeArraySizeException e) { return ""; } } // clamps value to 0-255 range private int clamp(float value) { return (int) ((value < 0) ? 0 : ((value > 255) ? 255 : value)); } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) { throw new UnsupportedOperationException( "DCTFilter encoding not implemented, use the JPEGFactory methods instead"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/DecodeResult.java000066400000000000000000000043401320103431700254030ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.pdmodel.graphics.color.PDJPXColorSpace; /** * The result of a filter decode operation. Allows information such as color space to be extracted from image streams, * and for stream parameters to be repaired during reading. * * @author John Hewson */ public final class DecodeResult { /** Default decode result. */ public static final DecodeResult DEFAULT = new DecodeResult(new COSDictionary()); private final COSDictionary parameters; private PDJPXColorSpace colorSpace; DecodeResult(COSDictionary parameters) { this.parameters = parameters; } DecodeResult(COSDictionary parameters, PDJPXColorSpace colorSpace) { this.parameters = parameters; this.colorSpace = colorSpace; } /** * Returns the stream parameters, repaired using the embedded stream data. * @return the repaired stream parameters, or an empty dictionary */ public COSDictionary getParameters() { return parameters; } /** * Returns the embedded JPX color space, if any. * * @return the the embedded JPX color space, or null if there is none. */ public PDJPXColorSpace getJPXColorSpace() { return colorSpace; } // Sets the JPX color space void setColorSpace(PDJPXColorSpace colorSpace) { this.colorSpace = colorSpace; } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/Filter.java000066400000000000000000000136161320103431700242540ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import static java.util.Objects.nonNull; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Iterator; import java.util.Optional; import java.util.zip.Deflater; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A filter for stream data. * * @author Ben Litchfield * @author John Hewson */ public abstract class Filter { private static final Logger LOG = LoggerFactory.getLogger(Filter.class); /** * Compression Level System Property. Set this to a value from 0 to 9 to change the zlib deflate compression level * used to compress /Flate streams. The default value is -1 which is {@link Deflater#DEFAULT_COMPRESSION}. To set * maximum compression, use {@code System.setProperty(Filter.SYSPROP_DEFLATELEVEL, "9");} */ public static final String SYSPROP_DEFLATELEVEL = "org.apache.pdfbox.filter.deflatelevel"; protected Filter() { } /** * Decodes data, producing the original non-encoded data. * * @param encoded the encoded byte stream * @param decoded the stream where decoded data will be written * @param parameters the parameters used for decoding * @param index the index to the filter being decoded * @return repaired parameters dictionary, or the original parameters dictionary * @throws IOException if the stream cannot be decoded */ public abstract DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException; /** * Encodes data. * * @param input the byte stream to encode * @param encoded the stream where encoded data will be written * @param parameters the parameters used for encoding * @throws IOException if the stream cannot be encoded */ public abstract void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException; // gets the decode params for a specific filter index, this is used to // normalise the DecodeParams entry so that it is always a dictionary protected COSDictionary getDecodeParams(COSDictionary dictionary, int index) { // AV: PDFBOX-3932 considers valid only the case name,dictionary and array,array but discards dp in the case // name,array of 1 where we previously picked the one element as dp COSBase filter = dictionary.getDictionaryObject(COSName.FILTER, COSName.F); COSBase dp = Optional .ofNullable(dictionary.getDictionaryObject(COSName.DECODE_PARMS, COSName.DP)) .orElseGet(COSDictionary::new); if (filter instanceof COSName && dp instanceof COSDictionary) { // PDFBOX-3932: The PDF specification requires "If there is only one filter and that // filter has parameters, DecodeParms shall be set to the filter’s parameter dictionary" // but tests show that Adobe means "one filter name object". return (COSDictionary) dp; } if (filter instanceof COSArray && dp instanceof COSArray) { COSArray array = (COSArray) dp; if (index < array.size()) { COSBase params = Optional.ofNullable(array.getObject(index)) .orElseGet(COSDictionary::new); if (params instanceof COSDictionary) { return (COSDictionary) params; } LOG.error("Ignoring invalid DecodeParams. Expected dictionary but found {}", params.getClass().getName()); return new COSDictionary(); } } if (!(dp instanceof COSArray || dp instanceof COSArray)) { LOG.error("Ignoring invalid DecodeParams. Expected array or dictionary but found {}", dp.getClass().getName()); } return new COSDictionary(); } /** * Finds a suitable image reader for a format. * * @param formatName The format to search for. * @param errorCause The probably cause if something goes wrong. * @return The image reader for the format. * @throws MissingImageReaderException if no image reader is found. */ protected static ImageReader findImageReader(String formatName, String errorCause) throws MissingImageReaderException { Iterator readers = ImageIO.getImageReadersByFormatName(formatName); ImageReader reader = null; while (readers.hasNext()) { reader = readers.next(); if (nonNull(reader) && reader.canReadRaster()) { break; } } if (reader == null) { throw new MissingImageReaderException( "Cannot read " + formatName + " image: " + errorCause); } return reader; } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/FilterFactory.java000066400000000000000000000072401320103431700256000ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.cos.COSName; /** * Factory for Filter classes. * * @author Ben Litchfield */ public final class FilterFactory { /** * Singleton instance. */ public static final FilterFactory INSTANCE = new FilterFactory(); private final Map filters = new HashMap(); private FilterFactory() { Filter flate = new FlateFilter(); Filter dct = new DCTFilter(); Filter ccittFax = new CCITTFaxFilter(); Filter lzw = new LZWFilter(); Filter asciiHex = new ASCIIHexFilter(); Filter ascii85 = new ASCII85Filter(); Filter runLength = new RunLengthDecodeFilter(); Filter crypt = new CryptFilter(); Filter jpx = new JPXFilter(); Filter jbig2 = new JBIG2Filter(); filters.put(COSName.FLATE_DECODE, flate); filters.put(COSName.FLATE_DECODE_ABBREVIATION, flate); filters.put(COSName.DCT_DECODE, dct); filters.put(COSName.DCT_DECODE_ABBREVIATION, dct); filters.put(COSName.CCITTFAX_DECODE, ccittFax); filters.put(COSName.CCITTFAX_DECODE_ABBREVIATION, ccittFax); filters.put(COSName.LZW_DECODE, lzw); filters.put(COSName.LZW_DECODE_ABBREVIATION, lzw); filters.put(COSName.ASCII_HEX_DECODE, asciiHex); filters.put(COSName.ASCII_HEX_DECODE_ABBREVIATION, asciiHex); filters.put(COSName.ASCII85_DECODE, ascii85); filters.put(COSName.ASCII85_DECODE_ABBREVIATION, ascii85); filters.put(COSName.RUN_LENGTH_DECODE, runLength); filters.put(COSName.RUN_LENGTH_DECODE_ABBREVIATION, runLength); filters.put(COSName.CRYPT, crypt); filters.put(COSName.JPX_DECODE, jpx); filters.put(COSName.JBIG2_DECODE, jbig2); } /** * Returns a filter instance given its name as a string. * @param filterName the name of the filter to retrieve * @return the filter that matches the name * @throws IOException if the filter name was invalid */ public Filter getFilter(String filterName) throws IOException { return getFilter(COSName.getPDFName(filterName)); } /** * Returns a filter instance given its COSName. * @param filterName the name of the filter to retrieve * @return the filter that matches the name * @throws IOException if the filter name was invalid */ public Filter getFilter(COSName filterName) throws IOException { Filter filter = filters.get(filterName); if (filter == null) { throw new IOException("Invalid filter: " + filterName); } return filter; } // returns all available filters, for testing Collection getAllFilters() { return filters.values(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/FlateFilter.java000066400000000000000000000141441320103431700252250ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Decompresses data encoded using the zlib/deflate compression method, reproducing the original text or binary data. * * @author Ben Litchfield * @author Marcel Kammer */ final class FlateFilter extends Filter { private static final Logger LOG = LoggerFactory.getLogger(FlateFilter.class); private static final int BUFFER_SIZE = 16348; @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { int predictor = -1; final COSDictionary decodeParams = getDecodeParams(parameters, index); if (decodeParams != null) { predictor = decodeParams.getInt(COSName.PREDICTOR); } try { if (predictor > 1) { int colors = Math.min(decodeParams.getInt(COSName.COLORS, 1), 32); int bitsPerPixel = decodeParams.getInt(COSName.BITS_PER_COMPONENT, 8); int columns = decodeParams.getInt(COSName.COLUMNS, 1); FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); decompress(encoded, baos); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); Predictor.decodePredictor(predictor, colors, bitsPerPixel, columns, bais, decoded); decoded.flush(); baos.reset(); bais.reset(); } else { decompress(encoded, decoded); } } catch (DataFormatException e) { // if the stream is corrupt a DataFormatException may occur LOG.error("FlateFilter: stop reading corrupt stream due to a DataFormatException"); // re-throw the exception throw new IOException(e); } return new DecodeResult(parameters); } // Use Inflater instead of InflateInputStream to avoid an EOFException due to a probably // missing Z_STREAM_END, see PDFBOX-1232 for details private void decompress(InputStream in, OutputStream out) throws IOException, DataFormatException { byte[] buf = new byte[2048]; // skip zlib header in.read(buf, 0, 2); int read = in.read(buf); if (read > 0) { // use nowrap mode to bypass zlib-header and checksum to avoid a DataFormatException Inflater inflater = new Inflater(true); inflater.setInput(buf, 0, read); byte[] res = new byte[1024]; boolean dataWritten = false; while (true) { int resRead = 0; try { resRead = inflater.inflate(res); } catch (DataFormatException exception) { if (dataWritten) { // some data could be read -> don't throw an exception LOG.warn( "FlateFilter: premature end of stream due to a DataFormatException"); break; } // nothing could be read -> re-throw exception throw exception; } if (resRead != 0) { out.write(res, 0, resRead); dataWritten = true; continue; } if (inflater.finished() || inflater.needsDictionary() || in.available() == 0) { break; } read = in.read(buf); inflater.setInput(buf, 0, read); } inflater.end(); } out.flush(); } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException { int compressionLevel = Deflater.DEFAULT_COMPRESSION; try { compressionLevel = Integer .parseInt(System.getProperty(Filter.SYSPROP_DEFLATELEVEL, "-1")); } catch (NumberFormatException ex) { LOG.warn(ex.getMessage(), ex); } compressionLevel = Math.max(-1, Math.min(Deflater.BEST_COMPRESSION, compressionLevel)); Deflater deflater = new Deflater(compressionLevel); DeflaterOutputStream out = new DeflaterOutputStream(encoded, deflater); int amountRead; int mayRead = input.available(); if (mayRead > 0) { byte[] buffer = new byte[Math.min(mayRead, BUFFER_SIZE)]; while ((amountRead = input.read(buffer, 0, Math.min(mayRead, BUFFER_SIZE))) != -1) { out.write(buffer, 0, amountRead); } } out.close(); encoded.flush(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/IdentityFilter.java000066400000000000000000000041241320103431700257600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.sejda.sambox.cos.COSDictionary; /** * The IdentityFilter filter passes the data through without any modifications. * It is defined in section 7.6.5 of the PDF 1.7 spec and also stated in table 26. * * @author Adam Nichols */ final class IdentityFilter extends Filter { private static final int BUFFER_SIZE = 1024; @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { byte[] buffer = new byte[BUFFER_SIZE]; int amountRead; while((amountRead = encoded.read(buffer, 0, BUFFER_SIZE)) != -1) { decoded.write(buffer, 0, amountRead); } decoded.flush(); return new DecodeResult(parameters); } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) throws IOException { byte[] buffer = new byte[BUFFER_SIZE]; int amountRead; while((amountRead = input.read(buffer, 0, BUFFER_SIZE)) != -1) { encoded.write(buffer, 0, amountRead); } encoded.flush(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/JBIG2Filter.java000066400000000000000000000117411320103431700247670ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.SequenceInputStream; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Decompresses data encoded using the JBIG2 standard, reproducing the original * monochrome (1 bit per pixel) image data (or an approximation of that data). * * Requires a JBIG2 plugin for Java Image I/O to be installed. A known working * plug-in is jbig2-imageio * which is available under the GPL v3 license. * * @author Timo Boehme */ final class JBIG2Filter extends Filter { private static final Logger LOG = LoggerFactory.getLogger(JBIG2Filter.class); @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { ImageReader reader = findImageReader("JBIG2", "jbig2-imageio is not installed"); DecodeResult result = new DecodeResult(new COSDictionary()); result.getParameters().addAll(parameters); int bits = parameters.getInt(COSName.BITS_PER_COMPONENT, 1); COSDictionary params = getDecodeParams(parameters, index); COSStream globals = null; if (params != null) { globals = (COSStream) params.getDictionaryObject(COSName.JBIG2_GLOBALS); } ImageInputStream iis = null; try { if (globals != null) { iis = ImageIO.createImageInputStream( new SequenceInputStream(globals.getUnfilteredStream(), encoded)); reader.setInput(iis); } else { iis = ImageIO.createImageInputStream(encoded); reader.setInput(iis); } BufferedImage image; try { image = reader.read(0, reader.getDefaultReadParam()); } catch (Exception e) { // wrap and rethrow any exceptions throw new IOException("Could not read JBIG2 image", e); } // I am assuming since JBIG2 is always black and white // depending on your renderer this might or might be needed if (image.getColorModel().getPixelSize() != bits) { if (bits != 1) { LOG.warn("Attempting to handle a JBIG2 with more than 1-bit depth"); } BufferedImage packedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_BINARY); Graphics graphics = packedImage.getGraphics(); graphics.drawImage(image, 0, 0, null); graphics.dispose(); image = packedImage; } DataBuffer dBuf = image.getData().getDataBuffer(); if (dBuf.getDataType() == DataBuffer.TYPE_BYTE) { decoded.write(((DataBufferByte) dBuf).getData()); } else { throw new IOException("Unexpected image buffer type"); } } finally { if (iis != null) { iis.close(); } reader.dispose(); } // repair missing color space if (!parameters.containsKey(COSName.COLORSPACE)) { result.getParameters().setName(COSName.COLORSPACE, COSName.DEVICEGRAY.getName()); } return new DecodeResult(parameters); } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) { throw new UnsupportedOperationException("JBIG2 encoding not implemented"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/JPXFilter.java000066400000000000000000000124051320103431700246310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferUShort; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * Decompress data encoded using the wavelet-based JPEG 2000 standard, reproducing the original data. * * Requires the Java Advanced Imaging (JAI) Image I/O Tools to be installed from java.net, see * jai-imageio. Alternatively you can build * from the source available in the jai-imageio-core svn repo. * * Mac OS X users should download the tar.gz file for linux and unpack it to obtain the required jar files. The .so file * can be safely ignored. * * @author John Hewson * @author Timo Boehme */ public final class JPXFilter extends Filter { @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { DecodeResult result = new DecodeResult(new COSDictionary()); result.getParameters().addAll(parameters); BufferedImage image = readJPX(encoded, result); WritableRaster raster = image.getRaster(); switch (raster.getDataBuffer().getDataType()) { case DataBuffer.TYPE_BYTE: DataBufferByte byteBuffer = (DataBufferByte) raster.getDataBuffer(); decoded.write(byteBuffer.getData()); return result; case DataBuffer.TYPE_USHORT: DataBufferUShort wordBuffer = (DataBufferUShort) raster.getDataBuffer(); for (short w : wordBuffer.getData()) { decoded.write(w >> 8); decoded.write(w); } return result; default: throw new IOException( "Data type " + raster.getDataBuffer().getDataType() + " not implemented"); } } // try to read using JAI Image I/O private BufferedImage readJPX(InputStream input, DecodeResult result) throws IOException { ImageReader reader = findImageReader("JPEG2000", "Java Advanced Imaging (JAI) Image I/O Tools are not installed"); ImageInputStream iis = null; try { iis = ImageIO.createImageInputStream(input); reader.setInput(iis, true, true); BufferedImage image; try { image = reader.read(0); } catch (Exception e) { // wrap and rethrow any exceptions throw new IOException("Could not read JPEG 2000 (JPX) image", e); } COSDictionary parameters = result.getParameters(); // "If the image stream uses the JPXDecode filter, this entry is optional // and shall be ignored if present" // // note that indexed color spaces make the BPC logic tricky, see PDFBOX-2204 int bpc = image.getColorModel().getPixelSize() / image.getRaster().getNumBands(); parameters.setInt(COSName.BITS_PER_COMPONENT, bpc); // "Decode shall be ignored, except in the case where the image is treated as a mask" if (!parameters.getBoolean(COSName.IMAGE_MASK, false)) { parameters.removeItem(COSName.DECODE); } // override dimensions, see PDFBOX-1735 parameters.setInt(COSName.WIDTH, image.getWidth()); parameters.setInt(COSName.HEIGHT, image.getHeight()); // extract embedded color space if (!parameters.containsKey(COSName.COLORSPACE)) { // result.setColorSpace(new PDJPXColorSpace(image.getColorModel().getColorSpace())); } return image; } finally { if (iis != null) { iis.close(); } reader.dispose(); } } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) { throw new UnsupportedOperationException("JPX encoding not implemented"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/LZWFilter.java000066400000000000000000000247061320103431700246530ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.filter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.imageio.stream.MemoryCacheImageInputStream; import javax.imageio.stream.MemoryCacheImageOutputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * This is the filter used for the LZWDecode filter. * * @author Ben Litchfield * @author Tilman Hausherr */ public class LZWFilter extends Filter { private static final Logger LOG = LoggerFactory.getLogger(LZWFilter.class); /** * The LZW clear table code. */ public static final long CLEAR_TABLE = 256; /** * The LZW end of data code. */ public static final long EOD = 257; //BEWARE: codeTable must be local to each method, because there is only // one instance of each filter /** * {@inheritDoc} */ @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { int predictor = -1; int earlyChange = 1; COSDictionary decodeParams = getDecodeParams(parameters, index); if (decodeParams != null) { predictor = decodeParams.getInt(COSName.PREDICTOR); earlyChange = decodeParams.getInt(COSName.EARLY_CHANGE, 1); if (earlyChange != 0 && earlyChange != 1) { earlyChange = 1; } } if (predictor > 1) { @SuppressWarnings("null") int colors = Math.min(decodeParams.getInt(COSName.COLORS, 1), 32); int bitsPerPixel = decodeParams.getInt(COSName.BITS_PER_COMPONENT, 8); int columns = decodeParams.getInt(COSName.COLUMNS, 1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); doLZWDecode(encoded, baos, earlyChange); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); Predictor.decodePredictor(predictor, colors, bitsPerPixel, columns, bais, decoded); decoded.flush(); baos.reset(); bais.reset(); } else { doLZWDecode(encoded, decoded, earlyChange); } return new DecodeResult(parameters); } private void doLZWDecode(InputStream encoded, OutputStream decoded, int earlyChange) throws IOException { List codeTable = new ArrayList(); int chunk = 9; final MemoryCacheImageInputStream in = new MemoryCacheImageInputStream(encoded); long nextCommand; long prevCommand = -1; try { while ((nextCommand = in.readBits(chunk)) != EOD) { if (nextCommand == CLEAR_TABLE) { chunk = 9; codeTable = createCodeTable(); prevCommand = -1; } else { if (nextCommand < codeTable.size()) { byte[] data = codeTable.get((int) nextCommand); byte firstByte = data[0]; decoded.write(data); if (prevCommand != -1) { checkIndexBounds(codeTable, prevCommand, in); data = codeTable.get((int) prevCommand); byte[] newData = Arrays.copyOf(data, data.length + 1); newData[data.length] = firstByte; codeTable.add(newData); } } else { checkIndexBounds(codeTable, prevCommand, in); byte[] data = codeTable.get((int) prevCommand); byte[] newData = Arrays.copyOf(data, data.length + 1); newData[data.length] = data[0]; decoded.write(newData); codeTable.add(newData); } chunk = calculateChunk(codeTable.size(), earlyChange); prevCommand = nextCommand; } } } catch (EOFException ex) { LOG.warn("Premature EOF in LZW stream, EOD code missing"); } decoded.flush(); } private void checkIndexBounds(List codeTable, long index, MemoryCacheImageInputStream in) throws IOException { if (index < 0) { throw new IOException( "negative array index: " + index + " near offset " + in.getStreamPosition()); } if (index >= codeTable.size()) { throw new IOException("array index overflow: " + index + " >= " + codeTable.size() + " near offset " + in.getStreamPosition()); } } @Override public void encode(InputStream rawData, OutputStream encoded, COSDictionary parameters) throws IOException { List codeTable = createCodeTable(); int chunk = 9; byte[] inputPattern = null; final MemoryCacheImageOutputStream out = new MemoryCacheImageOutputStream(encoded); out.writeBits(CLEAR_TABLE, chunk); int foundCode = -1; int r; while ((r = rawData.read()) != -1) { byte by = (byte) r; if (inputPattern == null) { inputPattern = new byte[] { by }; foundCode = by & 0xff; } else { inputPattern = Arrays.copyOf(inputPattern, inputPattern.length + 1); inputPattern[inputPattern.length - 1] = by; int newFoundCode = findPatternCode(codeTable, inputPattern); if (newFoundCode == -1) { // use previous chunk = calculateChunk(codeTable.size() - 1, 1); out.writeBits(foundCode, chunk); // create new table entry codeTable.add(inputPattern); if (codeTable.size() == 4096) { // code table is full out.writeBits(CLEAR_TABLE, chunk); codeTable = createCodeTable(); } inputPattern = new byte[] { by }; foundCode = by & 0xff; } else { foundCode = newFoundCode; } } } if (foundCode != -1) { chunk = calculateChunk(codeTable.size() - 1, 1); out.writeBits(foundCode, chunk); } // PPDFBOX-1977: the decoder wouldn't know that the encoder would output // an EOD as code, so he would have increased his own code table and // possibly adjusted the chunk. Therefore, the encoder must behave as // if the code table had just grown and thus it must be checked it is // needed to adjust the chunk, based on an increased table size parameter chunk = calculateChunk(codeTable.size(), 1); out.writeBits(EOD, chunk); // pad with 0 out.writeBits(0, 7); // must do or file will be empty :-( out.flush(); out.close(); } /** * Find the longest matching pattern in the code table. * * @param codeTable The LZW code table. * @param pattern The pattern to be searched for. * @return The index of the longest matching pattern or -1 if nothing is * found. */ private int findPatternCode(List codeTable, byte[] pattern) { int foundCode = -1; int foundLen = 0; for (int i = codeTable.size() - 1; i >= 0; --i) { if (i <= EOD) { // we're in the single byte area if (foundCode != -1) { // we already found pattern with size > 1 return foundCode; } else if (pattern.length > 1) { // we won't find anything here anyway return -1; } } byte[] tryPattern = codeTable.get(i); if ((foundCode != -1 || tryPattern.length > foundLen) && Arrays.equals(tryPattern, pattern)) { foundCode = i; foundLen = tryPattern.length; } } return foundCode; } /** * Init the code table with 1 byte entries and the EOD and CLEAR_TABLE * markers. */ private List createCodeTable() { List codeTable = new ArrayList(4096); for (int i = 0; i < 256; ++i) { codeTable.add(new byte[] { (byte) (i & 0xFF) }); } codeTable.add(null); // 256 EOD codeTable.add(null); // 257 CLEAR_TABLE return codeTable; } /** * Calculate the appropriate chunk size * * @param tabSize the size of the code table * @param earlyChange 0 or 1 for early chunk increase * * @return a value between 9 and 12 */ private int calculateChunk(int tabSize, int earlyChange) { if (tabSize >= 2048 - earlyChange) { return 12; } if (tabSize >= 1024 - earlyChange) { return 11; } if (tabSize >= 512 - earlyChange) { return 10; } return 9; } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/MissingImageReaderException.java000066400000000000000000000022441320103431700304000ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.IOException; /** * Thrown when a required JAI ImageReader is missing. * * @author John Hewson */ public class MissingImageReaderException extends IOException { /** * */ private static final long serialVersionUID = 1L; public MissingImageReaderException(String message) { super(message); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/Predictor.java000066400000000000000000000243251320103431700247610ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.filter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.io.IOUtils; /** * Helper class to contain predictor decoding used by Flate and LZW filter. * To see the history, look at the FlateFilter class. */ public final class Predictor { private Predictor() { } static void decodePredictor(int predictor, int colors, int bitsPerComponent, int columns, InputStream in, OutputStream out) throws IOException { if (predictor == 1) { // no prediction IOUtils.copy(in, out); } else { // calculate sizes final int bitsPerPixel = colors * bitsPerComponent; final int bytesPerPixel = (bitsPerPixel + 7) / 8; final int rowlength = (columns * bitsPerPixel + 7) / 8; byte[] actline = new byte[rowlength]; byte[] lastline = new byte[rowlength]; int linepredictor = predictor; while (in.available() > 0) { // test for PNG predictor; each value >= 10 (not only 15) indicates usage of PNG predictor if (predictor >= 10) { // PNG predictor; each row starts with predictor type (0, 1, 2, 3, 4) // read per line predictor linepredictor = in.read(); if (linepredictor == -1) { return; } // add 10 to tread value 0 as 10, 1 as 11, ... linepredictor += 10; } // read line int i, offset = 0; while (offset < rowlength && ((i = in.read(actline, offset, rowlength - offset)) != -1)) { offset += i; } // do prediction as specified in PNG-Specification 1.2 switch (linepredictor) { case 2: // PRED TIFF SUB if (bitsPerComponent == 8) { // for 8 bits per component it is the same algorithm as PRED SUB of PNG format for (int p = bytesPerPixel; p < rowlength; p++) { int sub = actline[p] & 0xff; int left = actline[p - bytesPerPixel] & 0xff; actline[p] = (byte) (sub + left); } break; } if (bitsPerComponent == 16) { for (int p = bytesPerPixel; p < rowlength; p += 2) { int sub = ((actline[p] & 0xff) << 8) + (actline[p + 1] & 0xff); int left = (((actline[p - bytesPerPixel] & 0xff) << 8) + (actline[p - bytesPerPixel + 1] & 0xff)); actline[p] = (byte) (((sub + left) >> 8) & 0xff); actline[p + 1] = (byte) ((sub + left) & 0xff); } break; } if (bitsPerComponent == 1 && colors == 1) { // bytesPerPixel cannot be used: // "A row shall occupy a whole number of bytes, rounded up if necessary. // Samples and their components shall be packed into bytes // from high-order to low-order bits." for (int p = 0; p < rowlength; p++) { for (int bit = 7; bit >= 0; --bit) { int sub = (actline[p] >> bit) & 1; if (p == 0 && bit == 7) { continue; } int left; if (bit == 7) { // use bit #0 from previous byte left = actline[p - 1] & 1; } else { // use "previous" bit left = (actline[p] >> (bit + 1)) & 1; } if (((sub + left) & 1) == 0) { // reset bit actline[p] = (byte) (actline[p] & ~(1 << bit)); } else { // set bit actline[p] = (byte) (actline[p] | (1 << bit)); } } } break; } // everything else, i.e. bpc 2 and 4, but has been tested for bpc 1 and 8 too int elements = columns * colors; for (int p = colors; p < elements; ++p) { int bytePosSub = p * bitsPerComponent / 8; int bitPosSub = 8 - p * bitsPerComponent % 8 - bitsPerComponent; int bytePosLeft = (p - colors) * bitsPerComponent / 8; int bitPosLeft = 8 - (p - colors) * bitsPerComponent % 8 - bitsPerComponent; int sub = getBitSeq(actline[bytePosSub], bitPosSub, bitsPerComponent); int left = getBitSeq(actline[bytePosLeft], bitPosLeft, bitsPerComponent); actline[bytePosSub] = (byte) calcSetBitSeq(actline[bytePosSub], bitPosSub, bitsPerComponent, sub + left); } break; case 10: // PRED NONE // do nothing break; case 11: // PRED SUB for (int p = bytesPerPixel; p < rowlength; p++) { int sub = actline[p]; int left = actline[p - bytesPerPixel]; actline[p] = (byte) (sub + left); } break; case 12: // PRED UP for (int p = 0; p < rowlength; p++) { int up = actline[p] & 0xff; int prior = lastline[p] & 0xff; actline[p] = (byte) ((up + prior) & 0xff); } break; case 13: // PRED AVG for (int p = 0; p < rowlength; p++) { int avg = actline[p] & 0xff; int left = p - bytesPerPixel >= 0 ? actline[p - bytesPerPixel] & 0xff : 0; int up = lastline[p] & 0xff; actline[p] = (byte) ((avg + (left + up) / 2) & 0xff); } break; case 14: // PRED PAETH for (int p = 0; p < rowlength; p++) { int paeth = actline[p] & 0xff; int a = p - bytesPerPixel >= 0 ? actline[p - bytesPerPixel] & 0xff : 0;// left int b = lastline[p] & 0xff;// upper int c = p - bytesPerPixel >= 0 ? lastline[p - bytesPerPixel] & 0xff : 0;// upperleft int value = a + b - c; int absa = Math.abs(value - a); int absb = Math.abs(value - b); int absc = Math.abs(value - c); if (absa <= absb && absa <= absc) { actline[p] = (byte) ((paeth + a) & 0xff); } else if (absb <= absc) { actline[p] = (byte) ((paeth + b) & 0xff); } else { actline[p] = (byte) ((paeth + c) & 0xff); } } break; default: break; } System.arraycopy(actline, 0, lastline, 0, rowlength); out.write(actline); } } } // get value from bit interval from a byte static int getBitSeq(int by, int startBit, int bitSize) { int mask = ((1 << bitSize) - 1); return (by >>> startBit) & mask; } // set value in a bit interval and return that value static int calcSetBitSeq(int by, int startBit, int bitSize, int val) { int mask = ((1 << bitSize) - 1); int truncatedVal = val & mask; mask = ~(mask << startBit); return (by & mask) | (truncatedVal << startBit); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/RunLengthDecodeFilter.java000066400000000000000000000051321320103431700272010ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.filter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.sejda.sambox.cos.COSDictionary; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Decompresses data encoded using a byte-oriented run-length encoding algorithm, * reproducing the original text or binary data * * @author Ben Litchfield */ final class RunLengthDecodeFilter extends Filter { private static final Logger LOG = LoggerFactory.getLogger(RunLengthDecodeFilter.class); private static final int RUN_LENGTH_EOD = 128; @Override public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary parameters, int index) throws IOException { int dupAmount; byte[] buffer = new byte[128]; while ((dupAmount = encoded.read()) != -1 && dupAmount != RUN_LENGTH_EOD) { if (dupAmount <= 127) { int amountToCopy = dupAmount + 1; int compressedRead; while(amountToCopy > 0) { compressedRead = encoded.read(buffer, 0, amountToCopy); decoded.write(buffer, 0, compressedRead); amountToCopy -= compressedRead; } } else { int dupByte = encoded.read(); for (int i = 0; i < 257 - dupAmount; i++) { decoded.write(dupByte); } } } return new DecodeResult(parameters); } @Override public void encode(InputStream input, OutputStream encoded, COSDictionary parameters) { LOG.warn("RunLengthDecodeFilter.encode is not implemented yet, skipping this stream."); } } sambox-1.1.19/src/main/java/org/sejda/sambox/filter/TIFFExtension.java000066400000000000000000000100611320103431700254430ustar00rootroot00000000000000/* * Copyright (c) 2012, Harald Kuhr * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name "TwelveMonkeys" nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.sejda.sambox.filter; /** * TIFFExtension * * @author Harald Kuhr * @author last modified by $Author: haraldk$ * @version $Id: TIFFExtension.java,v 1.0 08.05.12 16:45 haraldk Exp$ */ interface TIFFExtension { /** CCITT T.4/Group 3 Fax compression. */ int COMPRESSION_CCITT_T4 = 3; /** CCITT T.6/Group 4 Fax compression. */ int COMPRESSION_CCITT_T6 = 4; /** LZW Compression. Was baseline, but moved to extension due to license issues in the LZW algorithm. */ int COMPRESSION_LZW = 5; /** Deprecated. For backwards compatibility only ("Old-style" JPEG). */ int COMPRESSION_OLD_JPEG = 6; /** JPEG Compression (lossy). */ int COMPRESSION_JPEG = 7; /** Custom: PKZIP-style Deflate. */ int COMPRESSION_DEFLATE = 32946; /** Adobe-style Deflate. */ int COMPRESSION_ZLIB = 8; int PHOTOMETRIC_SEPARATED = 5; int PHOTOMETRIC_YCBCR = 6; int PHOTOMETRIC_CIELAB = 8; int PHOTOMETRIC_ICCLAB = 9; int PHOTOMETRIC_ITULAB = 10; int PLANARCONFIG_PLANAR = 2; int PREDICTOR_HORIZONTAL_DIFFERENCING = 2; int PREDICTOR_HORIZONTAL_FLOATINGPOINT = 3; int FILL_RIGHT_TO_LEFT = 2; int SAMPLEFORMAT_INT = 2; int SAMPLEFORMAT_FP = 3; int SAMPLEFORMAT_UNDEFINED = 4; int YCBCR_POSITIONING_CENTERED = 1; int YCBCR_POSITIONING_COSITED = 2; /** Deprecated. For backwards compatibility only ("Old-style" JPEG). */ int JPEG_PROC_BASELINE = 1; /** Deprecated. For backwards compatibility only ("Old-style" JPEG). */ int JPEG_PROC_LOSSLESS = 14; /** For use with Photometric: 5 (Separated), when image data is in CMYK color space. */ int INKSET_CMYK = 1; /** * For use with Photometric: 5 (Separated), when image data is in a color space other than CMYK. * See {@link com.twelvemonkeys.imageio.metadata.exif.TIFF#TAG_INK_NAMES InkNames} field for a * description of the inks to be used. */ int INKSET_NOT_CMYK = 2; int ORIENTATION_TOPRIGHT = 2; int ORIENTATION_BOTRIGHT = 3; int ORIENTATION_BOTLEFT = 4; int ORIENTATION_LEFTTOP = 5; int ORIENTATION_RIGHTTOP = 6; int ORIENTATION_RIGHTBOT = 7; int ORIENTATION_LEFTBOT = 8; int GROUP3OPT_2DENCODING = 1; int GROUP3OPT_UNCOMPRESSED = 2; int GROUP3OPT_FILLBITS = 4; int GROUP3OPT_BYTEALIGNED = 8; int GROUP4OPT_UNCOMPRESSED = 2; int GROUP4OPT_BYTEALIGNED = 4; int COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE = 2; int FILL_LEFT_TO_RIGHT = 1; // Default } sambox-1.1.19/src/main/java/org/sejda/sambox/input/000077500000000000000000000000001320103431700220275ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/input/AbstractXrefStreamParser.java000066400000000000000000000136311320103431700276170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static java.util.stream.LongStream.empty; import static java.util.stream.LongStream.rangeClosed; import java.io.IOException; import java.io.InputStream; import java.util.PrimitiveIterator.OfLong; import java.util.stream.LongStream; import java.util.stream.LongStream.Builder; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.xref.CompressedXrefEntry; import org.sejda.sambox.xref.XrefEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base class for an xref stream parser. Implementors will decide what to do when the parser finds a new trailer or a * new entries. * * @author Andrea Vacondio * @see AbstractXrefTableParser */ abstract class AbstractXrefStreamParser { private static final Logger LOG = LoggerFactory.getLogger(AbstractXrefStreamParser.class); private COSParser parser; AbstractXrefStreamParser(COSParser parser) { this.parser = parser; } /** * Action to perform when a trailer is found * * @param trailer */ abstract void onTrailerFound(COSDictionary trailer); /** * Action to perform when an {@link XrefEntry} is found * * @param entry */ abstract void onEntryFound(XrefEntry entry); /** * Parse the xref object stream. * * @param streamObjectOffset xref stream object offset * @return the stream dictionary * @throws IOException */ COSDictionary parse(long streamObjectOffset) throws IOException { LOG.debug("Parsing xref stream at offset " + streamObjectOffset); parser.position(streamObjectOffset); parser.skipIndirectObjectDefinition(); parser.skipSpaces(); COSDictionary dictionary = parser.nextDictionary(); try (COSStream xrefStream = parser.nextStream(dictionary)) { onTrailerFound(dictionary); parseStream(xrefStream); } LOG.debug("Done parsing xref stream"); return dictionary; } void parseStream(COSStream xrefStream) throws IOException { LongStream objectNumbers = empty(); COSArray index = xrefStream.getDictionaryObject(COSName.INDEX, COSArray.class); if (index == null) { LOG.debug("No index found for xref stream, using default values"); objectNumbers = rangeClosed(0, xrefStream.getInt(COSName.SIZE)); } else { LOG.debug("Index found, now retrieving expected object numbers"); Builder builder = LongStream.builder(); for (int i = 0; i < index.size(); i += 2) { long start = ((COSNumber) index.get(i)).longValue(); long end = start + Math.max(((COSNumber) index.get(i + 1)).longValue() - 1, 0); LOG.trace(String.format("Adding expected range from %d to %d", start, end)); rangeClosed(start, end).forEach(builder::add); } objectNumbers = builder.build(); } COSArray xrefFormat = (COSArray) xrefStream.getDictionaryObject(COSName.W); int w0 = xrefFormat.getInt(0); int w1 = xrefFormat.getInt(1); int w2 = xrefFormat.getInt(2); int lineSize = w0 + w1 + w2; try (InputStream stream = xrefStream.getUnfilteredStream()) { OfLong objectIds = objectNumbers.iterator(); while (stream.available() > 0 && objectIds.hasNext()) { Long objectId = objectIds.next(); byte[] currLine = new byte[lineSize]; stream.read(currLine); int type = (w0 == 0) ? 1 : 0; int i = 0; /* * Grabs the number of bytes specified for the first column in the W array and stores it. */ for (i = 0; i < w0; i++) { type += (currLine[i] & 0x00ff) << ((w0 - i - 1) * 8); } int field1 = 0; for (i = 0; i < w1; i++) { field1 += (currLine[i + w0] & 0x00ff) << ((w1 - i - 1) * 8); } int field2 = 0; for (i = 0; i < w2; i++) { field2 += (currLine[i + w0 + w1] & 0x00ff) << ((w2 - i - 1) * 8); } switch (type) { case 0: onEntryFound(XrefEntry.freeEntry(objectId, field2)); break; case 1: onEntryFound(XrefEntry.inUseEntry(objectId, field1, field2)); break; case 2: onEntryFound(CompressedXrefEntry.compressedEntry(objectId, field1, field2)); break; default: LOG.warn("Unknown xref entry type " + type); break; } } } } COSParser parser() { return parser; } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/AbstractXrefTableParser.java000066400000000000000000000121721320103431700274120ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static org.sejda.sambox.util.CharUtils.isDigit; import static org.sejda.sambox.util.CharUtils.isEOF; import static org.sejda.sambox.util.CharUtils.isEndOfName; import static org.sejda.sambox.xref.XrefEntry.inUseEntry; import java.io.IOException; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.xref.XrefEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base class for an xref table parser. Implementors will decide what to do when the parser finds a new trailer or a new * entries. * * @author Andrea Vacondio * @see AbstractXrefStreamParser */ abstract class AbstractXrefTableParser { private static final Logger LOG = LoggerFactory.getLogger(AbstractXrefTableParser.class); static final String TRAILER = "trailer"; static final String XREF = "xref"; private COSParser parser; AbstractXrefTableParser(COSParser parser) { this.parser = parser; } /** * Action to perform when a trailer is found * * @param trailer */ abstract void onTrailerFound(COSDictionary trailer); /** * Action to perform when an {@link XrefEntry} is found * * @param entry */ abstract void onEntryFound(XrefEntry entry); /** * Parse the xref table * * @param tableOffset xref table offset. This is the offset of the "xref" keyword. * @return the trailer for this table * @throws IOException */ public COSDictionary parse(long tableOffset) throws IOException { parseXrefTable(tableOffset); parser.skipSpaces(); while (parser.source().peek() != 't') { LOG.warn("Expected trailer object at position " + parser.position() + ", skipping line."); parser.readLine(); } return parseTrailer(); } private void parseXrefTable(long tableOffset) throws IOException { parser.position(tableOffset); parser.skipExpected(XREF); if (parser.isNextToken(TRAILER)) { LOG.warn("Skipping empty xref table at offset " + tableOffset); return; } while (true) { long currentObjectNumber = parser.readObjectNumber(); long numberOfEntries = parser.readLong(); parser.skipSpaces(); for (int i = 0; i < numberOfEntries; i++) { int next = parser.source().peek(); if (next == 't' || isEndOfName(next) || isEOF(next)) { break; } String currentLine = parser.readLine(); String[] splitString = currentLine.split("\\s"); if (splitString.length < 3) { throw new IOException( "Corrupted xref table entry. Invalid xref line: " + currentLine); } String entryType = splitString[splitString.length - 1]; if ("n".equals(entryType)) { try { onEntryFound(inUseEntry(currentObjectNumber, Long.parseLong(splitString[0]), Integer.parseInt(splitString[1]))); } catch (NumberFormatException e) { throw new IOException("Corrupted xref table entry.", e); } } else if (!"f".equals(entryType)) { throw new IOException( "Corrupted xref table entry. Expected 'f' but was " + entryType); } currentObjectNumber++; parser.skipSpaces(); } parser.skipSpaces(); if (!isDigit(parser.source().peek())) { break; } } } private COSDictionary parseTrailer() throws IOException { long offset = parser.position(); LOG.debug("Parsing trailer at " + offset); parser.skipExpected(TRAILER); parser.skipSpaces(); COSDictionary dictionary = parser.nextDictionary(); onTrailerFound(dictionary); parser.skipSpaces(); return dictionary; } COSParser parser() { return parser; } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/BaseCOSParser.java000066400000000000000000000367271320103431700253050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static org.sejda.sambox.util.CharUtils.isCarriageReturn; import static org.sejda.sambox.util.CharUtils.isLineFeed; import static org.sejda.sambox.util.CharUtils.isSpace; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.sejda.io.SeekableSource; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base parser for COS objects providing methods to get parsed objects from the given {@link SeekableSource} * * @author Andrea Vacondio */ abstract class BaseCOSParser extends SourceReader { private static final Logger LOG = LoggerFactory.getLogger(BaseCOSParser.class); public static final String ENDOBJ = "endobj"; public static final String OBJ = "obj"; public static final String STREAM = "stream"; public static final String ENDSTREAM = "endstream"; BaseCOSParser(SeekableSource source) { super(source); } /** * @return The next parsed basic type object from the stream or null if the next token is not a COSBase. Basic types * are defined in Chap 7.3 of PDF 32000-1:2008 * @throws IOException If there is an error during parsing. */ public abstract COSBase nextParsedToken() throws IOException; /** * @return The next parsed dictionary object from the stream. Dictionary objects are defined in Chap 7.3.7 of PDF * 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSDictionary nextDictionary() throws IOException { skipExpected("<<"); skipSpaces(); COSDictionary dictionary = new COSDictionary(); int c; while ((c = source().peek()) != -1 && c != '>') { if (c != '/') { LOG.warn("Invalid dictionary key, expected '/' but was '" + (char) c + "' at " + position()); if (!consumeInvalidDictionaryKey()) { return dictionary; } } else { COSName key = nextName(); COSBase value = nextParsedToken(); if (value == null) { LOG.warn("Bad dictionary declaration for key '{}'", key); } else { dictionary.setItem(key, value); } } skipSpaces(); } skipExpected(">>"); return dictionary; } /** * Consumes an invalid dictionary key * * @return true if the dictionary has been recovered and parsing can go on * @throws IOException */ private boolean consumeInvalidDictionaryKey() throws IOException { int c; while ((c = source().peek()) != -1 && c != '>' && c != '/') { // in addition to stopping when we find / or >, we also want // to stop when we find endstream or endobj. if (isNextToken(ENDOBJ, ENDSTREAM)) { LOG.warn( "Found unexpected 'endobj or 'endstream' at position {}, assuming end of dictionary", position()); return false; } source().read(); } return c != -1; } /** * @return The next parsed array object from the stream. Array objects are defined in Chap 7.3.6 of PDF 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSArray nextArray() throws IOException { skipExpected('['); COSArray array = new COSArray(); skipSpaces(); int c; while (((c = source().peek()) != -1) && c != ']') { long position = position(); COSBase item = nextParsedToken(); if (item != null) { array.add(item); } else { // This could be an "endobj" or "endstream" which means we can assume that // the array has ended. if (isNextToken(ENDOBJ, ENDSTREAM)) { LOG.warn( "Found unexpected 'endobj or 'endstream' at position {}, assuming end of array", position); return array; } // the next token is "obj" and the latest two are two integer. We assume the array wasn't // correctly terminated and we read the object definition as part of the array. // We have to unread the latest two int and remove them from the array if (isNextToken(OBJ) && array.size() >= 2 && (array.getObject(array.size() - 1) instanceof COSInteger) && (array.getObject(array.size() - 2) instanceof COSInteger)) { unreadSpaces(); unreadUntilSpaces(); unreadSpaces(); unreadUntilSpaces(); array.removeLast(); array.removeLast(); LOG.warn( "Found unexpected object definition at position {}, assuming end of array", position); return array; } LOG.warn("Found invalid token while parsing array at {}", position); } skipSpaces(); } skipExpected(']'); return array; } /** * @return The next parsed boolean object from the stream. Boolean objects are defined in Chap 7.3.2 of PDF * 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSBoolean nextBoolean() throws IOException { char c = (char) source().peek(); if (c == 't') { skipExpected(Boolean.TRUE.toString()); return COSBoolean.TRUE; } skipExpected(Boolean.FALSE.toString()); return COSBoolean.FALSE; } /** * @return The next parsed numeric object from the stream. Numeric objects are defined in Chap 7.3.3 of PDF * 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSNumber nextNumber() throws IOException { return COSNumber.get(readNumber()); } /** * @return The next parsed null object from the stream. Null object is defined in Chap 7.3.9 of PDF 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSNull nextNull() throws IOException { skipExpected("null"); return COSNull.NULL; } /** * @return The next parsed name object from the stream. Name objects are defined in Chap 7.3.5 of PDF 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSName nextName() throws IOException { return COSName.getPDFName(readName()); } /** * @return The next parsed literal string object from the stream. Literal string objects are defined in Chap 7.3.4.2 * of PDF 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSString nextLiteralString() throws IOException { return COSString.newInstance(readLiteralString().getBytes(StandardCharsets.ISO_8859_1)); } /** * @return The next parsed hexadecimal string object from the stream. Hexadecimal string objects is defined in Chap * 7.3.4.3 of PDF 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSString nextHexadecimalString() throws IOException { return COSString.parseHex(readHexString()); } /** * @return The next parsed string object from the stream. String objects is defined in Chap 7.3.4 of PDF * 32000-1:2008 * @throws IOException If there is an error during parsing. */ public COSString nextString() throws IOException { char next = (char) source().peek(); switch (next) { case '(': return nextLiteralString(); case '<': return nextHexadecimalString(); default: throw new IOException(String.format("Expected '(' or '<' at offset %d but was '%c'", position(), next)); } } /** * This will read a COSStream from the input stream using length attribute within dictionary. If length attribute is * a indirect reference it is first resolved to get the stream length. This means we copy stream data without * testing for 'endstream' or 'endobj' and thus it is no problem if these keywords occur within stream. We require * 'endstream' to be found after stream data is read. * * @param dic dictionary that goes with this stream. * * @return parsed pdf stream. * * @throws IOException if an error occurred reading the stream, like problems with reading length attribute, stream * does not end with 'endstream' after data read, stream too short etc. */ public COSStream nextStream(COSDictionary streamDictionary) throws IOException { skipSpaces(); skipExpected(STREAM); int c = source().read(); while (isSpace(c)) { LOG.warn("Found unexpected space character after 'stream' keyword"); c = source().read(); } if (isCarriageReturn(c)) { c = source().read(); if (!isLineFeed(c)) { source().back(); LOG.warn("Couldn't find expected LF following CR after 'stream' keyword at " + position()); } } else if (!isLineFeed(c)) { source().back(); } final COSStream stream; long length = streamLength(streamDictionary); if (length > 0) { stream = new COSStream(streamDictionary, source(), position(), length); } else { stream = new COSStream(streamDictionary); } source().forward(stream.getFilteredLength()); if (!skipTokenIfValue(ENDSTREAM)) { if (isNextToken(ENDOBJ)) { LOG.warn("Expected 'endstream' at " + position() + " but was 'endobj'"); } } return stream; } /** * Retrieves the stream length. It gets it from the dictionary, if not present there it applies fallback strategy * searching for endstream or endobj keywords. * * @param streamDictionary * @return the length * @throws IOException */ private long streamLength(COSDictionary streamDictionary) throws IOException { long length = streamLengthFrom(streamDictionary); if (length <= 0) { LOG.info( "Using fallback strategy reading until 'endstream' or 'endobj' is found. Starting at offset " + position()); length = findStreamLength(); } return length; } /** * @param streamDictionary * @return the stream length if found in the dictionary. -1 if nothing is found or if the length is incorrect. * @throws IOException */ private long streamLengthFrom(COSDictionary streamDictionary) throws IOException { long start = position(); COSBase lengthBaseObj = streamDictionary.getItem(COSName.LENGTH); try { return doStreamLengthFrom(lengthBaseObj); } finally { position(start); } } private long doStreamLengthFrom(COSBase lengthBaseObj) throws IOException { long startingOffset = position(); if (lengthBaseObj == null) { LOG.warn("Invalid stream length. No length provided"); return -1; } COSBase retVal = lengthBaseObj.getCOSObject(); if (!(retVal instanceof COSNumber)) { throw new IOException("Invalid stream length. Expected number instance but was " + retVal.getClass().getSimpleName()); } long length = ((COSNumber) retVal).longValue(); long endStreamOffset = startingOffset + length; if (endStreamOffset > length()) { LOG.warn("Invalid stream length. Out of range"); return -1; } position(endStreamOffset); if (!isNextToken(ENDSTREAM)) { LOG.warn("Invalid stream length. Expected '" + ENDSTREAM + "' at " + endStreamOffset); return -1; } return length; } /** * Reads from the current position until it finds the "endstream" meaning we're at the end of this stream object. * Some pdf files, however, forget to write some endstream tags and just close off objects with an "endobj" tag so * we have to handle this case as well. * * @return the length from the current position to the position where "endstream" or "endobj" was found * @throws IOException */ private long findStreamLength() throws IOException { long start = position(); try { return doFindStreamLength(start); } finally { position(start); } } private long doFindStreamLength(long start) throws IOException { Pattern pattern = Pattern.compile("endstream|endobj"); while (true) { long currentPosition = position(); String currentLine = readLine(); Matcher matcher = pattern.matcher(currentLine); if (matcher.find()) { position(currentPosition + matcher.start()); long length = position() - start; int prevChar = source().back().peek(); if (isCarriageReturn(prevChar)) { return length - 1; } if (isLineFeed(prevChar)) { prevChar = source().back().peek(); if (isCarriageReturn(prevChar)) { return length - 2; } return length - 1; } return length; } } } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/COSParser.java000066400000000000000000000121301320103431700244700ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static org.sejda.sambox.util.CharUtils.isDigit; import java.io.IOException; import java.nio.charset.StandardCharsets; import org.sejda.io.SeekableSource; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Parser for COS objects capable of handling indirect references. * * @author Andrea Vacondio */ class COSParser extends BaseCOSParser { private static final Logger LOG = LoggerFactory.getLogger(COSParser.class); private IndirectObjectsProvider provider; COSParser(SeekableSource source) { super(source); this.provider = new LazyIndirectObjectsProvider().initializeWith(this); } COSParser(SeekableSource source, IndirectObjectsProvider provider) { super(source); this.provider = provider; } @Override public COSBase nextParsedToken() throws IOException { skipSpaces(); char c = (char) source().peek(); switch (c) { case '<': { source().read(); c = (char) source().peek(); source().back(); if (c == '<') { return nextDictionary(); } return nextHexadecimalString(); } case '[': return nextArray(); case '(': return nextLiteralString(); case '/': return nextName(); case 'n': return nextNull(); case 't': case 'f': return nextBoolean(); case '.': case '-': case '+': return nextNumber(); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return nextNumberOrIndirectReference(); case (char) -1: return null; default: { String badString = readToken(); // if it's an endstream/endobj, we want to put it back so the caller will see it if (ENDOBJ.equals(badString) || ENDSTREAM.equals(badString) || OBJ.equals(badString)) { source().back(badString.getBytes(StandardCharsets.ISO_8859_1).length); } else { LOG.warn(String.format("Unknown token with value '%s' ending at offset %d", badString, position())); if (badString.length() <= 0) { // we are at a zero length end of token, we try to skip it and hopefully recover source().read(); } } } } return null; } /** * @return The next parsed numeric object from the stream or the next indirect object in case the number is an * object number . Numeric objects are defined in Chap 7.3.3 of PDF 32000-1:2008. Indirect objects are defined in * Chap 7.3.10 of PDF 32000-1:2008 * @throws IOException If there is an error during parsing. * @see #nextNumber() */ public COSBase nextNumberOrIndirectReference() throws IOException { String first = readNumber(); long offset = position(); skipSpaces(); if (isDigit(source().peek())) { String second = readIntegerNumber(); skipSpaces(); if ('R' == source().read()) { try { return new ExistingIndirectCOSObject(Long.parseLong(first), Integer.parseInt(second), provider); } catch (NumberFormatException nfe) { throw new IOException(String.format( "Unable to parse an object indirect reference with object number '%s' and generation number '%s'", first, second), nfe); } } } position(offset); return COSNumber.get(first); } public IndirectObjectsProvider provider() { return provider; } /** * Closes the parser but not the associated provider */ @Override public void close() throws IOException { super.close(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/ContentStreamCOSParser.java000066400000000000000000000066621320103431700272140ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import java.io.IOException; import org.sejda.io.SeekableSource; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.cos.COSNull; /** * A component capable of parsing COS objects in a content stream as defined in in Chap 7.8.2 of PDF 32000-1:2008, where * "Indirect objects and object references shall not be permitted at all". * * @author Andrea Vacondio */ class ContentStreamCOSParser extends BaseCOSParser { ContentStreamCOSParser(SeekableSource source) { super(source); } /** * @return The next parsed basic type object from the stream or null if the next token is not a COSBase. Basic types * are defined in Chap 7.3 of PDF 32000-1:2008 except for Chap 7.3.10. This method doesn't parse indirect objects * definition because they are not permitted in contest stream. * @throws IOException If there is an error during parsing. */ @Override public COSBase nextParsedToken() throws IOException { String token; skipSpaces(); char c = (char) source().peek(); switch (c) { case '<': { source().read(); c = (char) source().peek(); source().back(); if (c == '<') { return nextDictionary(); } return nextHexadecimalString(); } case '[': return nextArray(); case '(': return nextLiteralString(); case '/': return nextName(); case 'n': token = readToken(); if ("null".equals(token)) { return COSNull.NULL; } return null; case 't': case 'f': token = readToken(); if ("true".equals(token)) { return COSBoolean.TRUE; } if ("false".equals(token)) { return COSBoolean.FALSE; } return null; case '.': case '-': case '+': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return nextNumber(); case (char) -1: return null; default: String badString = readToken(); if (badString.length() <= 0) { // we are at a zero length end of token, we try to skip it and hopefully recover source().read(); } return null; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/ContentStreamParser.java000066400000000000000000000172211320103431700266400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static org.sejda.sambox.contentstream.operator.Operator.BI_OPERATOR; import static org.sejda.sambox.contentstream.operator.Operator.ID_OPERATOR; import static org.sejda.sambox.util.CharUtils.ASCII_SPACE; import static org.sejda.sambox.util.CharUtils.isEOF; import static org.sejda.sambox.util.CharUtils.isEOL; import static org.sejda.sambox.util.CharUtils.isNul; import static org.sejda.sambox.util.CharUtils.isSpace; import static org.sejda.sambox.util.CharUtils.isWhitespace; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.io.SeekableSource; import org.sejda.io.SeekableSources; import org.sejda.sambox.contentstream.PDContentStream; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.util.IOUtils; /** * Component responsible for parsing a a content stream to extract operands and such. * * @author Andrea Vacondio */ public class ContentStreamParser extends SourceReader { private ContentStreamCOSParser cosParser; private List tokens = new ArrayList<>(); public ContentStreamParser(PDContentStream stream) throws IOException { this(SeekableSources.inMemorySeekableSourceFrom(stream.getContents())); } public ContentStreamParser(SeekableSource source) { super(source); this.cosParser = new ContentStreamCOSParser(source()); } /** * @return a list of tokens retrieved parsing the source this parser was created from. * @throws IOException */ public List tokens() throws IOException { tokens.clear(); Object token; while ((token = nextParsedToken()) != null) { tokens.add(token); } return Collections.unmodifiableList(tokens); } /** * @return the next token parsed from the content stream * @throws IOException */ public Object nextParsedToken() throws IOException { skipSpaces(); long pos = position(); COSBase token = cosParser.nextParsedToken(); if (token != null) { return token; } position(pos); return nextOperator(); } private Object nextOperator() throws IOException { if ('B' == (char) source().peek()) { Operator operator = Operator.getOperator(readToken()); if (BI_OPERATOR.equals(operator.getName())) { nextInlineImage(operator); } return operator; } return Optional.ofNullable(readToken()).filter(s -> s.length() > 0) .map(Operator::getOperator).orElse(null); } private void nextInlineImage(Operator operator) throws IOException { COSDictionary imageParams = new COSDictionary(); operator.setImageParameters(imageParams); COSBase nextToken = null; long position = position(); while ((nextToken = cosParser.nextParsedToken()) instanceof COSName) { imageParams.setItem((COSName) nextToken, cosParser.nextParsedToken()); position = position(); } position(position); operator.setImageData(nextImageData()); } /** * Reads data until it finds an "EI" operator followed by a whitespace. * * @return the image data * @throws IOException */ private byte[] nextImageData() throws IOException { skipSpaces(); skipExpected(ID_OPERATOR); if (!isWhitespace(source().read())) { source().back(); } try (FastByteArrayOutputStream imageData = new FastByteArrayOutputStream()) { int current; while ((current = source().read()) != -1) { long position = source().position(); if ((current == 'E' && isEndOfImageFrom(position - 1)) || (isWhitespace(current) && isEndOfImageFrom(position))) { break; } imageData.write(current); } return imageData.toByteArray(); } } private boolean isEndOfImageFrom(long position) throws IOException { long currentPosition = source().position(); source().position(position); int current = source().read(); if (current == 'E') { current = source().read(); // if not a EI we restore the position and go on if (current == 'I' && (isEndOfImage() || isEOF(source().peek()))) { return true; } } source().position(currentPosition); return false; } private boolean isEndOfImage() throws IOException { long currentPosition = source().position(); try { int current = source().read(); // we do what PDF.js does if (isSpace(current) || isEOL(current)) { // from PDF.js: Let's check the next ten bytes are ASCII... just be sure. for (int i = 0; i < 10; i++) { current = source().read(); if (isNul(current) && !isNul(source().peek())) { // from PDF.js: NUL bytes are not supposed to occur *outside* of inline // images, but some PDF generators violate that assumption, // thus breaking the EI detection heuristics used below. // // However, we can't unconditionally treat NUL bytes as "ASCII", // since that *could* result in inline images being truncated. // // To attempt to address this, we'll still treat any *sequence* // of NUL bytes as non-ASCII, but for a *single* NUL byte we'll // continue checking the `followingBytes` (fixes issue8823.pdf). continue; } if (!isEOF(current) && !isEOL(current) && (current < ASCII_SPACE || current > 0x7F)) { // from PDF.js: Not a LF, CR, SPACE or any visible ASCII character, i.e. it's binary stuff. return false; } } return true; } return false; } finally { source().position(currentPosition); } } @Override public void close() throws IOException { super.close(); IOUtils.closeQuietly(cosParser); } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/ExistingIndirectCOSObject.java000066400000000000000000000052431320103431700276460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import java.io.IOException; import java.util.Optional; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.cos.COSVisitor; import org.sejda.sambox.cos.DisposableCOSObject; import org.sejda.sambox.cos.IndirectCOSObjectIdentifier; /** * An indirect object belonging to an existing pdf document. Indirect objects are defined in Chap 7.3.10 of PDF * 32000-1:2008. The {@link COSBase} wrapped by an {@link ExistingIndirectCOSObject} is loaded on demand by querying the * associated {@link IndirectObjectsProvider} when the {@link ExistingIndirectCOSObject#getCOSObject()} is called. * * @author Andrea Vacondio */ public class ExistingIndirectCOSObject extends COSBase implements DisposableCOSObject { private IndirectCOSObjectIdentifier id; private IndirectObjectsProvider provider; ExistingIndirectCOSObject(long objectNumber, int generationNumber, IndirectObjectsProvider provider) { this.id = new IndirectCOSObjectIdentifier(new COSObjectKey(objectNumber, generationNumber), provider.id()); this.provider = provider; } @Override public COSBase getCOSObject() { COSBase baseObject = Optional.ofNullable(provider.get(id.objectIdentifier)) .orElse(COSNull.NULL); baseObject.idIfAbsent(id); return baseObject; } @Override public void releaseCOSObject() { provider.release(id.objectIdentifier); } @Override public void accept(COSVisitor visitor) throws IOException { getCOSObject().accept(visitor); } @Override public IndirectCOSObjectIdentifier id() { return id; } @Override public String toString() { return String.format("%s[%s]", super.toString(), id.toString()); } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/IncrementablePDDocument.java000066400000000000000000000264401320103431700273730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import static org.sejda.io.CountingWritableByteChannel.from; import static org.sejda.sambox.cos.DirectCOSObject.asDirectObject; import static org.sejda.sambox.util.SpecVersionUtils.V1_4; import static org.sejda.sambox.util.SpecVersionUtils.isAtLeast; import static org.sejda.util.RequireUtils.requireNotBlank; import static org.sejda.util.RequireUtils.requireNotNullArg; import static org.sejda.util.RequireUtils.requireState; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.WritableByteChannel; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.cos.DirectCOSObject; import org.sejda.sambox.cos.IndirectCOSObjectIdentifier; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.sambox.output.IncrementablePDDocumentWriter; import org.sejda.sambox.output.WriteOption; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.xref.FileTrailer; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Model for a document to be used to incrementally update an existing PDF file. Has info regarding PDF objects that * need to be replaced in the original document. * * @author Andrea Vacondio * */ public class IncrementablePDDocument implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(IncrementablePDDocument.class); private Map replacements = new HashMap<>(); private Set newIndirects = new HashSet<>(); private PDDocument incremented; public final COSParser parser; IncrementablePDDocument(PDDocument incremented, COSParser parser) { requireNotNullArg(incremented, "Incremented document cannot be null"); requireNotNullArg(parser, "COSParser cannot be null"); this.incremented = incremented; this.parser = parser; } public PDDocument incremented() { return incremented; } /** * @return the trailer of the incremented document */ public FileTrailer trailer() { return incremented.getDocument().getTrailer(); } /** * @return the incremented document as a stream to be written "as is" * @throws IOException */ public InputStream incrementedAsStream() throws IOException { parser.source().position(0); return parser.source().asInputStream(); } /** * @return the highest object reference in the document that is being incrementally updated */ public COSObjectKey highestExistingReference() { return parser.provider().highestKey(); } /** * Replaces the object with the given {@link IndirectCOSObjectIdentifier} during the incremental update * * @param toReplace * @param replacement */ public void replace(IndirectCOSObjectIdentifier toReplace, COSObjectable replacement) { requireNotNullArg(toReplace, "Missing id of the object to be replaced"); replacements.put(toReplace, ofNullable(replacement).map(COSObjectable::getCOSObject).orElse(COSNull.NULL)); } /** * Adds the given object as modified, this object will be written as part of the incremental update. * * @param modified * @return true if the {@link COSBase} was added, false if not. In case where false is returned, the {@link COSBase} * doesn't have an id, meaning it's not written as indirect object in the original document but it's written as * direct object. In this case we have to call {@link IncrementablePDDocument#modified(COSBase)} on the first * indirect parent because incremental updates are meant to replace indirect references. */ public boolean modified(COSObjectable modified) { requireNotNullArg(modified, "Missing modified object"); if (modified.getCOSObject().hasId()) { replacements.put(modified.getCOSObject().id(), modified.getCOSObject()); return true; } return false; } /** * Adds the given object to the set of the new indirect objects. These objects will be written as new indirect * objects (with a new object number) as part of the incremental update. If, when writing the incremental update, a * new object that was not added using this method is found, it will be written as direct object and no indirect * reference will be created. * * @param newObject */ public void newIndirect(COSObjectable newObject) { requireNotNullArg(newObject, "Missing new object object"); newIndirects.add(newObject.getCOSObject()); } /** * @return a list of {@link IndirectCOSObjectReference} to be written as replacements for this incremental update */ public List replacements() { return replacements.entrySet().stream() .map(e -> new IndirectCOSObjectReference(e.getKey().objectIdentifier.objectNumber(), e.getKey().objectIdentifier.generation(), e.getValue().getCOSObject())) .collect(Collectors.toList()); } /** * @return a set of objects for which a new indirect reference should be created */ public Set newIndirects() { return Collections.unmodifiableSet(newIndirects); } /** * @return the encryption dictionary for the existing document */ public COSDictionary encryptionDictionary() { return incremented.getDocument().getEncryptionDictionary(); } /** * @return the encryption key, if the incremented document is encrypted, null otherwise. */ public byte[] encryptionKey() { return ofNullable(incremented.getSecurityHandler()).map(s -> s.getEncryptionKey()) .orElse(null); } @Override public void close() throws IOException { incremented.close(); IOUtils.close(parser.provider()); IOUtils.close(parser); } /** * Writes the document to the given {@link File}. The document is closed once written. * * @param file * @param options * @throws IOException */ public void writeTo(File file, WriteOption... options) throws IOException { writeTo(from(file), options); } /** * Writes the document to the file corresponding the given file name. The document is closed once written. * * @param filename * @param options * @throws IOException */ public void writeTo(String filename, WriteOption... options) throws IOException { writeTo(from(filename), options); } /** * Writes the document to the given {@link WritableByteChannel}. The document is closed once written. * * @param channel * @param options * @throws IOException */ public void writeTo(WritableByteChannel channel, WriteOption... options) throws IOException { writeTo(from(channel), options); } /** * Writes the document to the given {@link OutputStream}. The document is closed once written. * * @param out * @param options * @throws IOException */ public void writeTo(OutputStream out, WriteOption... options) throws IOException { writeTo(from(out), options); } private void writeTo(CountingWritableByteChannel output, WriteOption... options) throws IOException { requireState(incremented.isOpen(), "The document is closed"); requireState(!replacements.isEmpty(), "No update to be incrementally written"); updateId(output.toString().getBytes(StandardCharsets.ISO_8859_1)); try (IncrementablePDDocumentWriter writer = new IncrementablePDDocumentWriter(output, options)) { writer.write(this); } finally { IOUtils.close(this); } } /** * Updates the file identifier as defined in the chap 14.4 PDF 32000-1:2008 * * @param bytes */ private void updateId(byte[] bytes) { DirectCOSObject id = asDirectObject(incremented.generateFileIdentifier(bytes)); COSArray existingId = incremented.getDocument().getTrailer().getCOSObject() .getDictionaryObject(COSName.ID, COSArray.class); if (nonNull(existingId) && existingId.size() == 2) { ((COSString) existingId.get(0).getCOSObject()).encryptable(false); existingId.set(1, id); } else { incremented.getDocument().getTrailer().getCOSObject().setItem(COSName.ID, asDirectObject(new COSArray(id, id))); } } /** * Sets the version for this document if not at the minimum version required * * @param version */ public void requireMinVersion(String version) { if (!isAtLeast(incremented.getVersion(), version)) { LOG.debug("Minimum spec version required is {}", version); setVersion(version); } } public void setVersion(String newVersion) { requireState(incremented.isOpen(), "The document is closed"); requireNotBlank(newVersion, "Spec version cannot be blank"); int compare = incremented.getVersion().compareTo(newVersion); if (compare > 0) { LOG.info("Spec version downgrade not allowed"); } else if (compare < 0) { if (isAtLeast(newVersion, V1_4)) { COSDictionary catalog = incremented.getDocument().getCatalog(); catalog.setName(COSName.VERSION, newVersion); modified(catalog); } else { LOG.warn( "Sepc version must be at least 1.4 to be set as catalog entry in an incremental update"); } } } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/IndirectObjectsProvider.java000066400000000000000000000063271320103431700274700ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import java.io.Closeable; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.pdmodel.encryption.SecurityHandler; import org.sejda.sambox.xref.Xref; import org.sejda.sambox.xref.XrefEntry; /** * Component providing {@link COSBase} objects for given keys. It's used when an indirect reference is asked to resolve * to the actual COS object. This component is populated during the xref parsing process by adding {@link XrefEntry}s * found in the xref table/stream, it's then initialized with {@link COSParser} to use to parse and retrieve requested * objects and the {@link SecurityHandler} required (if any) to decrypt streams and string. * * @author Andrea Vacondio * */ interface IndirectObjectsProvider extends Closeable { /** * @param key * @return the {@link COSBase} corresponding to the given key. */ COSBase get(COSObjectKey key); /** * Signals that the object corresponding to the given key is no longer needed and can be released * * @param key */ void release(COSObjectKey key); /** * Adds the given xref entry to the {@link Xref} if absent * * @param entry * @return null if the entry was added. The current entry with the given object number and generation if the entry * was already present. * @see Xref#addIfAbsent(XrefEntry) */ XrefEntry addEntryIfAbsent(XrefEntry entry); /** * Adds the given xref entry to the {@link Xref} * * @param entry * @return the previous value or null if no entry was previously associated to the given object number and * generation. * @see Xref#add(XrefEntry) */ XrefEntry addEntry(XrefEntry entry); /** * Initialize the component with the {@link COSParser} to use to retrieve and parse requested object * * @param parser * @return this provider */ IndirectObjectsProvider initializeWith(COSParser parser); /** * Initialize the component with the {@link SecurityHandler} to decrypt streams and strings. * * @param handler * @return this provider */ IndirectObjectsProvider initializeWith(SecurityHandler handler); /** * @return the highest key (object number + generation number) for this provider. */ COSObjectKey highestKey(); /** * @return the unique id for the provider. */ String id(); } sambox-1.1.19/src/main/java/org/sejda/sambox/input/LazyIndirectObjectsProvider.java000066400000000000000000000264661320103431700303360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static org.sejda.sambox.input.BaseCOSParser.ENDOBJ; import static org.sejda.sambox.input.BaseCOSParser.ENDSTREAM; import static org.sejda.sambox.input.BaseCOSParser.STREAM; import static org.sejda.sambox.input.SourceReader.OBJ; import static org.sejda.util.RequireUtils.requireIOCondition; import java.io.Closeable; import java.io.IOException; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.encryption.SecurityHandler; import org.sejda.sambox.xref.CompressedXrefEntry; import org.sejda.sambox.xref.Xref; import org.sejda.sambox.xref.XrefEntry; import org.sejda.sambox.xref.XrefType; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A lazy implementation of the {@link IndirectObjectsProvider} that retrieves {@link COSBase} objects parsing the * underlying source on demand (ie. when the {@link IndirectObjectsProvider#get(COSObjectKey)} method is called). Parsed * objects are stored in a cache to be reused. If for given a {@link COSObjectKey} no entry is found in the xref, a * fallback mechanism is activated performing a full scan of the document to retrieve all the objects defined in it. * * @author Andrea Vacondio */ class LazyIndirectObjectsProvider implements IndirectObjectsProvider { private static final Logger LOG = LoggerFactory.getLogger(LazyIndirectObjectsProvider.class); private Xref xref = new Xref(); private ObjectsFullScanner scanner; // TODO references that the GC can claim private Map store = new ConcurrentHashMap<>(); private SecurityHandler securityHandler = null; private COSParser parser; @Override public COSBase get(COSObjectKey key) { if (isNull(store.get(key))) { parseObject(key); } return store.get(key); } @Override public void release(COSObjectKey key) { store.remove(key); } @Override public XrefEntry addEntryIfAbsent(XrefEntry entry) { XrefEntry retVal = xref.addIfAbsent(entry); if (retVal == null) { LOG.trace("Added xref entry {}", entry); } return retVal; } @Override public XrefEntry addEntry(XrefEntry entry) { LOG.trace("Added xref entry {}", entry); return xref.add(entry); } @Override public COSObjectKey highestKey() { return xref.highestKey(); } @Override public LazyIndirectObjectsProvider initializeWith(COSParser parser) { requireNonNull(parser); this.parser = parser; this.scanner = new ObjectsFullScanner(parser); return this; } @Override public LazyIndirectObjectsProvider initializeWith(SecurityHandler handler) { this.securityHandler = handler; return this; } private void parseObject(COSObjectKey key) { XrefEntry xrefEntry = xref.get(key); if (nonNull(xrefEntry)) { try { doParse(xrefEntry); } catch (IOException e) { LOG.warn("An error occurred while parsing " + xrefEntry, e); doParseFallbackObject(key); } } else { LOG.warn("Unable to find xref data for {}", key); doParseFallbackObject(key); } } private void doParseFallbackObject(COSObjectKey key) { LOG.info("Trying fallback strategy for " + key); XrefEntry xrefEntry = scanner.entries().get(key); if (nonNull(xrefEntry)) { try { doParse(xrefEntry); } catch (IOException e) { LOG.warn("Unable to find fallback xref entry for " + key, e); } } else { LOG.warn("Unable to find fallback xref entry for " + key); } } private void doParse(XrefEntry xrefEntry) throws IOException { LOG.trace("Parsing indirect object {}", xrefEntry); if (xrefEntry.getType() == XrefType.IN_USE) { parseInUseEntry(xrefEntry); } if (xrefEntry.getType() == XrefType.COMPRESSED) { parseCompressedEntry(xrefEntry); } LOG.trace("Parsing done"); } private void parseInUseEntry(XrefEntry xrefEntry) throws IOException { parser.position(xrefEntry.getByteOffset()); parser.skipExpectedIndirectObjectDefinition(xrefEntry.key()); parser.skipSpaces(); COSBase found = parser.nextParsedToken(); parser.skipSpaces(); if (parser.isNextToken(STREAM)) { requireIOCondition(found instanceof COSDictionary, "Found stream with missing dictionary"); found = parser.nextStream((COSDictionary) found); if (parser.skipTokenIfValue(ENDSTREAM)) { LOG.warn("Found double 'endstream' token for {}", xrefEntry); } } if (securityHandler != null) { LOG.trace("Decrypting entry {}", xrefEntry); securityHandler.decrypt(found, xrefEntry.getObjectNumber(), xrefEntry.getGenerationNumber()); } if (!parser.skipTokenIfValue(ENDOBJ)) { LOG.warn("Missing 'endobj' token for {}", xrefEntry); } store.put(xrefEntry.key(), ofNullable(found).orElse(COSNull.NULL)); } private void parseCompressedEntry(XrefEntry xrefEntry) throws IOException { XrefEntry containingStreamEntry = xref.get( new COSObjectKey(((CompressedXrefEntry) xrefEntry).getObjectStreamNumber(), 0)); requireIOCondition( nonNull(containingStreamEntry) && containingStreamEntry.getType() != XrefType.COMPRESSED, "Expected an uncompressed indirect object reference for the ObjectStream"); parseObject(containingStreamEntry.key()); COSBase stream = ofNullable(store.get(containingStreamEntry.key())) .map(COSBase::getCOSObject).orElseThrow(() -> new IOException( "Unable to find ObjectStream " + containingStreamEntry)); if (!(stream instanceof COSStream)) { throw new IOException( "Expected an object stream instance for " + containingStreamEntry); } parseObjectStream(containingStreamEntry, (COSStream) stream); } private void parseObjectStream(XrefEntry containingStreamEntry, COSStream stream) throws IOException { try (COSParser streamParser = new COSParser(stream.getUnfilteredSource(), this)) { requireIOCondition( !isIndirectContainedIn(stream.getItem(COSName.N), containingStreamEntry), "Objects stream size cannot be store as indirect reference in the ObjStm itself"); int numberOfObjects = stream.getInt(COSName.N); requireIOCondition(numberOfObjects >= 0, "Missing or negative required objects stream size"); requireIOCondition( !isIndirectContainedIn(stream.getItem(COSName.FIRST), containingStreamEntry), "Objects stream first offset cannot be store as indirect reference in the ObjStm itself"); long firstOffset = stream.getLong(COSName.FIRST); requireIOCondition(firstOffset >= 0, "Missing or negative required bytes offset of the fist object in the objects stream"); Map entries = new TreeMap<>(); for (int i = 0; i < numberOfObjects; i++) { long number = streamParser.readObjectNumber(); long offset = firstOffset + streamParser.readLong(); entries.put(offset, number); } LOG.trace("Found {} entries in object stream of size {}", entries.size(), streamParser.source().size()); for (Entry entry : entries.entrySet()) { LOG.trace("Parsing compressed object {} at offset {}", entry.getValue(), entry.getKey()); streamParser.position(entry.getKey()); if (streamParser.skipTokenIfValue(OBJ)) { LOG.warn("Unexptected 'obj' token in objects stream"); } COSBase object = streamParser.nextParsedToken(); if (object != null) { COSObjectKey key = new COSObjectKey(entry.getValue(), 0); // make sure the xref points to this copy of the object and not one in another more recent stream if (containingStreamEntry.owns(xref.get(key))) { LOG.trace("Parsed compressed object {} {}", key, object.getClass()); store.put(key, object); } } if (streamParser.skipTokenIfValue(ENDOBJ)) { LOG.warn("Unexptected 'endobj' token in objects stream"); } } } IOUtils.close(stream); } private boolean isIndirectContainedIn(COSBase item, XrefEntry containingStreamEntry) { if (item instanceof ExistingIndirectCOSObject) { return ofNullable(item.id()).map(i -> i.objectIdentifier).map(xref::get) .filter(e -> e instanceof CompressedXrefEntry).map(e -> (CompressedXrefEntry) e) .map(e -> containingStreamEntry.key().objectNumber() == e .getObjectStreamNumber()) .orElse(Boolean.FALSE); } return false; } @Override public void close() { store.values().stream().filter(o -> o instanceof Closeable).map(o -> (Closeable) o) .forEach(IOUtils::closeQuietly); store.clear(); } @Override public String id() { return parser.source().id(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/ObjectsFullScanner.java000066400000000000000000000075101320103431700264230ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.sejda.sambox.xref.Xref; import org.sejda.sambox.xref.XrefEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Component performing a full scan of the document and retrieving objects definition and the corresponding offset. This * implementation is lazy and the full scan is performed the first time the entries are accessed. * * @author Andrea Vacondio */ class ObjectsFullScanner { private static final Logger LOG = LoggerFactory.getLogger(ObjectsFullScanner.class); private static final Pattern OBJECT_DEF_PATTERN = Pattern.compile("^(\\d+)[\\s](\\d+)[\\s]obj"); private Xref xref = new Xref(); private SourceReader reader; private boolean scanned = false; ObjectsFullScanner(SourceReader reader) { requireNotNullArg(reader, "Cannot read from a null reader"); this.reader = reader; } private void scan() { LOG.info("Performing full scan to retrieve objects"); try { long savedPos = reader.position(); reader.position(0); reader.skipSpaces(); while (reader.source().peek() != -1) { long offset = reader.position(); addEntryIfObjectDefinition(offset, reader.readLine()); reader.skipSpaces(); } reader.position(savedPos); } catch (IOException e) { LOG.error("An error occurred performing a full scan of the document", e); } } private void addEntryIfObjectDefinition(long offset, String line) throws IOException { Matcher matcher = OBJECT_DEF_PATTERN.matcher(line); if (matcher.find()) { xref.add(XrefEntry.inUseEntry(Long.parseUnsignedLong(matcher.group(1)), offset, Integer.parseUnsignedInt(matcher.group(2)))); onObjectDefinitionLine(offset, line); } else { onNonObjectDefinitionLine(offset, line); } } /** * Called when the the scanner has read a line which is not an object definition * * @param originalOffset offset from where the line was read * @param line * @throws IOException */ protected void onNonObjectDefinitionLine(long originalOffset, String line) throws IOException { // nothing } /** * Called when the the scanner has read a line which is an object definition * * @param originalOffset offset from where the line was read * @param line * @throws IOException */ protected void onObjectDefinitionLine(long originalOffset, String line) throws IOException { // nothing } /** * @return the scanned entries */ Xref entries() { if (!scanned) { this.scanned = true; scan(); } return xref; } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/PDFParser.java000066400000000000000000000164201320103431700244630ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static java.util.Objects.requireNonNull; import static org.sejda.sambox.util.SpecVersionUtils.EXPECTED_HEADER_LENGTH; import static org.sejda.sambox.util.SpecVersionUtils.PDF_HEADER; import static org.sejda.sambox.util.SpecVersionUtils.parseHeaderString; import static org.sejda.util.RequireUtils.requireIOCondition; import java.io.IOException; import java.util.Optional; import org.sejda.io.SeekableSource; import org.sejda.sambox.cos.COSDocument; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.encryption.DecryptionMaterial; import org.sejda.sambox.pdmodel.encryption.PDEncryption; import org.sejda.sambox.pdmodel.encryption.SecurityHandler; import org.sejda.sambox.pdmodel.encryption.StandardDecryptionMaterial; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Provides public entry point to parse a {@link SeekableSource} and obtain a {@link PDDocument} or * {@link IncrementablePDDocument}. * * @author Andrea Vacondio */ public class PDFParser { private static final Logger LOG = LoggerFactory.getLogger(PDFParser.class); /** * Parses the given {@link SeekableSource} returning the corresponding {@link PDDocument}. * * @param source * @return the parsed document * @throws IOException */ public static PDDocument parse(SeekableSource source) throws IOException { return parse(source, (String) null); } /** * Parses the given {@link SeekableSource} returning the corresponding {@link IncrementablePDDocument}. * * @param source * @return the incrementable document * @throws IOException */ public static IncrementablePDDocument parseToIncrement(SeekableSource source) throws IOException { return parseToIncrement(source, (String) null); } /** * Parses the given {@link SeekableSource} using the given password, returning the corresponding decrypted * {@link PDDocument}. * * @param source {@link SeekableSource} to parse * @param password to be used for decryption. Optional. * @return the parsed document * @throws IOException */ public static PDDocument parse(SeekableSource source, String password) throws IOException { return parse(source, Optional.ofNullable(password).map(StandardDecryptionMaterial::new).orElse(null)); } /** * Parses the given {@link SeekableSource} using the given password, , returning the corresponding decrypted * {@link IncrementablePDDocument} to be used for an incremental update. * * @param source {@link SeekableSource} to parse * @param password to be used for decryption. Optional. * @return the incrementable document * @throws IOException */ public static IncrementablePDDocument parseToIncrement(SeekableSource source, String password) throws IOException { return parseToIncrement(source, Optional.ofNullable(password).map(StandardDecryptionMaterial::new).orElse(null)); } /** * Parses the given {@link SeekableSource} using the given {@link DecryptionMaterial}, returning the corresponding * decrypted {@link PDDocument}. * * @param source {@link SeekableSource} to parse * @param decryptionMaterial to be used for decryption. Optional. * @return the parsed document * @throws IOException */ public static PDDocument parse(SeekableSource source, DecryptionMaterial decryptionMaterial) throws IOException { requireNonNull(source); COSParser parser = new COSParser(source); PDDocument document = doParse(decryptionMaterial, parser); document.setOnCloseAction(() -> { IOUtils.close(parser.provider()); IOUtils.close(parser); }); return document; } /** * Parses the given {@link SeekableSource} using the given {@link DecryptionMaterial}, returning the corresponding * decrypted {@link IncrementablePDDocument} to be used for an incremental update. * * @param source {@link SeekableSource} to parse * @param decryptionMaterial to be used for decryption. Optional. * @return the incrementable document * @throws IOException */ public static IncrementablePDDocument parseToIncrement(SeekableSource source, DecryptionMaterial decryptionMaterial) throws IOException { requireNonNull(source); COSParser parser = new COSParser(source); return new IncrementablePDDocument(doParse(decryptionMaterial, parser), parser); } private static PDDocument doParse(DecryptionMaterial decryptionMaterial, COSParser parser) throws IOException { String headerVersion = readHeader(parser); LOG.trace("Parsed header version: " + headerVersion); XrefParser xrefParser = new XrefParser(parser); xrefParser.parse(); COSDocument document = new COSDocument(xrefParser.trailer(), headerVersion); if (document.isEncrypted()) { LOG.debug("Preparing for document decryption"); PDEncryption encryption = new PDEncryption(document.getEncryptionDictionary()); SecurityHandler securityHandler = encryption.getSecurityHandler(); securityHandler.prepareForDecryption(encryption, document.getDocumentID(), Optional .ofNullable(decryptionMaterial).orElse(new StandardDecryptionMaterial(""))); parser.provider().initializeWith(securityHandler); return new PDDocument(document, securityHandler); } return new PDDocument(document); } private static String readHeader(COSParser parser) throws IOException { parser.position(0); int headerIndex = -1; String header = parser.readLine(); while ((headerIndex = header.indexOf(PDF_HEADER)) < 0) { // we seach the header up to a certain point, then we fail requireIOCondition(parser.position() <= 1024, "Unable to find expected file header"); header = parser.readLine(); } final String trimmedLeftHeader = header.substring(headerIndex, header.length()) .replaceAll("\\s", ""); requireIOCondition(trimmedLeftHeader.length() >= EXPECTED_HEADER_LENGTH, "Unable to find expected header '%PDF-n.n'"); LOG.debug("Found header " + trimmedLeftHeader); return parseHeaderString(trimmedLeftHeader); } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/SourceReader.java000066400000000000000000000573521320103431700252710ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static java.util.Arrays.asList; import static org.sejda.sambox.util.CharUtils.ASCII_BACKSPACE; import static org.sejda.sambox.util.CharUtils.ASCII_CARRIAGE_RETURN; import static org.sejda.sambox.util.CharUtils.ASCII_FORM_FEED; import static org.sejda.sambox.util.CharUtils.ASCII_HORIZONTAL_TAB; import static org.sejda.sambox.util.CharUtils.ASCII_LINE_FEED; import static org.sejda.sambox.util.CharUtils.isCarriageReturn; import static org.sejda.sambox.util.CharUtils.isDigit; import static org.sejda.sambox.util.CharUtils.isEOL; import static org.sejda.sambox.util.CharUtils.isEndOfName; import static org.sejda.sambox.util.CharUtils.isHexDigit; import static org.sejda.sambox.util.CharUtils.isLineFeed; import static org.sejda.sambox.util.CharUtils.isOctalDigit; import static org.sejda.sambox.util.CharUtils.isWhitespace; import static org.sejda.util.RequireUtils.requireIOCondition; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.io.SeekableSource; import org.sejda.sambox.SAMBox; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.util.CharUtils; import org.sejda.sambox.util.Pool; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Component responsible for reading a {@link SeekableSource}. Methods to read expected kind of tokens are available as * well as methods to skip them. This implementation uses a pool of {@link StringBuilder}s to minimize garbage * collection. * * @author Andrea Vacondio */ class SourceReader implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(SourceReader.class); private static final long OBJECT_NUMBER_THRESHOLD = 10000000000L; private static final int GENERATION_NUMBER_THRESHOLD = 65535; public static final String OBJ = "obj"; private Pool pool = new Pool<>(StringBuilder::new, Integer.getInteger(SAMBox.BUFFERS_POOL_SIZE_PROPERTY, 10)).onGive(b -> { b.setLength(0); b.trimToSize(); }); private SeekableSource source; public SourceReader(SeekableSource source) { requireNotNullArg(source, "Cannot read a null source"); this.source = source; } /** * @return the source for this reader */ public SeekableSource source() { return source; } /** * @return the current position * @throws IOException * @see {@link SeekableSource#position()} */ public long position() throws IOException { return source.position(); } /** * seeks to the given offset * * @param offset the new offset * @throws IOException * @see {@link SeekableSource#position(long)} */ public void position(long offset) throws IOException { source.position(offset); } /** * @return the source length * @see {@link SeekableSource#size()} */ public long length() { return source.size(); } /** * Skips the expected given String * * @param expectedString the String value that is expected. * @throws IOException if the String char is not the expected value or if an I/O error occurs. */ public final void skipExpected(String expected) throws IOException { for (char c : expected.toCharArray()) { skipExpected(c); } } /** * Skips one char and throws an exception if it is not the expected value. * * @param ec the char value that is expected. * @throws IOException if the read char is not the expected value or if an I/O error occurs. */ public void skipExpected(char ec) throws IOException { char c = (char) source.read(); if (c != ec) { throw new IOException( "expected='" + ec + "' actual='" + c + "' at offset " + (position() - 1)); } } /** * Skips the next token if it's value is one of the given ones * * @param values the values to skip * @return true if the token is found and skipped, false otherwise. * @throws IOException if there is an error reading from the stream */ public boolean skipTokenIfValue(String... values) throws IOException { long pos = position(); String token = readToken(); if (!asList(values).contains(token)) { source.position(pos); return false; } return true; } /** * Skips an indirect object definition open tag (Ex. "12 0 obj") as defined in the chap 7.3.10 PDF 32000-1:2008. * * @throws IOException if we are reading a not valid indirect object definition open tag */ public void skipIndirectObjectDefinition() throws IOException { readObjectNumber(); readGenerationNumber(); skipSpaces(); skipExpected(OBJ); } /** * Skips an indirect object definition open tag (Ex. "12 0 obj") as defined in the chap 7.3.10 PDF 32000-1:2008. * * @param expected object we are expecting to find * @throws IOException if we are reading a not valid indirect object definition open tag or the object number or * generation number don't match the expected object */ public void skipExpectedIndirectObjectDefinition(COSObjectKey expected) throws IOException { long objNumOffset = position(); long number = readObjectNumber(); if (number != expected.objectNumber()) { throw new IOException( String.format("Expected '%d' object number at offset %d but was '%d'", expected.objectNumber(), objNumOffset, number)); } long genNumOffset = position(); long generation = readGenerationNumber(); if (generation != expected.generation()) { throw new IOException( String.format("Expected '%d' generation number at offset %d but was '%d'", expected.generation(), genNumOffset, number)); } skipSpaces(); skipExpected(OBJ); } /** * @return The next token that was read from the stream. * * @throws IOException If there is an error reading from the stream. * @see CharUtils#isEndOfName(int) */ public String readToken() throws IOException { skipSpaces(); StringBuilder builder = pool.borrow(); try { int c; while (((c = source.read()) != -1) && !isEndOfName(c)) { builder.append((char) c); } unreadIfValid(c); return builder.toString(); } finally { pool.give(builder); } } /** * Unreads white spaces * * @throws IOException */ public void unreadSpaces() throws IOException { int c; while ((c = source.peekBack()) != -1 && isWhitespace(c)) { source.back(); } } /** * Unreads characters until it finds a white space * * @throws IOException */ public void unreadUntilSpaces() throws IOException { int c; while ((c = source.peekBack()) != -1 && !isWhitespace(c)) { source.back(); } } /** * @param valid values for the next token. * @return true if the next token is one of the given values. false otherwise. * @throws IOException if there is an error reading from the stream */ public boolean isNextToken(String... values) throws IOException { long pos = position(); String token = readToken(); position(pos); return asList(values).contains(token); } /** * Reads bytes until the first end of line marker occurs. NOTE: The EOL marker may consists of 1 (CR or LF) or 2 (CR * and CL) bytes which is an important detail if one wants to unread the line. * * @return The characters between the current position and the end of the line. * @throws IOException If there is an error reading from the stream. */ public String readLine() throws IOException { requireIOCondition(source.peek() != -1, "Expected line but was end of file"); StringBuilder builder = pool.borrow(); try { int c; while ((c = source.read()) != -1 && !isEOL(c)) { builder.append((char) c); } if (isCarriageReturn(c) && isLineFeed(source.peek())) { source.read(); } return builder.toString(); } finally { pool.give(builder); } } /** * Reads a long and throws an {@link IOException} if the long value is negative or has more than 10 digits (i.e. : * bigger than {@link #OBJECT_NUMBER_THRESHOLD}) * * @return the object number being read. * @throws IOException if an I/O error occurs */ public long readObjectNumber() throws IOException { long retval = readLong(); if (retval < 0 || retval >= OBJECT_NUMBER_THRESHOLD) { throw new IOException( "Object Number '" + retval + "' has more than 10 digits or is negative"); } return retval; } /** * reads an integer and throws an {@link IOException} if the integer value has more than the maximum object revision * (i.e. : bigger than {@link #GENERATION_NUMBER_THRESHOLD}) * * @return the generation number being read. * @throws IOException if an I/O error occurs */ public int readGenerationNumber() throws IOException { int retval = readInt(); if (retval < 0 || retval > GENERATION_NUMBER_THRESHOLD) { throw new IOException("Generation Number '" + retval + "' has more than 5 digits"); } return retval; } /** * Reads a token conforming with PDF Name Objects chap 7.3.5 PDF 32000-1:2008. * * @return the generation number being read. * @throws IOException if an I/O error occurs */ public String readName() throws IOException { skipExpected('/'); FastByteArrayOutputStream buffer = new FastByteArrayOutputStream(); int i; while (((i = source.read()) != -1) && !isEndOfName(i)) { if (i == '#') { int ch1 = source.read(); int ch2 = source.read(); requireIOCondition(ch2 != -1 && ch1 != -1, "Expected 2-digit hexadecimal code but was end of file"); // Prior to PDF v1.2, the # was not a special character. Also, // it has been observed that various PDF tools do not follow the // spec with respect to the # escape, even though they report // PDF versions of 1.2 or later. The solution here is that we // interpret the # as an escape only when it is followed by two // valid hex digits. // if (isHexDigit((char) ch1) && isHexDigit((char) ch2)) { String hex = "" + (char) ch1 + (char) ch2; i = Integer.parseInt(hex, 16); } else { source.back(2); LOG.warn( "Found NUMBER SIGN (#) not used as escaping char while reading name at " + position()); } } buffer.write(i); } unreadIfValid(i); byte[] bytes = buffer.toByteArray(); try { StandardCharsets.UTF_8.newDecoder().decode(ByteBuffer.wrap(bytes)); } catch (CharacterCodingException e) { return new String(bytes, Charset.forName("Windows-1252")); } return new String(bytes, StandardCharsets.UTF_8); } /** * @return The integer that was read from the stream. * @throws IOException If there is an error reading from the stream. */ public int readInt() throws IOException { String intBuffer = readIntegerNumber(); try { return Integer.parseInt(intBuffer); } catch (NumberFormatException e) { source.back(intBuffer.getBytes(StandardCharsets.ISO_8859_1).length); throw new IOException( String.format("Expected an integer type at offset %d but was '%s'", position(), intBuffer), e); } } /** * @return The long that was read from the stream. * @throws IOException If there is an error reading from the stream. */ public long readLong() throws IOException { String longBuffer = readIntegerNumber(); try { return Long.parseLong(longBuffer); } catch (NumberFormatException e) { source.back(longBuffer.getBytes(StandardCharsets.ISO_8859_1).length); throw new IOException(String.format("Expected a long type at offset %d but was '%s'", position(), longBuffer), e); } } /** * Reads a a token conforming with a PDF Integer object defined in Numeric Objects chap 7.3.3 PDF 32000-1:2008. * * @return the token to parse as {@link Integer} or {@link Long}. * @throws IOException If there is an error reading from the stream. */ public final String readIntegerNumber() throws IOException { skipSpaces(); StringBuilder builder = pool.borrow(); try { int c = source.read(); if (c != -1 && (isDigit(c) || c == '+' || c == '-')) { builder.append((char) c); while ((c = source.read()) != -1 && isDigit(c)) { builder.append((char) c); } } unreadIfValid(c); return builder.toString(); } finally { pool.give(builder); } } /** * Reads a token conforming with PDF Numeric Objects chap 7.3.3 PDF 32000-1:2008. * * @return the token to parse as integer or real number. * @throws IOException If there is an error reading from the stream. */ public final String readNumber() throws IOException { StringBuilder builder = pool.borrow(); try { int c = source.read(); if (c != -1 && (isDigit(c) || c == '+' || c == '-' || c == '.')) { builder.append((char) c); while ((c = source.read()) != -1 && (isDigit(c) || c == '.' || c == 'E' || c == 'e' || c == '+' || c == '-')) { builder.append((char) c); } } unreadIfValid(c); return builder.toString(); } finally { pool.give(builder); } } /** * Reads a token conforming with PDF Hexadecimal Strings chap 7.3.4.3 PDF 32000-1:2008. Any non hexadecimal char * found while parsing the token is replace with the default '0' hex char. * * @return the token to parse as an hexadecimal string * @throws IOException If there is an error reading from the stream. */ public final String readHexString() throws IOException { skipExpected('<'); StringBuilder builder = pool.borrow(); try { int c; while (((c = source.read()) != -1) && c != '>') { if (isHexDigit(c)) { builder.append((char) c); } else if (isWhitespace(c)) { continue; } else { // this differs from original PDFBox implementation. It replaces the wrong char with a default value // and goes on. LOG.warn(String.format( "Expected an hexadecimal char at offset %d but was '%c'. Replaced with default 0.", position() - 1, c)); builder.append('0'); } } requireIOCondition(c != -1, "Unexpected EOF. Missing closing bracket for hexadecimal string."); return builder.toString(); } finally { pool.give(builder); } } /** * Reads a token conforming with PDF Literal Strings chap 7.3.4.2 PDF 32000-1:2008. * * @return the token to parse as a literal string * @throws IOException If there is an error during parsing. */ public String readLiteralString() throws IOException { skipExpected('('); int bracesCounter = 1; StringBuilder builder = pool.borrow(); try { int i; while ((i = source.read()) != -1 && bracesCounter > 0) { char c = (char) i; switch (c) { case '(': bracesCounter++; builder.append(c); break; case ')': bracesCounter--; // TODO PDFBox 276 // this differs from the PDFBox 2.0.0 impl. // consider if we want to take care of this. Maybe investigate Acrobat to see how they do it if (bracesCounter > 0) { builder.append(c); } break; case '\\': { char next = (char) source.read(); switch (next) { case 'n': builder.append((char) ASCII_LINE_FEED); break; case 'r': builder.append((char) ASCII_CARRIAGE_RETURN); break; case 't': builder.append((char) ASCII_HORIZONTAL_TAB); break; case 'b': builder.append((char) ASCII_BACKSPACE); break; case 'f': builder.append((char) ASCII_FORM_FEED); break; case ')': // TODO PDFBox 276 // this differs from the PDFBox 2.0.0 impl. // consider if we want to take care of this. Maybe investigate Acrobat to see how they do it case '(': case '\\': builder.append(next); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { StringBuilder octal = pool.borrow(); try { octal.append(next); next = (char) source.read(); if (isOctalDigit(next)) { octal.append(next); next = (char) source.read(); if (isOctalDigit(next)) { octal.append(next); } else { unreadIfValid(next); } } else { unreadIfValid(next); } builder.append((char) Integer.parseInt(octal.toString(), 8)); } finally { pool.give(octal); } break; } case ASCII_LINE_FEED: case ASCII_CARRIAGE_RETURN: { // this is a break in the line so ignore it and the newline and continue while ((c = (char) source.read()) != -1 && isEOL(c)) { // NOOP } unreadIfValid(c); break; } default: // dropping the backslash unreadIfValid(c); } break; } case ASCII_LINE_FEED: builder.append((char) ASCII_LINE_FEED); break; case ASCII_CARRIAGE_RETURN: { builder.append((char) ASCII_LINE_FEED); if (!CharUtils.isLineFeed(source.read())) { unreadIfValid(c); } break; } default: builder.append(c); } } unreadIfValid(i); return builder.toString(); } finally { pool.give(builder); } } /** * Skips all spaces and comments that are present. * * @throws IOException If there is an error reading from the stream. */ public void skipSpaces() throws IOException { int c = source.read(); // 37 is the % character, a comment while (isWhitespace(c) || c == 37) { if (c == 37) { // skip past the comment section while ((c = source.read()) != -1 && !isEOL(c)) { // NOOP } } else { c = source.read(); } } unreadIfValid(c); } /** * Unreads the given character if it's not -1 * * @param c * @throws IOException */ public void unreadIfValid(int c) throws IOException { if (c != -1) { source.back(); } } /** * Closes the {@link SeekableSource} this reader was created from. */ @Override public void close() throws IOException { IOUtils.close(source); } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/XrefFullScanner.java000066400000000000000000000156521320103431700257440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static org.sejda.sambox.input.AbstractXrefTableParser.XREF; import java.io.IOException; import java.util.regex.Pattern; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.xref.FileTrailer; import org.sejda.sambox.xref.XrefEntry; import org.sejda.sambox.xref.XrefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Component scanning for xref tables/streams. It scans top to bottom parsing any xref table/stream found with the * assumption that xrefs found later in the file are more recent. * * @author Andrea Vacondio */ class XrefFullScanner { private static final Logger LOG = LoggerFactory.getLogger(XrefFullScanner.class); private FileTrailer trailer = new FileTrailer(); private AbstractXrefStreamParser xrefStreamParser; private AbstractXrefTableParser xrefTableParser; private COSParser parser; private Pattern objectDefPatter = Pattern.compile("^(\\d+)[\\s](\\d+)[\\s]obj"); private XrefScanOutcome outcome = XrefScanOutcome.NOT_FOUND; XrefFullScanner(COSParser parser) { this.parser = parser; this.xrefStreamParser = new AbstractXrefStreamParser(parser) { @Override void onTrailerFound(COSDictionary found) { trailer.getCOSObject().merge(found); } @Override void onEntryFound(XrefEntry entry) { addEntryIfValid(entry); } }; this.xrefTableParser = new AbstractXrefTableParser(parser) { @Override void onTrailerFound(COSDictionary found) { trailer.getCOSObject().merge(found); } @Override void onEntryFound(XrefEntry entry) { addEntryIfValid(entry); } }; } private void addEntryIfValid(XrefEntry entry) { if (isValidEntry(entry)) { parser.provider().addEntry(entry); } else { outcome = outcome.moveTo(XrefScanOutcome.WITH_ERRORS); } } /** * * @return the state of the scan */ XrefScanOutcome scan() { try { doScan(); } catch (Exception e) { outcome = outcome.moveTo(XrefScanOutcome.WITH_ERRORS); LOG.warn("An error occurred while performing full scan looking for xrefs", e); } return outcome; } private void doScan() throws IOException { LOG.info("Performing full scan looking for xrefs"); long savedPos = parser.position(); parser.position(0); parser.skipSpaces(); while (parser.source().peek() != -1) { long offset = parser.position(); String line = parser.readLine(); if (line.startsWith(XREF)) { outcome = outcome.moveTo(XrefScanOutcome.FOUND); parseFoundXrefTable(offset); } if (objectDefPatter.matcher(line).find()) { parseFoundObject(offset); } parser.skipSpaces(); } parser.position(savedPos); } private void parseFoundXrefTable(long offset) throws IOException { LOG.debug("Found xref table at " + offset); trailer.xrefOffset(offset); xrefTableParser.parse(offset); } FileTrailer trailer() { return trailer; } private void parseFoundObject(long offset) throws IOException { parser.position(offset); parser.skipIndirectObjectDefinition(); parser.skipSpaces(); COSBase found = parser.nextParsedToken(); if (found instanceof COSDictionary && COSName.XREF.equals(((COSDictionary) found).getItem(COSName.TYPE))) { LOG.debug("Found xref stream at " + offset); trailer.xrefOffset(offset); parseFoundXrefStream((COSDictionary) found); } } private void parseFoundXrefStream(COSDictionary trailer) throws IOException { outcome = outcome.moveTo(XrefScanOutcome.FOUND); try (COSStream xrefStream = parser.nextStream(trailer)) { xrefStreamParser.onTrailerFound(trailer); xrefStreamParser.parseStream(xrefStream); } LOG.debug("Done parsing xref stream"); } /** * @param entry * @return true if the given xref entry actually points to a valid object definition */ private boolean isValidEntry(XrefEntry entry) { if (entry.getType() == XrefType.IN_USE) { if (entry.getByteOffset() < 0) { return false; } try { long origin = parser.position(); try { parser.position(entry.getByteOffset()); parser.skipIndirectObjectDefinition(); return true; } catch (IOException e) { LOG.warn("Xref entry points to an invalid object definition {}", entry); } finally { parser.position(origin); } } catch (IOException e) { LOG.error("Unable to change source position", e); } return false; } return true; } /** * possible outcome of the xref full scan * * @author Andrea Vacondio * */ public static enum XrefScanOutcome { NOT_FOUND, FOUND, WITH_ERRORS { @Override XrefScanOutcome moveTo(XrefScanOutcome newState) { return this; } }; XrefScanOutcome moveTo(XrefScanOutcome newState) { if (newState != NOT_FOUND) { return newState; } return this; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/input/XrefParser.java000066400000000000000000000273541320103431700247660ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.input; import static java.util.Objects.nonNull; import static org.sejda.sambox.input.AbstractXrefTableParser.TRAILER; import static org.sejda.sambox.input.AbstractXrefTableParser.XREF; import static org.sejda.util.RequireUtils.requireIOCondition; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.input.XrefFullScanner.XrefScanOutcome; import org.sejda.sambox.xref.FileTrailer; import org.sejda.sambox.xref.XrefEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Component responsible for finding and parsing the xref chain (either tables and streams). In case of errors while * parsing the xref chain (Ex. invalid offset, bad dictionaries etc) it has a fallback mechanism performing a document * full scan searching for xrefs. When parsing the document, xref info are passed to the {@link COSParser} which will * use them to retrieve COS objects on demand. * * @author Andrea Vacondio * @see XrefFullScanner */ class XrefParser { private static final Logger LOG = LoggerFactory.getLogger(XrefParser.class); /** * How many trailing bytes to read for EOF marker. */ private static final int DEFAULT_TRAIL_BYTECOUNT = 2048; private static final String STARTXREF = "startxref"; private FileTrailer trailer = new FileTrailer(); private AbstractXrefStreamParser xrefStreamParser; private AbstractXrefTableParser xrefTableParser; private COSParser parser; public XrefParser(COSParser parser) { this.parser = parser; this.xrefStreamParser = new AbstractXrefStreamParser(parser) { @Override void onTrailerFound(COSDictionary found) { trailer.getCOSObject().mergeWithoutOverwriting(found); } @Override void onEntryFound(XrefEntry entry) { parser().provider().addEntryIfAbsent(entry); } }; this.xrefTableParser = new AbstractXrefTableParser(parser) { @Override void onTrailerFound(COSDictionary found) { trailer.getCOSObject().mergeWithoutOverwriting(found); } @Override void onEntryFound(XrefEntry entry) { parser().provider().addEntryIfAbsent(entry); } }; } /** * parse the xref using the given parser. * * @throws IOException */ public void parse() throws IOException { long xrefOffset = findXrefOffset(); if (xrefOffset <= 0 || !parseXref(xrefOffset)) { XrefFullScanner fallbackFullScanner = new XrefFullScanner(parser); XrefScanOutcome xrefScanStatus = fallbackFullScanner.scan(); if (xrefScanStatus != XrefScanOutcome.NOT_FOUND) { // something was found so we keep the trailer trailer = fallbackFullScanner.trailer(); } if (xrefScanStatus != XrefScanOutcome.FOUND) { // there were errors in the found xrefs so we perform objects full scan LOG.warn( "Xref full scan encountered some errors, now performing objects full scan"); ObjectsFullScanner objectsFullScanner = new ObjectsFullScanner(parser) { private long lastObjectOffset = 0; @Override protected void onNonObjectDefinitionLine(long offset, String line) throws IOException { if (nonNull(line)) { if (line.startsWith(TRAILER)) { LOG.debug("Parsing trailer at " + offset); parser.position(offset); parser.skipExpected(TRAILER); parser.skipSpaces(); trailer.getCOSObject().merge(parser.nextDictionary()); parser.skipSpaces(); } else if (line.contains(COSName.CATALOG.getName())) { long position = parser.position(); try { // we do our best to make sure we have a catalog even in corrupted docs LOG.debug("Parsing potential Catalog at " + lastObjectOffset); parser.position(lastObjectOffset); parser.skipIndirectObjectDefinition(); parser.skipSpaces(); COSDictionary possibleCatalog = parser.nextDictionary(); if (COSName.CATALOG .equals(possibleCatalog.getCOSName(COSName.TYPE))) { trailer.getCOSObject().putIfAbsent(COSName.ROOT, possibleCatalog); } parser.skipSpaces(); } catch (IOException e) { LOG.warn("Unable to parse potential Catalog", e); parser.position(position); } } else if (line.startsWith(XREF)) { LOG.debug("Found xref at " + offset); trailer.xrefOffset(offset); } } } @Override protected void onObjectDefinitionLine(long offset, String line) { lastObjectOffset = offset; } }; // and we consider it more reliable compared to what was found in the somehow broken xrefs objectsFullScanner.entries().values().stream().forEach(parser.provider()::addEntry); } } } /** * Looks for the startxref keyword within the latest {@link #DEFAULT_TRAIL_BYTECOUNT} bytes of the source. If found * it returns the Long read after the keyword, if not it returns -1. * * @return the xref offset or -1 if the startxref keyword is not found * @throws IOException If something went wrong. */ private final long findXrefOffset() throws IOException { int chunkSize = (int) Math.min(parser.length(), DEFAULT_TRAIL_BYTECOUNT); long startPosition = parser.length() - chunkSize; parser.position(startPosition); byte[] buffer = new byte[chunkSize]; parser.source().read(ByteBuffer.wrap(buffer)); int relativeIndex = new String(buffer, StandardCharsets.ISO_8859_1).lastIndexOf(STARTXREF); if (relativeIndex < 0) { LOG.warn("Unable to find 'startxref' keyword"); return -1; } try { parser.position(startPosition + relativeIndex + STARTXREF.length()); parser.skipSpaces(); long xrefOffset = parser.readLong(); LOG.debug("Found xref offset at " + xrefOffset); return xrefOffset; } catch (IOException e) { LOG.warn("An error occurred while parsing the xref offset", e); return -1; } } private boolean parseXref(long xrefOffset) { try { return doParseXref(xrefOffset); } catch (IOException e) { LOG.warn("An error occurred while parsing the xref, applying fallback strategy", e); return false; } } private boolean doParseXref(long xrefOffset) throws IOException { requireIOCondition(isValidXrefOffset(xrefOffset), "Offset '" + xrefOffset + "' doesn't point to an xref table or stream"); Set parsedOffsets = new HashSet<>(); long currentOffset = xrefOffset; while (currentOffset > -1) { requireIOCondition(!parsedOffsets.contains(currentOffset), "/Prev loop detected"); requireIOCondition(isValidXrefOffset(currentOffset), "Offset '" + currentOffset + "' doesn't point to an xref table or stream"); parser.position(currentOffset); parser.skipSpaces(); if (parser.isNextToken(XREF)) { COSDictionary trailer = xrefTableParser.parse(currentOffset); parsedOffsets.add(currentOffset); long streamOffset = trailer.getLong(COSName.XREF_STM); if (streamOffset > 0) { requireIOCondition(isValidXrefStreamOffset(streamOffset), "Offset '" + streamOffset + "' doesn't point to an xref stream"); xrefStreamParser.parse(streamOffset); } currentOffset = trailer.getLong(COSName.PREV); } else { COSDictionary streamDictionary = xrefStreamParser.parse(currentOffset); parsedOffsets.add(currentOffset); currentOffset = streamDictionary.getLong(COSName.PREV); } } trailer.xrefOffset(xrefOffset); return true; } /** * @param xrefOffset * @return true if the given offset points to an xref table or and xref stream * @throws IOException */ private boolean isValidXrefOffset(long xrefOffset) throws IOException { if (isValidXrefStreamOffset(xrefOffset)) { return true; } parser.position(xrefOffset); return parser.isNextToken(XREF); } /** * @param xrefStreamOffset * @return true if the given offset points to a valid xref stream * @throws IOException */ private boolean isValidXrefStreamOffset(long xrefStreamOffset) throws IOException { parser.position(xrefStreamOffset); try { parser.skipIndirectObjectDefinition(); parser.skipSpaces(); COSDictionary xrefStreamDictionary = parser.nextDictionary(); parser.position(xrefStreamOffset); return COSName.XREF.equals(xrefStreamDictionary.getCOSName(COSName.TYPE)); } catch (IOException exception) { return false; } } public FileTrailer trailer() { return this.trailer; } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/000077500000000000000000000000001320103431700222305ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/output/AsyncPDFBodyObjectsWriter.java000066400000000000000000000073571320103431700300430ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.IOException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of a PDFBodyObjectsWriter that asynchronously writes {@link IndirectCOSObjectReference}. Objects are * written submitting a task to a single thread executor service. * * @author Andrea Vacondio * */ class AsyncPDFBodyObjectsWriter implements PDFBodyObjectsWriter { private static final Logger LOG = LoggerFactory.getLogger(AsyncPDFBodyObjectsWriter.class); private ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable target) { return new Thread(null, target, "pdf-writer-thread", 0); } }); private AtomicReference executionException = new AtomicReference<>(); private IndirectObjectsWriter writer; AsyncPDFBodyObjectsWriter(IndirectObjectsWriter writer) { requireNotNullArg(writer, "Cannot write to a null writer"); this.writer = writer; } @Override public void writeObject(IndirectCOSObjectReference ref) throws IOException { assertCanSubmitAsyncTask(); executor.execute(() -> { try { if (executionException.get() == null) { writer.writeObjectIfNotWritten(ref); } } catch (IOException e) { executionException.set(e); } catch (Exception e) { executionException.set(new IOException(e)); } }); } private void assertCanSubmitAsyncTask() throws IOException { IOException previous = executionException.get(); if (previous != null) { executor.shutdownNow(); throw previous; } } @Override public void onWriteCompletion() throws IOException { assertCanSubmitAsyncTask(); try { executor.submit(() -> { IOException previous = executionException.get(); if (previous != null) { throw previous; } LOG.debug("Written document body"); return null; }).get(); } catch (InterruptedException e) { throw new IOException(e); } catch (ExecutionException e) { throw new IOException(e.getCause()); } } @Override public void close() { executor.shutdown(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/COSWriter.java000066400000000000000000000037411320103431700247210ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import java.io.Closeable; import java.io.IOException; import org.sejda.io.BufferedCountingChannelWriter; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSVisitor; import org.sejda.util.IOUtils; /** * Component capable of writing COS objects using a {@link BufferedCountingChannelWriter}. * * @author Andrea Vacondio */ interface COSWriter extends COSVisitor, Closeable { /** * writes the separator after a {@link COSArray} or a {@link COSDictionary} or a {@link COSStream} are written. * * @throws IOException */ default void writeComplexObjectSeparator() throws IOException { writer().writeEOL(); } /** * writes the separator after a {@link COSDictionary} value is written, before the next key/value is written. * * @throws IOException */ default void writeDictionaryItemsSeparator() throws IOException { writer().writeEOL(); } @Override default void close() throws IOException { IOUtils.close(writer()); } BufferedCountingChannelWriter writer(); } sambox-1.1.19/src/main/java/org/sejda/sambox/output/ContentStreamWriter.java000066400000000000000000000112201320103431700270520ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static org.sejda.sambox.contentstream.operator.Operator.BI_OPERATOR; import static org.sejda.sambox.contentstream.operator.Operator.EI_OPERATOR; import static org.sejda.sambox.contentstream.operator.Operator.ID_OPERATOR; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; import org.sejda.io.BufferedCountingChannelWriter; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.IndirectCOSObjectReference; /** * Component capable of writing a content stream tokens, {@link Operator}s and {@link COSBase} operands. * * @author Andrea Vacondio */ public class ContentStreamWriter extends DefaultCOSWriter { public ContentStreamWriter(CountingWritableByteChannel channel) { super(channel); } public ContentStreamWriter(BufferedCountingChannelWriter writer) { super(writer); } public void writeTokens(List tokens) throws IOException { for (Object token : tokens) { if (token instanceof COSBase) { ((COSBase) token).accept(this); writeSpace(); } else if (token instanceof Operator) { this.writeOperator((Operator) token); } else { throw new IOException("Unsupported type in content stream:" + token); } } } public void writeTokens(Operator... tokens) throws IOException { for (Operator token : tokens) { writeOperator(token); } } public void writeOperator(List operands, Operator operator) throws IOException { for (COSBase operand : operands) { operand.accept(this); writeSpace(); } this.writeOperator(operator); } /** * Writes the byte array as is as content of the stream. * * @param byteArray * @throws IOException */ public void writeContent(byte[] byteArray) throws IOException { writer().write(byteArray); } public void writeEOL() throws IOException { writer().writeEOL(); } public void writeSpace() throws IOException { writer().write(SPACE); } private void writeOperator(Operator token) throws IOException { writer().write(token.getName().getBytes(StandardCharsets.ISO_8859_1)); if (token.getName().equals(BI_OPERATOR)) { writeEOL(); COSDictionary imageParams = Optional.ofNullable(token.getImageParameters()) .orElseGet(COSDictionary::new); for (COSName key : imageParams.keySet()) { key.accept(this); writeSpace(); imageParams.getDictionaryObject(key).accept(this); writeEOL(); } writer().write(ID_OPERATOR.getBytes(StandardCharsets.US_ASCII)); writeEOL(); writer().write(token.getImageData()); writeEOL(); writer().write(EI_OPERATOR.getBytes(StandardCharsets.US_ASCII)); } writeEOL(); } @Override public void visit(COSStream value) { throw new UnsupportedOperationException("Cannot write a stream inside a stream"); } @Override public void visit(IndirectCOSObjectReference value) { throw new UnsupportedOperationException( "Cannot write an indirect object reference inside a stream"); } @Override public void writeComplexObjectSeparator() { // write nothing } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/DefaultCOSWriter.java000066400000000000000000000170211320103431700262220ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static java.util.Objects.isNull; import static org.sejda.sambox.util.CharUtils.isDigit; import static org.sejda.sambox.util.CharUtils.isLetter; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.Map; import java.util.Optional; import org.sejda.io.BufferedCountingChannelWriter; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.sambox.util.Hex; import org.sejda.util.IOUtils; /** * Default implementation of a {@link COSWriter} that writes COS objects to the given * {@link CountingWritableByteChannel} or {@link BufferedCountingChannelWriter} * * @author Andrea Vacondio */ class DefaultCOSWriter implements COSWriter { protected static final byte SPACE = 0x20; private static final byte[] CRLF = { '\r', '\n' }; private static final byte SOLIDUS = 0x2F; private static final byte REVERSE_SOLIDUS = 0x5C; private static final byte NUMBER_SIGN = 0x23; private static final byte LESS_THEN = 0x3C; private static final byte GREATER_THEN = 0x3E; private static final byte LEFT_PARENTHESIS = 0x28; private static final byte RIGHT_PARENTHESIS = 0x29; private static final byte LEFT_SQUARE_BRACKET = 0x5B; private static final byte RIGHT_SQUARE_BRACKET = 0x5D; private static final byte[] STREAM = "stream".getBytes(StandardCharsets.US_ASCII); private static final byte[] ENDSTREAM = "endstream".getBytes(StandardCharsets.US_ASCII); private BufferedCountingChannelWriter writer; public DefaultCOSWriter(CountingWritableByteChannel channel) { requireNotNullArg(channel, "Cannot write to a null channel"); this.writer = new BufferedCountingChannelWriter(channel); } public DefaultCOSWriter(BufferedCountingChannelWriter writer) { requireNotNullArg(writer, "Cannot write to a null writer"); this.writer = writer; } @Override public void visit(COSArray value) throws IOException { writer.write(LEFT_SQUARE_BRACKET); for (Iterator i = value.iterator(); i.hasNext();) { COSBase current = i.next(); writeValue(Optional.ofNullable(current).orElse(COSNull.NULL)); if (i.hasNext()) { writer.write(SPACE); } } writer.write(RIGHT_SQUARE_BRACKET); writeComplexObjectSeparator(); } @Override public void visit(COSBoolean value) throws IOException { writer.write(value.toString()); } @Override public void visit(COSDictionary dictionary) throws IOException { writer.write(LESS_THEN); writer.write(LESS_THEN); writeDictionaryItemsSeparator(); for (Map.Entry entry : dictionary.entrySet()) { COSBase value = entry.getValue(); if (value != null) { entry.getKey().accept(this); writer.write(SPACE); writeValue(entry.getValue()); writeDictionaryItemsSeparator(); } } writer.write(GREATER_THEN); writer.write(GREATER_THEN); writeComplexObjectSeparator(); } @Override public void visit(COSFloat value) throws IOException { writer.write(value.toString()); } @Override public void visit(COSInteger value) throws IOException { writer.write(value.toString()); } @Override public void visit(COSName value) throws IOException { writer.write(SOLIDUS); byte[] bytes = value.getName().getBytes(StandardCharsets.US_ASCII); for (int i = 0; i < bytes.length; i++) { int current = bytes[i] & 0xFF; if (isLetter(current) || isDigit(current)) { writer.write(bytes[i]); } else { writer.write(NUMBER_SIGN); writer.write(Hex.getBytes(bytes[i])); } } } @Override public void visit(COSNull value) throws IOException { writer.write("null".getBytes(StandardCharsets.US_ASCII)); } @Override public void visit(COSStream value) throws IOException { try { COSBase length = value.getItem(COSName.LENGTH); if (isNull(length)) { value.setLong(COSName.LENGTH, value.getFilteredLength()); } visit((COSDictionary) value); writer.write(STREAM); writer.write(CRLF); long streamStartingPosition = writer.offset(); writer.write(value.getFilteredStream()); if (length instanceof IndirectCOSObjectReference) { ((IndirectCOSObjectReference) length) .setValue(new COSInteger(writer.offset() - streamStartingPosition)); } writer.write(CRLF); writer.write(ENDSTREAM); writeComplexObjectSeparator(); } finally { IOUtils.closeQuietly(value); } } @Override public void visit(COSString value) throws IOException { if (value.isForceHexForm()) { writer.write(LESS_THEN); writer.write(value.toHexString()); writer.write(GREATER_THEN); } else { writer.write(LEFT_PARENTHESIS); for (byte b : value.getBytes()) { switch (b) { case '(': case ')': case '\\': writer.write(REVERSE_SOLIDUS); //$FALL-THROUGH$ default: writer.write(b); } } writer.write(RIGHT_PARENTHESIS); } } @Override public void visit(IndirectCOSObjectReference value) throws IOException { writer.write(value.toString()); } /** * writes the given dictionary or array value item * * @throws IOException */ void writeValue(COSBase value) throws IOException { value.accept(this); } @Override public BufferedCountingChannelWriter writer() { return this.writer; } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/DefaultPDFWriter.java000066400000000000000000000151111320103431700262050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static java.util.Objects.nonNull; import static org.sejda.sambox.util.SpecVersionUtils.PDF_HEADER; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.Closeable; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.sambox.xref.XrefEntry; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default PDF writer that writes part of the pdf document using the given {@link IndirectObjectsWriter}. * * @author Andrea Vacondio */ class DefaultPDFWriter implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(DefaultPDFWriter.class); byte COMMENT = '%'; byte[] GARBAGE = new byte[] { (byte) 0xA7, (byte) 0xE3, (byte) 0xF1, (byte) 0xF1 }; private IndirectObjectsWriter writer; public DefaultPDFWriter(IndirectObjectsWriter writer) { requireNotNullArg(writer, "Cannot write to a null COSWriter"); this.writer = writer; } public void writeHeader(String version) throws IOException { LOG.debug("Writing header " + version); writer().write(PDF_HEADER); writer().write(version); writer().writeEOL(); writer().write(COMMENT); writer().write(GARBAGE); writer().writeEOL(); } /** * writes the xref table * * @return the startxref value * @throws IOException */ public long writeXrefTable() throws IOException { long startxref = writer().offset(); LOG.debug("Writing xref table at offset " + startxref); if (nonNull(writer.context().addWritten(XrefEntry.DEFAULT_FREE_ENTRY))) { LOG.warn("Reserved object number 0 has been overwritten with the expected free entry"); } writer().write("xref"); writer().writeEOL(); for (List continuos : writer.context().getWrittenContiguousGroups()) { writer().write(continuos.get(0).toString() + " " + continuos.size()); writer().writeEOL(); for (long key : continuos) { writer().write(Optional.ofNullable(writer.context().getWritten(key)) .orElse(XrefEntry.DEFAULT_FREE_ENTRY).toXrefTableEntry()); } } return startxref; } /** * Writes the given trailer setting startxref to the given offsets * * @param trailer * @param startxref * @throws IOException */ public void writeTrailer(COSDictionary trailer, long startxref) throws IOException { writeTrailer(trailer, startxref, -1); } /** * Writes the given trailer setting the /Prev entry and startxref to the given offsets * * @param trailer * @param startxref * @param prev prev offset, written only if != -1 * @throws IOException */ public void writeTrailer(COSDictionary trailer, long startxref, long prev) throws IOException { LOG.trace("Writing trailer"); sanitizeTrailer(trailer, prev); trailer.setLong(COSName.SIZE, writer.context().highestObjectNumber() + 1); writer.write("trailer".getBytes(StandardCharsets.US_ASCII)); writer.writeEOL(); trailer.getCOSObject().accept(writer.writer()); writeXrefFooter(startxref); } /** * Writes an xref stream using the given trailer * * @param trailer * @throws IOException */ public void writeXrefStream(COSDictionary trailer) throws IOException { writeXrefStream(trailer, -1); } /** * Writes an xref stream using the given trailer and setting the /Prev entry to the given offset * * @param trailer * @param prev * @throws IOException */ public void writeXrefStream(COSDictionary trailer, long prev) throws IOException { long startxref = writer().offset(); LOG.debug("Writing xref stream at offset " + startxref); sanitizeTrailer(trailer, prev); XrefEntry entry = XrefEntry .inUseEntry(writer.context().highestObjectNumber() + 1, startxref, 0); writer.context().addWritten(entry); writer.writeObject(new IndirectCOSObjectReference(entry.getObjectNumber(), entry.getGenerationNumber(), new XrefStream(trailer, writer.context()))); writeXrefFooter(startxref); } private static void sanitizeTrailer(COSDictionary trailer, long prev) { trailer.removeItem(COSName.PREV); trailer.removeItem(COSName.XREF_STM); trailer.removeItem(COSName.DOC_CHECKSUM); trailer.removeItem(COSName.DECODE_PARMS); trailer.removeItem(COSName.FILTER); trailer.removeItem(COSName.F_DECODE_PARMS); trailer.removeItem(COSName.F_FILTER); trailer.removeItem(COSName.F); trailer.removeItem(COSName.LENGTH); trailer.removeItem(COSName.W); trailer.removeItem(COSName.DL); trailer.removeItem(COSName.TYPE); trailer.removeItem(COSName.INDEX); if (prev != -1) { trailer.setLong(COSName.PREV, prev); } } private void writeXrefFooter(long startxref) throws IOException { writer.write("startxref".getBytes(StandardCharsets.US_ASCII)); writer.writeEOL(); writer.write(Long.toString(startxref)); writer.writeEOL(); writer.write("%%EOF".getBytes(StandardCharsets.US_ASCII)); writer.writeEOL(); } IndirectObjectsWriter writer() { return writer; } @Override public void close() throws IOException { IOUtils.close(writer); } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/EncryptingIndirectReferencesAwareCOSWriter.java000066400000000000000000000042011320103431700334200ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import java.io.IOException; import org.sejda.io.BufferedCountingChannelWriter; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; /** * {@link IndirectReferencesAwareCOSWriter} implementation that will encrypt {@link COSString} and {@link COSStream} if * the {@link PDFWriteContext} has an encryptor. * * @author Andrea Vacondio * */ class EncryptingIndirectReferencesAwareCOSWriter extends IndirectReferencesAwareCOSWriter { EncryptingIndirectReferencesAwareCOSWriter(BufferedCountingChannelWriter writer, PDFWriteContext context) { super(writer, context); } EncryptingIndirectReferencesAwareCOSWriter(CountingWritableByteChannel channel, PDFWriteContext context) { super(channel, context); } @Override public void visit(COSStream value) throws IOException { if (context.encryptor.isPresent()) { context.encryptor.get().visit(value); } super.visit(value); } @Override public void visit(COSString value) throws IOException { if (context.encryptor.isPresent()) { context.encryptor.get().visit(value); value.setForceHexForm(true); } super.visit(value); } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/ExistingPagesSizePredictor.java000066400000000000000000000154761320103431700303710ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import java.io.IOException; import org.sejda.io.CountingWritableByteChannel; import org.sejda.io.DevNullWritableByteChannel; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Component that tries to predict the size of a resulting document if {@link PDPage}s and {@link COSObjectable}s are * added to it. The component does its best to return exact predicted values and it does that by simulating an actual * write, despite that, the predicted values should be considered rough estimations and not a byte precision ones. * * @author Andrea Vacondio */ public class ExistingPagesSizePredictor extends PDFBodyWriter { private static final Logger LOG = LoggerFactory.getLogger(ExistingPagesSizePredictor.class); // stream, endstream and 2x CRLF private static final int STREAM_WRAPPING_SIZE = 19; private CountingWritableByteChannel channel; private IndirectObjectsWriter writer; private long pages; private ExistingPagesSizePredictor(PDFWriteContext context, IndirectObjectsWriter writer, CountingWritableByteChannel channel) { super(context, new BodyObjectsWriter(context, writer)); this.channel = channel; this.writer = writer; } /** * Adds a {@link PDPage} to the predicted size. This component simulates the page write to a * {@link DevNullWritableByteChannel} and does not release the page objects once written. * * @param page * @throws IOException */ public void addPage(PDPage page) throws IOException { if (page != null) { pages++; COSDictionary pageCopy = page.getCOSObject().duplicate(); pageCopy.removeItem(COSName.PARENT); createIndirectReferenceIfNeededFor(pageCopy); startWriting(); LOG.debug("Page {} addition simulated, now at {} body bytes and {} xref bytes", page, predictedPagesSize(), predictedXrefTableSize()); } } /** * Adds the {@link COSObjectable} to the predicted size. The object is added as an indirect reference and is * processed, specifically, in case of {@link COSDictionary} or {@link COSArray}, indirect reference might be * created for their values. * * @param value * @throws IOException */ public void addIndirectReferenceFor(COSObjectable value) throws IOException { if (value != null) { createIndirectReferenceIfNeededFor(value.getCOSObject()); startWriting(); LOG.debug("{} addition simulated, now at {} body bytes and {} xref bytes", value.getCOSObject(), predictedPagesSize(), predictedXrefTableSize()); } } /** * @return the current predicted page size * @throws IOException */ public long predictedPagesSize() throws IOException { writer.writer().writer().flush(); return ((BodyObjectsWriter) objectsWriter).streamsSize + channel.count(); } /** * @return the current predicted xref size * @throws IOException */ public long predictedXrefTableSize() { // each entry is 21 bytes plus the xref keyword and section header return (21 * (context().written() + 1)) + 10; } /** * @return true if some page has been written */ public boolean hasPages() { return pages > 0; } /** * @return the current number of written pages */ public long pages() { return pages; } @Override public void close() throws IOException { super.close(); IOUtils.close(writer); } /** * Factory method for an ExistingPagesSizePredictor * * @param opts * @return */ public static ExistingPagesSizePredictor instance(WriteOption... opts) { CountingWritableByteChannel channel = CountingWritableByteChannel .from(new DevNullWritableByteChannel()); PDFWriteContext context = new PDFWriteContext(null, opts); IndirectObjectsWriter writer = new IndirectObjectsWriter(channel, context) { @Override protected void onWritten(IndirectCOSObjectReference ref) { // don't release } }; return new ExistingPagesSizePredictor(context, writer, channel); } private static class BodyObjectsWriter implements PDFBodyObjectsWriter { long streamsSize; private PDFWriteContext context; private IndirectObjectsWriter writer; public BodyObjectsWriter(PDFWriteContext context, IndirectObjectsWriter writer) { this.context = context; this.writer = writer; } @Override public void writeObject(IndirectCOSObjectReference ref) throws IOException { if (!context.hasWritten(ref.xrefEntry())) { COSBase wrapped = ref.getCOSObject().getCOSObject(); if (wrapped instanceof COSStream) { COSStream stream = (COSStream) wrapped; // we don't simulate the write of the whole stream, we just save the expected size and simulate the // dictionary write streamsSize += stream.getFilteredLength(); streamsSize += STREAM_WRAPPING_SIZE; ref.setValue(stream.duplicate()); } } writer.writeObject(ref); } @Override public void onWriteCompletion() { // no op } @Override public void close() { // no op } } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/IncrementablePDDocumentWriter.java000066400000000000000000000145351320103431700307730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static java.util.Optional.ofNullable; import static org.sejda.sambox.encryption.EncryptionContext.encryptionAlgorithmFromEncryptionDictionary; import static org.sejda.sambox.util.SpecVersionUtils.V1_5; import static org.sejda.sambox.util.SpecVersionUtils.isAtLeast; import static org.sejda.util.RequireUtils.requireNotNullArg; import static org.sejda.util.RequireUtils.requireState; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.input.IncrementablePDDocument; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Writer for a {@link IncrementablePDDocument}. This component provides methods to write a * {@link IncrementablePDDocument} to a {@link CountingWritableByteChannel} performing an incremental update. * * @author Andrea Vacondio */ public class IncrementablePDDocumentWriter implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(IncrementablePDDocumentWriter.class); private DefaultPDFWriter writer; private PDFWriteContext context; private CountingWritableByteChannel channel; private Set options; public IncrementablePDDocumentWriter(CountingWritableByteChannel channel, WriteOption... options) { requireNotNullArg(channel, "Cannot write to a null channel"); this.channel = channel; this.options = ofNullable(options).map(Arrays::asList).map(HashSet::new) .orElseGet(HashSet::new); } /** * Writes the {@link PDDocument}. * * @param document * @param standardSecurity * @throws IOException */ public void write(IncrementablePDDocument document) throws IOException { requireNotNullArg(document, "Incremented document cannot be null"); // Trailer offset is -1, we managed to perform a full scan so SAMBox might be able to handle the doc but not for // an incremental update which requires to have the newly created xref to point to the previous one and we don't // have the previous one offset // TODO idea: we write the incremented to a tmp file so we generate a new xref table? requireState(document.trailer().xrefOffset() != -1, "The incremented document has errors and its xref table couldn't be found"); sanitizeWriteOptions(document); this.context = new PDFWriteContext(document.highestExistingReference().objectNumber(), encryptionAlgorithmFromEncryptionDictionary(document.encryptionDictionary(), document.encryptionKey()), options.stream().toArray(WriteOption[]::new)); this.writer = new DefaultPDFWriter(new IndirectObjectsWriter(channel, context)); try (InputStream stream = document.incrementedAsStream()) { writer.writer().write(stream); } writer.writer().writeEOL(); writeBody(document); writeXref(document); } private void sanitizeWriteOptions(IncrementablePDDocument document) { // for incremental updates we have to write xref stream if the incremented doc has xref streams, xref table // otherwise if (document.trailer().isXrefStream()) { options.add(WriteOption.XREF_STREAM); } else { options.remove(WriteOption.XREF_STREAM); options.remove(WriteOption.OBJECT_STREAMS); } // we remove the write option instead of increasing version because increasing the version would require to // update the Catalog as part of the incremental update, potentially breaking existing signatures if (!isAtLeast(document.incremented().getVersion(), V1_5)) { options.remove(WriteOption.OBJECT_STREAMS); } } private void writeBody(IncrementablePDDocument document) throws IOException { try (PDFBodyWriter bodyWriter = new IncrementalPDFBodyWriter(context, objectStreamWriter(objectsWriter()))) { LOG.debug("Writing body using " + bodyWriter.objectsWriter.getClass()); bodyWriter.write(document); } } private PDFBodyObjectsWriter objectsWriter() { if (context.hasWriteOption(WriteOption.SYNC_BODY_WRITE)) { return new SyncPDFBodyObjectsWriter(writer.writer()); } return new AsyncPDFBodyObjectsWriter(writer.writer()); } private PDFBodyObjectsWriter objectStreamWriter(PDFBodyObjectsWriter wrapped) { if (context.hasWriteOption(WriteOption.OBJECT_STREAMS)) { return new ObjectsStreamPDFBodyObjectsWriter(context, wrapped); } return wrapped; } private void writeXref(IncrementablePDDocument document) throws IOException { if (context.hasWriteOption(WriteOption.XREF_STREAM) || context.hasWriteOption(WriteOption.OBJECT_STREAMS)) { writer.writeXrefStream(document.trailer().getCOSObject(), document.trailer().xrefOffset()); } else { long startxref = writer.writeXrefTable(); writer.writeTrailer(document.trailer().getCOSObject(), startxref, document.trailer().xrefOffset()); } } @Override public void close() throws IOException { IOUtils.close(writer); } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/IncrementalPDFBodyWriter.java000066400000000000000000000030361320103431700277030ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.input.ExistingIndirectCOSObject; /** * Body writer for incremental updates. * * @author Andrea Vacondio */ public class IncrementalPDFBodyWriter extends PDFBodyWriter { IncrementalPDFBodyWriter(PDFWriteContext context, PDFBodyObjectsWriter objectsWriter) { super(context, objectsWriter); } @Override public void onPotentialIndirectObject(COSBase item) throws IOException { if (item instanceof ExistingIndirectCOSObject) { context().addExistingReference((ExistingIndirectCOSObject) item); } else { item.accept(this); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/IndirectObjectsWriter.java000066400000000000000000000133061320103431700273460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static org.sejda.sambox.output.DefaultCOSWriter.SPACE; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.sejda.io.BufferedCountingChannelWriter; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Component that writes pdf objects and keeps track of what has been written to avoid writing an object twice. * * @author Andrea Vacondio */ class IndirectObjectsWriter implements Closeable { private static final byte[] OBJ = "obj".getBytes(StandardCharsets.US_ASCII); private static final byte[] ENDOBJ = "endobj".getBytes(StandardCharsets.US_ASCII); private static final Logger LOG = LoggerFactory.getLogger(IndirectObjectsWriter.class); private COSWriter writer; private PDFWriteContext context; IndirectObjectsWriter(CountingWritableByteChannel channel, PDFWriteContext context) { this(new BufferedCountingChannelWriter(channel), context); } IndirectObjectsWriter(BufferedCountingChannelWriter writer, PDFWriteContext context) { requireNotNullArg(writer, "Writer cannot be null"); requireNotNullArg(context, "Write context cannot be null"); this.writer = new EncryptingIndirectReferencesAwareCOSWriter(writer, context); this.context = context; } PDFWriteContext context() { return context; } /** * Writes the given {@link IndirectCOSObjectReference} updating its offset and releasing it once written. The object * is written only if not previously already written. * * @param object * @throws IOException */ public void writeObjectIfNotWritten(IndirectCOSObjectReference object) throws IOException { if (!context.hasWritten(object.xrefEntry())) { writeObject(object); } } /** * Writes the given {@link IndirectCOSObjectReference} updating its offset and releasing it once written. * * @param object * @throws IOException */ public void writeObject(IndirectCOSObjectReference object) throws IOException { context.writing(object.xrefEntry().key()); doWriteObject(object); context.addWritten(object.xrefEntry()); onWritten(object); } /** * Called when the input indirect references has just been written * * @param object */ protected void onWritten(IndirectCOSObjectReference ref) { ref.releaseCOSObject(); LOG.trace("Released " + ref); } private void doWriteObject(IndirectCOSObjectReference object) throws IOException { object.xrefEntry().setByteOffset(writer.writer().offset()); writer.writer().write(Long.toString(object.xrefEntry().getObjectNumber())); writer.writer().write(SPACE); writer.writer().write(Integer.toString(object.xrefEntry().getGenerationNumber())); writer.writer().write(SPACE); writer.writer().write(OBJ); writer.writer().writeEOL(); object.getCOSObject().accept(writer); writer.writer().writeEOL(); writer.writer().write(ENDOBJ); writer.writer().writeEOL(); LOG.trace("Written object " + object.xrefEntry()); } /** * @return the underlying {@link COSWriter} */ COSWriter writer() { return writer; } /** * @see BufferedCountingChannelWriter#writeEOL() * @throws IOException */ public void writeEOL() throws IOException { writer.writer().writeEOL(); } /** * @see BufferedCountingChannelWriter#write(byte[]) * @param bytes * @throws IOException */ public void write(byte[] bytes) throws IOException { writer.writer().write(bytes); } /** * @see BufferedCountingChannelWriter#write(String) * @param string * @throws IOException */ public void write(String string) throws IOException { writer.writer().write(string); } /** * @see BufferedCountingChannelWriter#write(byte) * @param b * @throws IOException */ public void write(byte b) throws IOException { writer.writer().write(b); } /** * @see BufferedCountingChannelWriter#write(InputStream) * @param stream * @throws IOException */ public void write(InputStream stream) throws IOException { writer.writer().write(stream); } /** * @see BufferedCountingChannelWriter#offset() * @return the current offset */ public long offset() { return writer.writer().offset(); } @Override public void close() throws IOException { IOUtils.close(writer); context = null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/IndirectReferenceProvider.java000066400000000000000000000041641320103431700301730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import java.util.concurrent.atomic.AtomicLong; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.sambox.cos.NonStorableInObjectStreams; /** * Component responsible for creating the {@link IndirectCOSObjectReference}s keeping count of the next available object * number. * * @author Andrea Vacondio */ class IndirectReferenceProvider { final AtomicLong referencesCounter; IndirectReferenceProvider() { referencesCounter = new AtomicLong(0); } /** * @param next reference number will be highestAlreadyExisting+1 */ IndirectReferenceProvider(long highestAlreadyExisting) { referencesCounter = new AtomicLong(highestAlreadyExisting); } IndirectCOSObjectReference nextReferenceFor(COSBase baseObject) { return new IndirectCOSObjectReference(referencesCounter.incrementAndGet(), 0, baseObject); } IndirectCOSObjectReference nextNonStorableInObjectStreamsReferenceFor(COSBase baseObject) { return new NonStorableInObjectStreams(referencesCounter.incrementAndGet(), 0, baseObject); } IndirectCOSObjectReference nextNonStorableInObjectStreamsReference() { return new NonStorableInObjectStreams(referencesCounter.incrementAndGet(), 0, null); } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/IndirectReferencesAwareCOSWriter.java000066400000000000000000000037261320103431700313700ustar00rootroot00000000000000/* * Created on 27/ago/2015 * Copyright 2010 by Andrea Vacondio (andrea.vacondio@gmail.com). * * 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. */ package org.sejda.sambox.output; import java.io.IOException; import org.sejda.io.BufferedCountingChannelWriter; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.cos.COSBase; /** * {@link COSWriter} implementation that writes dictionary and array values as indirect references if they have been * added as indirect references to the context. * * @author Andrea Vacondio * */ class IndirectReferencesAwareCOSWriter extends DefaultCOSWriter { final PDFWriteContext context; IndirectReferencesAwareCOSWriter(CountingWritableByteChannel channel, PDFWriteContext context) { this(new BufferedCountingChannelWriter(channel), context); } IndirectReferencesAwareCOSWriter(BufferedCountingChannelWriter writer, PDFWriteContext context) { super(writer); this.context = context; } /** * writes the given dictionary or array value item * * @throws IOException */ @Override void writeValue(COSBase value) throws IOException { if (context.hasIndirectReferenceFor(value)) { context.getIndirectReferenceFor(value).accept(this); } else { value.accept(this); } } @Override public void close() throws IOException { super.close(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/ObjectsStreamPDFBodyObjectsWriter.java000066400000000000000000000165121320103431700315240ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static org.sejda.sambox.cos.DirectCOSObject.asDirectObject; import static org.sejda.sambox.util.CharUtils.ASCII_SPACE; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.SequenceInputStream; import java.nio.charset.StandardCharsets; import java.util.zip.DeflaterInputStream; import org.sejda.io.CountingWritableByteChannel; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.sambox.SAMBox; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.DisposableCOSObject; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.sambox.cos.NonStorableInObjectStreams; import org.sejda.sambox.xref.CompressedXrefEntry; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of a PDFBodyObjectsWriter where objects are written to an ObjectsStream and later the ObjectsStream is * written as COSStream using the delegate {@link PDFBodyObjectsWriter} * * @author Andrea Vacondio */ public class ObjectsStreamPDFBodyObjectsWriter implements PDFBodyObjectsWriter { private static final Logger LOG = LoggerFactory .getLogger(ObjectsStreamPDFBodyObjectsWriter.class); private PDFWriteContext context; private PDFBodyObjectsWriter delegate; private ObjectsStream currentStream; public ObjectsStreamPDFBodyObjectsWriter(PDFWriteContext context, PDFBodyObjectsWriter delegate) { requireNotNullArg(context, "Write context cannot be null"); requireNotNullArg(delegate, "Delegate writer cannot be null"); this.context = context; this.delegate = delegate; currentStream = new ObjectsStream(context); context.createIndirectReferenceFor(currentStream); } @Override public void writeObject(IndirectCOSObjectReference ref) throws IOException { if (ref instanceof NonStorableInObjectStreams || ref.getCOSObject().getCOSObject() instanceof COSStream) { delegate.writeObject(ref); } else { IndirectCOSObjectReference streamRef = context.getIndirectReferenceFor(currentStream); context.addWritten( CompressedXrefEntry.compressedEntry(ref.xrefEntry().getObjectNumber(), streamRef.xrefEntry().getObjectNumber(), currentStream.counter)); currentStream.addItem(ref); LOG.trace("Added ref {} to object stream {}", ref, streamRef); } if (currentStream.isFull()) { doWriteObjectsStream(); currentStream = new ObjectsStream(context); context.createIndirectReferenceFor(currentStream); } } private void doWriteObjectsStream() throws IOException { IndirectCOSObjectReference ref = context.getIndirectReferenceFor(currentStream); LOG.debug("Writing object stream {}", ref); currentStream.prepareForWriting(); IndirectCOSObjectReference length = context .createNonStorableInObjectStreamIndirectReference(); currentStream.setItem(COSName.LENGTH, length); delegate.writeObject(ref); LOG.trace("Writing object stream length {}", length); delegate.writeObject(length); } @Override public void onWriteCompletion() throws IOException { if (currentStream.hasItems()) { doWriteObjectsStream(); } // complete writing delegate.onWriteCompletion(); } @Override public void close() throws IOException { IOUtils.close(delegate); currentStream = null; } static class ObjectsStream extends COSStream implements DisposableCOSObject { private int counter; private FastByteArrayOutputStream header = new FastByteArrayOutputStream(); private FastByteArrayOutputStream data = new FastByteArrayOutputStream(); private DefaultCOSWriter dataWriter; private InputStream filtered; public ObjectsStream(PDFWriteContext context) { setName(COSName.TYPE, COSName.OBJ_STM.getName()); dataWriter = new IndirectReferencesAwareCOSWriter( CountingWritableByteChannel.from(data), context) { @Override public void writeComplexObjectSeparator() { // nothing } @Override public void writeDictionaryItemsSeparator() { // nothing } }; } public boolean hasItems() { return counter > 0; } void addItem(IndirectCOSObjectReference ref) throws IOException { this.counter++; header.write(Long.toUnsignedString(ref.xrefEntry().getObjectNumber()) .getBytes(StandardCharsets.US_ASCII)); header.write(ASCII_SPACE); header.write(Long.toUnsignedString(dataWriter.writer().offset()) .getBytes(StandardCharsets.US_ASCII)); header.write(ASCII_SPACE); ref.getCOSObject().accept(dataWriter); dataWriter.writer().write(ASCII_SPACE); ref.releaseCOSObject(); } boolean isFull() { return counter >= Integer.getInteger(SAMBox.OBJECTS_STREAM_SIZE_PROPERTY, 100); } @Override public InputStream doGetFilteredStream() { return this.filtered; } void prepareForWriting() { IOUtils.closeQuietly(dataWriter); setItem(COSName.N, asDirectObject(COSInteger.get(counter))); setItem(COSName.FIRST, asDirectObject(COSInteger.get(header.size()))); setItem(COSName.FILTER, asDirectObject(COSName.FLATE_DECODE)); this.filtered = new DeflaterInputStream( new SequenceInputStream(new ByteArrayInputStream(header.toByteArray()), new ByteArrayInputStream(data.toByteArray()))); this.header = null; this.data = null; } @Override public void close() throws IOException { IOUtils.closeQuietly(filtered); super.close(); } @Override public void releaseCOSObject() { this.filtered = null; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/PDDocumentWriter.java000066400000000000000000000117711320103431700263010ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static java.util.Optional.ofNullable; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.Closeable; import java.io.IOException; import java.util.Optional; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.cos.COSDocument; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.encryption.EncryptionContext; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.util.SpecVersionUtils; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Writer for a {@link PDDocument}. This component provides methods to write a {@link PDDocument} to a * {@link CountingWritableByteChannel}. * * @author Andrea Vacondio */ public class PDDocumentWriter implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(PDDocumentWriter.class); private DefaultPDFWriter writer; private PDFWriteContext context; private Optional encryptionContext; public PDDocumentWriter(CountingWritableByteChannel channel, Optional encryptionContext, WriteOption... options) { requireNotNullArg(channel, "Cannot write to a null channel"); this.encryptionContext = ofNullable(encryptionContext).orElseGet(Optional::empty); this.context = new PDFWriteContext( this.encryptionContext.map(EncryptionContext::encryptionAlgorithm).orElse(null), options); this.writer = new DefaultPDFWriter(new IndirectObjectsWriter(channel, context)); } /** * Writes the {@link PDDocument}. * * @param document * @throws IOException */ public void write(PDDocument document) throws IOException { requireNotNullArg(document, "PDDocument cannot be null"); if (context.hasWriteOption(WriteOption.XREF_STREAM) || context.hasWriteOption(WriteOption.OBJECT_STREAMS)) { document.requireMinVersion(SpecVersionUtils.V1_5); } ofNullable(document.getDocument().getTrailer()).map(t -> t.getCOSObject()) .ifPresent(t -> t.removeItem(COSName.ENCRYPT)); encryptionContext.ifPresent(c -> { document.getDocument() .setEncryptionDictionary(c.security.encryption.generateEncryptionDictionary(c)); LOG.debug("Generated encryption dictionary"); ofNullable(document.getDocumentCatalog().getMetadata()).map(m -> m.getCOSObject()) .ifPresent(str -> str.encryptable(c.security.encryptMetadata)); }); writer.writeHeader(document.getDocument().getHeaderVersion()); writeBody(document.getDocument()); writeXref(document); } private void writeBody(COSDocument document) throws IOException { try (PDFBodyWriter bodyWriter = new PDFBodyWriter(context, objectStreamWriter(objectsWriter()))) { LOG.debug("Writing body using " + bodyWriter.objectsWriter.getClass()); bodyWriter.write(document); } } private PDFBodyObjectsWriter objectsWriter() { if (context.hasWriteOption(WriteOption.SYNC_BODY_WRITE)) { return new SyncPDFBodyObjectsWriter(writer.writer()); } return new AsyncPDFBodyObjectsWriter(writer.writer()); } private PDFBodyObjectsWriter objectStreamWriter(PDFBodyObjectsWriter wrapped) { if (context.hasWriteOption(WriteOption.OBJECT_STREAMS)) { return new ObjectsStreamPDFBodyObjectsWriter(context, wrapped); } return wrapped; } private void writeXref(PDDocument document) throws IOException { if (context.hasWriteOption(WriteOption.XREF_STREAM) || context.hasWriteOption(WriteOption.OBJECT_STREAMS)) { writer.writeXrefStream(document.getDocument().getTrailer().getCOSObject()); } else { long startxref = writer.writeXrefTable(); writer.writeTrailer(document.getDocument().getTrailer().getCOSObject(), startxref); } } @Override public void close() throws IOException { IOUtils.close(writer); } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/PDFBodyObjectsWriter.java000066400000000000000000000025001320103431700270260ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import java.io.Closeable; import java.io.IOException; import org.sejda.sambox.cos.IndirectCOSObjectReference; /** * Component responsible for writing pdf body objects * * @author Andrea Vacondio */ public interface PDFBodyObjectsWriter extends Closeable { void writeObject(IndirectCOSObjectReference ref) throws IOException; /** * callback to perform once all the objects have been written * * @throws IOException */ void onWriteCompletion() throws IOException; } sambox-1.1.19/src/main/java/org/sejda/sambox/output/PDFBodyWriter.java000066400000000000000000000156161320103431700255300ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static java.util.Optional.ofNullable; import static org.sejda.util.RequireUtils.requireNotNullArg; import static org.sejda.util.RequireUtils.requireState; import java.io.Closeable; import java.io.IOException; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSDocument; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSVisitor; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.sambox.input.ExistingIndirectCOSObject; import org.sejda.sambox.input.IncrementablePDDocument; /** * Base component providing methods to write the body of a pdf document. This implementation starts from the document * trailer and visits the whole document graph updating the {@link PDFWriteContext}. An * {@link IndirectCOSObjectReference} is created by the context for each {@link COSDictionary} and * {@link ExistingIndirectCOSObject}, if not previously created. Once all the values of a {@link COSDictionary} or * {@link COSArray} have been explored, the {@link COSDictionary}/ {@link COSArray} is written as a pdf object, this * allows an async implementation to write objects while the body writer is still performing its algorithm. * * @author Andrea Vacondio */ class PDFBodyWriter implements COSVisitor, Closeable { private Queue stack = new LinkedList<>(); private PDFWriteContext context; private boolean open = true; PDFBodyObjectsWriter objectsWriter; PDFBodyWriter(PDFWriteContext context, PDFBodyObjectsWriter objectsWriter) { requireNotNullArg(context, "Write context cannot be null"); requireNotNullArg(objectsWriter, "Objects writer cannot be null"); this.context = context; this.objectsWriter = objectsWriter; } PDFWriteContext context() { return context; } /** * Writes the given document * * @param document * @throws IOException */ public void write(IncrementablePDDocument document) throws IOException { requireState(open, "The writer is closed"); document.newIndirects().forEach(o -> stack.add(context.getOrCreateIndirectReferenceFor(o))); document.trailer().getCOSObject().accept(this); document.replacements().forEach(stack::add); startWriting(); } /** * Writes the body of the given document * * @param document * @throws IOException */ public void write(COSDocument document) throws IOException { requireState(open, "The writer is closed"); document.accept(this); } @Override public void visit(COSDocument document) throws IOException { for (COSName k : Arrays.asList(COSName.ROOT, COSName.ENCRYPT)) { ofNullable(document.getTrailer().getCOSObject().getItem(k)).ifPresent( r -> stack.add(context.createNonStorableInObjectStreamIndirectReferenceFor(r))); } ofNullable(document.getTrailer().getCOSObject().getItem(COSName.INFO)) .ifPresent(this::createIndirectReferenceIfNeededFor); startWriting(); } /** * Starts writing whatever has been stacked * * @throws IOException */ void startWriting() throws IOException { while (!stack.isEmpty()) { IndirectCOSObjectReference item = stack.poll(); item.getCOSObject().accept(this); objectsWriter.writeObject(item); } objectsWriter.onWriteCompletion(); } @Override public void visit(COSArray array) throws IOException { for (int i = 0; i < array.size(); i++) { COSBase item = ofNullable(array.get(i)).orElse(COSNull.NULL); if (item instanceof ExistingIndirectCOSObject || item instanceof COSDictionary) { onPotentialIndirectObject(item); } else { item.accept(this); } } } @Override public void visit(COSDictionary value) throws IOException { for (COSName key : value.keySet()) { COSBase item = ofNullable(value.getItem(key)).orElse(COSNull.NULL); if (item instanceof ExistingIndirectCOSObject || item instanceof COSDictionary || COSName.THREADS.equals(key)) { onPotentialIndirectObject(item); } else { item.accept(this); } } } @Override public void visit(COSStream value) throws IOException { value.removeItem(COSName.LENGTH); if (context.hasWriteOption(WriteOption.COMPRESS_STREAMS)) { value.addCompression(); } // with encrypted docs we write length as an indirect ref value.indirectLength(context.encryptor.isPresent()); if (value.indirectLength()) { IndirectCOSObjectReference length = context .createNonStorableInObjectStreamIndirectReference(); value.setItem(COSName.LENGTH, length); stack.add(length); } this.visit((COSDictionary) value); } /** * Called during the visit on the objects graph, when a potential indirect object is met. Default implementation * creates a new indirect reference for it. * * @param item * @throws IOException */ public void onPotentialIndirectObject(COSBase item) throws IOException { createIndirectReferenceIfNeededFor(item); } final void createIndirectReferenceIfNeededFor(COSBase item) { if (!context.hasIndirectReferenceFor(item)) { stack.add(context.getOrCreateIndirectReferenceFor(item)); } } @Override public void close() throws IOException { objectsWriter.close(); context = null; this.open = false; } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/PDFWriteContext.java000066400000000000000000000243461320103431700260750ustar00rootroot00000000000000/* * Created on 27/ago/2015 * Copyright 2010 by Andrea Vacondio (andrea.vacondio@gmail.com). * * 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. */ package org.sejda.sambox.output; import static java.util.Optional.ofNullable; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.SortedMap; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.function.Function; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.cos.IndirectCOSObjectIdentifier; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.sejda.sambox.cos.NonStorableInObjectStreams; import org.sejda.sambox.encryption.GeneralEncryptionAlgorithm; import org.sejda.sambox.input.ExistingIndirectCOSObject; import org.sejda.sambox.xref.XrefEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Context that contains and keeps track of all the information needed during the process of writing a PDF document. It * creates indirect references for {@link COSBase} instances and provide lookup methods to be able to retrieve these * data and know what's the indirect reference created by the context for a given {@link COSBase}. I keeps track of what * object numbers have been written. * * @author Andrea Vacondio */ class PDFWriteContext { private static final Logger LOG = LoggerFactory.getLogger(PDFWriteContext.class); private String contextId = UUID.randomUUID().toString(); private IndirectReferenceProvider referencesProvider = new IndirectReferenceProvider(); private Map lookupNewRef = new ConcurrentHashMap<>(); private List opts; private SortedMap written = new ConcurrentSkipListMap<>(); public final Optional encryptor; PDFWriteContext(GeneralEncryptionAlgorithm encryptor, WriteOption... options) { this.encryptor = ofNullable(encryptor); this.opts = Arrays.asList(options); this.referencesProvider = new IndirectReferenceProvider(); } PDFWriteContext(long highestExistingReferenceNumber, GeneralEncryptionAlgorithm encryptor, WriteOption... options) { this.encryptor = ofNullable(encryptor); this.encryptor.ifPresent(e -> LOG.debug("Encryptor: {}", e)); this.opts = Arrays.asList(options); this.referencesProvider = new IndirectReferenceProvider(highestExistingReferenceNumber); LOG.debug("PDFWriteContext created with highest object reference number {}", highestExistingReferenceNumber); } /** * Creates a new {@link IndirectCOSObjectReference} for the given item. We store the association between the item id * and the reference so that if we meet the same id later, we can retrieve the reference that was previously * written. * * @param item * @return the created reference */ IndirectCOSObjectReference createIndirectReferenceFor(COSBase item) { return createNewReference(item, referencesProvider::nextReferenceFor); } /** * Creates a new {@link NonStorableInObjectStreams} for the given item. We store the association between the item id * and the reference so that if we meet the same id later, we can retrieve the reference that was previously * written. * * @param item * @return the created reference */ IndirectCOSObjectReference createNonStorableInObjectStreamIndirectReferenceFor(COSBase item) { return createNewReference(item, referencesProvider::nextNonStorableInObjectStreamsReferenceFor); } /** * Creates an empty {@link NonStorableInObjectStreams} * * @return the created reference */ IndirectCOSObjectReference createNonStorableInObjectStreamIndirectReference() { return referencesProvider.nextNonStorableInObjectStreamsReference(); } private IndirectCOSObjectReference createNewReference(COSBase item, Function supplier) { // It's an existing indirect object if (item instanceof ExistingIndirectCOSObject) { ExistingIndirectCOSObject existingItem = (ExistingIndirectCOSObject) item; IndirectCOSObjectReference newRef = supplier.apply(item); LOG.trace("Created new indirect reference {} replacing the existing one {}", newRef, existingItem.id()); // if a COSName was indirect in the original doc we write it as indirect but we don't as indirect every // occurrence of it, just this if (!(item.getCOSObject() instanceof COSName)) { lookupNewRef.put(existingItem.id(), newRef); } return newRef; } if (item instanceof COSNull) { // we don't associate any id or store any lookup for COSNull return supplier.apply(item); } // it's a new COSBase IndirectCOSObjectReference newRef = supplier.apply(item); LOG.trace("Created new indirect reference '{}' ", newRef); item.idIfAbsent(new IndirectCOSObjectIdentifier(newRef.xrefEntry().key(), contextId)); lookupNewRef.put(item.id(), newRef); return newRef; } /** * Creates a new {@link IndirectCOSObjectReference} for the given item if it has not been created before, it returns * the already existing reference otherwise. * * @param item * @return the reference */ IndirectCOSObjectReference getOrCreateIndirectReferenceFor(COSBase item) { if (hasIndirectReferenceFor(item)) { // I met it already return lookupNewRef.get(item.id()); } return createIndirectReferenceFor(item); } /** * @param item * @return the {@link IndirectCOSObjectReference} for the given item or null if an * {@link IndirectCOSObjectReference} has not been created for the item. */ IndirectCOSObjectReference getIndirectReferenceFor(COSBase item) { return lookupNewRef.get(item.id()); } /** * adds the reference to the context. When later queried, the context will return the existing indirect reference an * no new reference will be created. This is used during incremental updates when we don't want to create new * references for existing objects * * @param existing */ void addExistingReference(ExistingIndirectCOSObject existing) { lookupNewRef.put(existing.id(), new IndirectCOSObjectReference(existing.id().objectIdentifier.objectNumber(), existing.id().objectIdentifier.generation(), null)); } /** * @param item * @return true if the given item has been added to the context and an indirect reference created for it. */ boolean hasIndirectReferenceFor(COSBase item) { return item.hasId() && lookupNewRef.containsKey(item.id()); } /** * @param opt * @return true if the context has the given write option */ boolean hasWriteOption(WriteOption opt) { return opts.contains(opt); } /** * @return number of written objects so far. */ int written() { return written.size(); } /** * @param entry * @return true if the given entry has been already written */ boolean hasWritten(XrefEntry entry) { return written.containsKey(entry.getObjectNumber()); } /** * Adds an entry to the list of the written entries * * @param entry * @return the previous value if an entry with the same object number has been already written, null otherwise. */ XrefEntry addWritten(XrefEntry entry) { return written.put(entry.getObjectNumber(), entry); } /** * @return the written entry with the highest object number */ XrefEntry highestWritten() { return written.get(written.lastKey()); } /** * @return the highest object number that this context knows */ long highestObjectNumber() { if (written.isEmpty()) { return referencesProvider.referencesCounter.get(); } return Math.max(written.lastKey(), referencesProvider.referencesCounter.get()); } /** * @param objectNumber * @return the written entry with the given object number if any, null otherwise. */ XrefEntry getWritten(Long objectNumber) { return written.get(objectNumber); } /** * @return a list of contiguous groups of written object numbers */ List> getWrittenContiguousGroups() { List> contiguous = new ArrayList<>(); if (!written.isEmpty()) { LinkedList group = new LinkedList<>(); contiguous.add(group); for (Long current : written.keySet()) { if (group.isEmpty() || current == group.getLast() + 1) { group.addLast(current); } else { group = new LinkedList<>(Arrays.asList(current)); contiguous.add(group); } } } return contiguous; } /** * Informs the context of the object is about the written * * @param key */ void writing(COSObjectKey key) { encryptor.ifPresent(e -> e.setCurrentCOSObjectKey(key)); } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/SyncPDFBodyObjectsWriter.java000066400000000000000000000035421320103431700276720ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.IOException; import org.sejda.sambox.cos.IndirectCOSObjectReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of a PDFBodyObjectsWriter that synchronously writes {@link IndirectCOSObjectReference} * * @author Andrea Vacondio */ class SyncPDFBodyObjectsWriter implements PDFBodyObjectsWriter { private static final Logger LOG = LoggerFactory.getLogger(SyncPDFBodyObjectsWriter.class); private IndirectObjectsWriter writer; SyncPDFBodyObjectsWriter(IndirectObjectsWriter writer) { requireNotNullArg(writer, "Cannot write to a null writer"); this.writer = writer; } @Override public void writeObject(IndirectCOSObjectReference ref) throws IOException { writer.writeObjectIfNotWritten(ref); } @Override public void onWriteCompletion() { LOG.debug("Written document body"); } @Override public void close() { // nothing } } sambox-1.1.19/src/main/java/org/sejda/sambox/output/WriteOption.java000066400000000000000000000025051320103431700253600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; /** * Options that can be selected when writing a PDF document. * * @author Andrea Vacondio */ public enum WriteOption { /** * Writes the xref data as stream */ XREF_STREAM, /** * Writes the document using the synchronous writer as opposed to the default async one */ SYNC_BODY_WRITE, /** * Writes pdf objects using objects stream */ OBJECT_STREAMS, /** * Adds a Flate filter to the streams if not already there */ COMPRESS_STREAMS; } sambox-1.1.19/src/main/java/org/sejda/sambox/output/XrefStream.java000066400000000000000000000072311320103431700251560ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.output; import static org.sejda.sambox.cos.DirectCOSObject.asDirectObject; import static org.sejda.sambox.xref.XrefEntry.freeEntry; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Optional; import java.util.function.Function; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.DirectCOSObject; /** * A {@link COSStream} that represent and xref stream as defined in Chap 7.5.8 of PDF 32000-1:2008 * * @author Andrea Vacondio */ class XrefStream extends COSStream { /** * Creates an xref stream from the given dictionary. The stream will contain all the entries that have been written * using the given context * * @param dictionary * @param context * @throws IOException */ XrefStream(COSDictionary dictionary, PDFWriteContext context) throws IOException { super(dictionary); setItem(COSName.TYPE, COSName.XREF); setItem(COSName.SIZE, asDirect(context.highestObjectNumber() + 1)); COSArray index = new COSArray(); for (List continuos : context.getWrittenContiguousGroups()) { index.add(asDirect(continuos.get(0))); index.add(asDirect(continuos.size())); } setItem(COSName.INDEX, asDirectObject(index)); int secondFieldLength = sizeOf(context.highestWritten().getByteOffset()); setItem(COSName.W, asDirectObject( new COSArray(asDirect(1), asDirect(secondFieldLength), asDirect(2)))); try (OutputStream out = createUnfilteredStream()) { for (List continuos : context.getWrittenContiguousGroups()) { for (long key : continuos) { out.write(Optional.ofNullable(context.getWritten(key)).orElse(freeEntry(key, 0)) .toXrefStreamEntry(secondFieldLength, 2)); } } } setItem(COSName.DL, asDirect(getUnfilteredLength())); setItem(COSName.FILTER, asDirectObject(COSName.FLATE_DECODE)); } private static DirectCOSObject asDirect(long num) { return asDirectObject(COSInteger.get(num)); } @Override public boolean encryptable() { return false; } @Override public void encryptable(boolean encryptable) { // do nothing } @Override public void setEncryptor(Function encryptor) { // do nothing } private static int sizeOf(long number) { int size = 0; while (number > 0) { size++; number >>= 8; } return size; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/000077500000000000000000000000001320103431700223145ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/DefaultResourceCache.java000066400000000000000000000113051320103431700271770ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDPropertyList; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.pattern.PDAbstractPattern; import org.sejda.sambox.pdmodel.graphics.shading.PDShading; import org.sejda.sambox.pdmodel.graphics.state.PDExtendedGraphicsState; /** * A resource cached based on SoftReference, retains resources until memory pressure causes them to be garbage * collected. * * @author John Hewson */ public class DefaultResourceCache implements ResourceCache { private final Map> fonts = new HashMap<>(); private final Map> colorSpaces = new HashMap<>(); private final Map> xobjects = new HashMap<>(); private final Map> extGStates = new HashMap<>(); private final Map> shadings = new HashMap<>(); private final Map> patterns = new HashMap<>(); private final Map> properties = new HashMap<>(); @Override public PDFont getFont(COSObjectKey key) { return Optional.ofNullable(fonts.get(key)).map(SoftReference::get).orElse(null); } @Override public void put(COSObjectKey key, PDFont font) { fonts.put(key, new SoftReference<>(font)); } @Override public PDColorSpace getColorSpace(COSObjectKey key) { return Optional.ofNullable(colorSpaces.get(key)).map(SoftReference::get).orElse(null); } @Override public void put(COSObjectKey key, PDColorSpace colorSpace) { colorSpaces.put(key, new SoftReference<>(colorSpace)); } @Override public PDExtendedGraphicsState getExtGState(COSObjectKey key) { return Optional.ofNullable(extGStates.get(key)).map(SoftReference::get).orElse(null); } @Override public void put(COSObjectKey key, PDExtendedGraphicsState extGState) { extGStates.put(key, new SoftReference<>(extGState)); } @Override public PDShading getShading(COSObjectKey key) { return Optional.ofNullable(shadings.get(key)).map(SoftReference::get).orElse(null); } @Override public void put(COSObjectKey key, PDShading shading) { shadings.put(key, new SoftReference<>(shading)); } @Override public PDAbstractPattern getPattern(COSObjectKey key) { return Optional.ofNullable(patterns.get(key)).map(SoftReference::get).orElse(null); } @Override public void put(COSObjectKey key, PDAbstractPattern pattern) { patterns.put(key, new SoftReference<>(pattern)); } @Override public PDPropertyList getProperties(COSObjectKey key) { return Optional.ofNullable(properties.get(key)).map(SoftReference::get).orElse(null); } @Override public void put(COSObjectKey key, PDPropertyList propertyList) { properties.put(key, new SoftReference<>(propertyList)); } @Override public PDXObject getXObject(COSObjectKey key) { return Optional.ofNullable(xobjects.get(key)).map(SoftReference::get).orElse(null); } @Override public void put(COSObjectKey key, PDXObject xobject) { xobjects.put(key, new SoftReference<>(xobject)); } @Override public void clear() { fonts.clear(); colorSpaces.clear(); extGStates.clear(); patterns.clear(); properties.clear(); shadings.clear(); xobjects.clear(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/MissingResourceException.java000066400000000000000000000020641320103431700301610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import java.io.IOException; /** * Thrown when a named resource is missing. */ public final class MissingResourceException extends IOException { public MissingResourceException(String message) { super(message); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDDestinationNameTreeNode.java000066400000000000000000000042731320103431700301210ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDNameTreeNode; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDDestination; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDPageDestination; /** * This class holds all of the name trees that are available at the document level. * * @author Ben Litchfield */ public class PDDestinationNameTreeNode extends PDNameTreeNode { /** * Constructor. */ public PDDestinationNameTreeNode() { super(); } /** * Constructor. * * @param dic The COS dictionary. */ public PDDestinationNameTreeNode(COSDictionary dic) { super(dic); } @Override protected PDPageDestination convertCOSToPD(COSBase base) throws IOException { if (base instanceof COSDictionary) { return (PDPageDestination) PDDestination.create(((COSDictionary) base) .getDictionaryObject(COSName.D)); } return (PDPageDestination) PDDestination.create(base); } @Override protected PDNameTreeNode createChildNode(COSDictionary dic) { return new PDDestinationNameTreeNode(dic); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDDocument.java000066400000000000000000000466661320103431700252030ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import static java.util.Optional.ofNullable; import static org.sejda.io.CountingWritableByteChannel.from; import static org.sejda.sambox.cos.DirectCOSObject.asDirectObject; import static org.sejda.sambox.util.SpecVersionUtils.V1_4; import static org.sejda.sambox.util.SpecVersionUtils.isAtLeast; import static org.sejda.util.RequireUtils.requireNotBlank; import java.awt.Point; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.WritableByteChannel; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Calendar; import java.util.HashSet; import java.util.Optional; import java.util.Set; import org.sejda.io.CountingWritableByteChannel; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSDocument; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.cos.DirectCOSObject; import org.sejda.sambox.encryption.EncryptionContext; import org.sejda.sambox.encryption.MessageDigests; import org.sejda.sambox.encryption.StandardSecurity; import org.sejda.sambox.output.PDDocumentWriter; import org.sejda.sambox.output.WriteOption; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.encryption.AccessPermission; import org.sejda.sambox.pdmodel.encryption.PDEncryption; import org.sejda.sambox.pdmodel.encryption.SecurityHandler; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; import org.sejda.sambox.util.Version; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is the in-memory representation of the PDF document. * * @author Ben Litchfield */ public class PDDocument implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(PDDocument.class); /** * avoid concurrency issues with PDDeviceRGB and deadlock in COSNumber/COSInteger */ static { try { PDDeviceRGB.INSTANCE.toRGBImage( Raster.createBandedRaster(DataBuffer.TYPE_BYTE, 1, 1, 3, new Point(0, 0))); } catch (IOException e) { LOG.warn("This shouldn't happen", e); } try { // TODO remove this and deprecated COSNumber statics in 3.0 COSNumber.get("0"); COSNumber.get("1"); } catch (IOException ex) { // } } private final COSDocument document; private PDDocumentCatalog documentCatalog; private SecurityHandler securityHandler; private boolean open = true; private OnClose onClose; private ResourceCache resourceCache = new DefaultResourceCache(); // fonts to subset before saving private final Set fontsToSubset = new HashSet<>(); public PDDocument() { document = new COSDocument(); document.getCatalog().setItem(COSName.VERSION, COSName.getPDFName("1.4")); COSDictionary pages = new COSDictionary(); document.getCatalog().setItem(COSName.PAGES, pages); pages.setItem(COSName.TYPE, COSName.PAGES); pages.setItem(COSName.KIDS, new COSArray()); pages.setItem(COSName.COUNT, COSInteger.ZERO); } /** * Constructor that uses an existing document. The COSDocument that is passed in must be valid. * * @param document The COSDocument that this document wraps. */ public PDDocument(COSDocument document) { this(document, null); } /** * Constructor that uses an existing document. The COSDocument that is passed in must be valid. * * @param document The COSDocument that this document wraps. * @param securityHandler */ public PDDocument(COSDocument document, SecurityHandler securityHandler) { this.document = document; this.securityHandler = securityHandler; } /** * This will add a page to the document. This is a convenience method, that will add the page to the root of the * hierarchy and set the parent of the page to the root. * * @param page The page to add to the document. */ public void addPage(PDPage page) { requireOpen(); getPages().add(page); } /** * Remove the page from the document. * * @param page The page to remove from the document. */ public void removePage(PDPage page) { requireOpen(); getPages().remove(page); } /** * Remove the page from the document. * * @param pageNumber 0 based index to page number. */ public void removePage(int pageNumber) { requireOpen(); getPages().remove(pageNumber); } /** * This will import and copy the contents from another location. Currently the content stream is stored in a scratch * file. The scratch file is associated with the document. If you are adding a page to this document from another * document and want to copy the contents to this document's scratch file then use this method otherwise just use * the addPage method. * * @param page The page to import. * @return The page that was imported. * */ public PDPage importPage(PDPage page) { requireOpen(); PDPage importedPage = new PDPage(page.getCOSObject().duplicate()); InputStream in = null; try { in = page.getContents(); if (in != null) { PDStream dest = new PDStream(in, COSName.FLATE_DECODE); importedPage.setContents(dest); } addPage(importedPage); } catch (IOException e) { IOUtils.closeQuietly(in); } return importedPage; } /** * @return The document that this layer sits on top of. */ public COSDocument getDocument() { return document; } /** * This will get the document info dictionary. This is guaranteed to not return null. * * @return The documents /Info dictionary */ public PDDocumentInformation getDocumentInformation() { COSDictionary infoDic = document.getTrailer().getCOSObject() .getDictionaryObject(COSName.INFO, COSDictionary.class); if (infoDic == null) { infoDic = new COSDictionary(); document.getTrailer().getCOSObject().setItem(COSName.INFO, infoDic); } return new PDDocumentInformation(infoDic); } /** * This will set the document information for this document. * * @param info The updated document information. */ public void setDocumentInformation(PDDocumentInformation documentInformation) { requireOpen(); document.getTrailer().getCOSObject().setItem(COSName.INFO, documentInformation.getCOSObject()); } /** * This will get the document CATALOG. This is guaranteed to not return null. * * @return The documents /Root dictionary */ public PDDocumentCatalog getDocumentCatalog() { if (documentCatalog == null) { documentCatalog = new PDDocumentCatalog(this, document.getCatalog()); } return documentCatalog; } /** * @return true If this document is encrypted. */ public boolean isEncrypted() { return document.isEncrypted(); } /** * This will get the encryption dictionary for this document. * * @return The encryption dictionary */ public PDEncryption getEncryption() { if (isEncrypted()) { return new PDEncryption(document.getEncryptionDictionary()); } return new PDEncryption(); } /** * @return the list of fonts which will be subset before the document is saved. */ Set getFontsToSubset() { return fontsToSubset; } /** * @param pageIndex the page index * @return the page at the given zero based index. */ public PDPage getPage(int pageIndex) { return getDocumentCatalog().getPages().get(pageIndex); } public PDPageTree getPages() { return getDocumentCatalog().getPages(); } /** * @return The total number of pages in the PDF document. */ public int getNumberOfPages() { return getDocumentCatalog().getPages().getCount(); } /** * Returns the access permissions granted when the document was decrypted. If the document was not decrypted this * method returns the access permission for a document owner (ie can do everything). The returned object is in read * only mode so that permissions cannot be changed. Methods providing access to content should rely on this object * to verify if the current user is allowed to proceed. * * @return the access permissions for the current user on the document. */ public AccessPermission getCurrentAccessPermission() { return ofNullable(securityHandler).map(s -> s.getCurrentAccessPermission()) .orElseGet(AccessPermission::getOwnerAccessPermission); } public SecurityHandler getSecurityHandler() { return securityHandler; } /** * @return The version of the PDF specification to which the document conforms. */ public String getVersion() { String headerVersion = getDocument().getHeaderVersion(); if (isAtLeast(headerVersion, V1_4)) { return ofNullable(getDocumentCatalog().getVersion()) .filter(catalogVersion -> (catalogVersion.compareTo(headerVersion) > 0)) .orElse(headerVersion); } return headerVersion; } /** * Sets the version of the PDF specification to which the document conforms. Downgrading of the document version is * not allowed. * * @param newVersion the new PDF version * */ public void setVersion(String newVersion) { requireOpen(); requireNotBlank(newVersion, "Spec version cannot be blank"); int compare = getVersion().compareTo(newVersion); if (compare > 0) { LOG.info("Spec version downgrade not allowed"); } else if (compare < 0) { if (isAtLeast(newVersion, V1_4)) { getDocumentCatalog().setVersion(newVersion); } getDocument().setHeaderVersion(newVersion); } } /** * If the document is not at the given version or above, it sets the version of the PDF specification to which the * document conforms. * * @param version */ public void requireMinVersion(String version) { if (!isAtLeast(getVersion(), version)) { LOG.debug("Minimum spec version required is {}", version); setVersion(version); } } /** * Sets an action to be performed right before this {@link PDDocument} is closed. * * @param onClose */ public void setOnCloseAction(OnClose onClose) { requireOpen(); this.onClose = onClose; } private void requireOpen() throws IllegalStateException { if (!isOpen()) { throw new IllegalStateException("The document is closed"); } } /** * Generates file identifier as defined in the chap 14.4 PDF 32000-1:2008 and sets it as first and second value for * the ID array in the document trailer. * * @param md5Update * @param encContext */ private void generateFileIdentifier(byte[] md5Update, Optional encContext) { COSString id = generateFileIdentifier(md5Update); encContext.ifPresent(c -> c.documentId(id.getBytes())); DirectCOSObject directId = asDirectObject(id); getDocument().getTrailer().getCOSObject().setItem(COSName.ID, asDirectObject(new COSArray(directId, directId))); } /** * * @param md5Update * @return a newly generated ID based on the input bytes, current timestamp and some other information, to be used * as value of the ID array in the document trailer. */ public COSString generateFileIdentifier(byte[] md5Update) { MessageDigest md5 = MessageDigests.md5(); md5.update(Long.toString(System.currentTimeMillis()).getBytes(StandardCharsets.ISO_8859_1)); md5.update(md5Update); ofNullable(getDocument().getTrailer().getCOSObject().getDictionaryObject(COSName.INFO, COSDictionary.class)).ifPresent(d -> { for (COSBase current : d.getValues()) { md5.update(current.toString().getBytes(StandardCharsets.ISO_8859_1)); } }); COSString retVal = COSString.newInstance(md5.digest()); retVal.setForceHexForm(true); retVal.encryptable(false); return retVal; } /** * Writes the document to the given {@link File}. The document is closed once written. * * @see PDDocument#close() * @param file * @param options * @throws IOException */ public void writeTo(File file, WriteOption... options) throws IOException { writeTo(from(file), null, options); } /** * Writes the document to the file corresponding the given file name. The document is closed once written. * * @see PDDocument#close() * @param filename * @param options * @throws IOException */ public void writeTo(String filename, WriteOption... options) throws IOException { writeTo(from(filename), null, options); } /** * Writes the document to the given {@link WritableByteChannel}. The document is closed once written. * * @see PDDocument#close() * @param channel * @param options * @throws IOException */ public void writeTo(WritableByteChannel channel, WriteOption... options) throws IOException { writeTo(from(channel), null, options); } /** * Writes the document to the given {@link OutputStream}. The document is closed once written. * * @see PDDocument#close() * @param out * @param options * @throws IOException */ public void writeTo(OutputStream out, WriteOption... options) throws IOException { writeTo(from(out), null, options); } /** * Writes the document to the given {@link File} encrypting it using the given security. The document is closed once * written. * * @see PDDocument#close() * @param file * @param security * @param options * @throws IOException */ public void writeTo(File file, StandardSecurity security, WriteOption... options) throws IOException { writeTo(from(file), security, options); } /** * Writes the document to the file corresponding the given file name encrypting it using the given security. The * document is closed once written. * * @see PDDocument#close() * @param filename * @param security * @param options * @throws IOException */ public void writeTo(String filename, StandardSecurity security, WriteOption... options) throws IOException { writeTo(from(filename), security, options); } /** * Writes the document to the given {@link WritableByteChannel} encrypting it using the given security. The document * is closed once written. * * @see PDDocument#close() * @param channel * @param security * @param options * @throws IOException */ public void writeTo(WritableByteChannel channel, StandardSecurity security, WriteOption... options) throws IOException { writeTo(from(channel), security, options); } /** * Writes the document to the given {@link OutputStream} encrypting it using the given security. The document is * closed once written. * * @see PDDocument#close() * @param out * @param security * @param options * @throws IOException */ public void writeTo(OutputStream out, StandardSecurity security, WriteOption... options) throws IOException { writeTo(from(out), security, options); } private void writeTo(CountingWritableByteChannel output, StandardSecurity security, WriteOption... options) throws IOException { requireOpen(); getDocumentInformation().setProducer("SAMBox " + Version.getVersion() + " (www.sejda.org)"); getDocumentInformation().setModificationDate(Calendar.getInstance()); for (PDFont font : fontsToSubset) { font.subset(); } fontsToSubset.clear(); Optional encryptionContext = ofNullable( ofNullable(security).map(EncryptionContext::new).orElse(null)); generateFileIdentifier(output.toString().getBytes(StandardCharsets.ISO_8859_1), encryptionContext); try (PDDocumentWriter writer = new PDDocumentWriter(output, encryptionContext, options)) { writer.write(this); } finally { IOUtils.close(this); } } /** * @return true if the {@link PDDocument} is open */ public boolean isOpen() { return this.open; } /** * Closes the {@link PDDocument} executing an onClose action is set. Once closed the document is pretty much * unusable since most of the mothods requires an open document. * * @see PDDocument#setOnCloseAction(OnClose) */ @Override public void close() throws IOException { if (onClose != null) { onClose.onClose(); } this.resourceCache.clear(); this.open = false; } /** * Action to be performed before the {@link PDDocument} is close * * @author Andrea Vacondio */ @FunctionalInterface public static interface OnClose { /** * Sets an action to be performed right before this {@link PDDocument} is closed. * * @param onClose */ void onClose() throws IOException; } /** * Returns the resource cache associated with this document, or null if there is none. */ public ResourceCache getResourceCache() { return resourceCache; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDDocumentCatalog.java000066400000000000000000000440151320103431700264600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import static java.util.Optional.ofNullable; import static org.sejda.sambox.util.SpecVersionUtils.V1_5; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDDestinationOrAction; import org.sejda.sambox.pdmodel.common.PDMetadata; import org.sejda.sambox.pdmodel.common.PDPageLabels; import org.sejda.sambox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo; import org.sejda.sambox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot; import org.sejda.sambox.pdmodel.graphics.color.PDOutputIntent; import org.sejda.sambox.pdmodel.graphics.optionalcontent.PDOptionalContentProperties; import org.sejda.sambox.pdmodel.interactive.action.PDActionFactory; import org.sejda.sambox.pdmodel.interactive.action.PDDocumentCatalogAdditionalActions; import org.sejda.sambox.pdmodel.interactive.action.PDURIDictionary; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDDestination; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDPageDestination; import org.sejda.sambox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; import org.sejda.sambox.pdmodel.interactive.form.PDAcroForm; import org.sejda.sambox.pdmodel.interactive.pagenavigation.PDThread; import org.sejda.sambox.pdmodel.interactive.viewerpreferences.PDViewerPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The Document Catalog of a PDF. * * @author Ben Litchfield */ public class PDDocumentCatalog implements COSObjectable { private static final Logger LOG = LoggerFactory.getLogger(PDDocumentCatalog.class); private final COSDictionary root; private final PDDocument document; private PDAcroForm cachedAcroForm; /** * Constructor. AcroForm. * * @param doc The document that this catalog is part of. */ public PDDocumentCatalog(PDDocument doc) { document = doc; root = new COSDictionary(); root.setItem(COSName.TYPE, COSName.CATALOG); document.getDocument().getTrailer().getCOSObject().setItem(COSName.ROOT, root); } /** * Constructor. * * @param doc The document that this catalog is part of. * @param rootDictionary The root dictionary that this object wraps. */ public PDDocumentCatalog(PDDocument doc, COSDictionary rootDictionary) { document = doc; root = rootDictionary; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return root; } /** * Get the documents AcroForm. This will return null if no AcroForm is part of the document. * * @return The document's AcroForm. */ public PDAcroForm getAcroForm() { if (cachedAcroForm == null) { COSDictionary dict = root.getDictionaryObject(COSName.ACRO_FORM, COSDictionary.class); cachedAcroForm = dict == null ? null : new PDAcroForm(document, dict); } return cachedAcroForm; } /** * Sets the AcroForm for this catalog. * * @param acroForm The new AcroForm. */ public void setAcroForm(PDAcroForm acroForm) { root.setItem(COSName.ACRO_FORM, acroForm); cachedAcroForm = null; } /** * Returns all pages in the document, as a page tree. */ public PDPageTree getPages() { // TODO cache this since it's probably going to be called often and we want to reduce GC return new PDPageTree((COSDictionary) root.getDictionaryObject(COSName.PAGES), document); } /** * Get the viewer preferences associated with this document or null if they do not exist. * * @return The document's viewer preferences. */ public PDViewerPreferences getViewerPreferences() { return ofNullable(root.getDictionaryObject(COSName.VIEWER_PREFERENCES, COSDictionary.class)) .map(PDViewerPreferences::new).orElse(null); } /** * Sets the viewer preferences. * * @param prefs The new viewer preferences. */ public void setViewerPreferences(PDViewerPreferences prefs) { root.setItem(COSName.VIEWER_PREFERENCES, prefs); } /** * Get the outline associated with this document or null if it does not exist. * * @return The document's outline. */ public PDDocumentOutline getDocumentOutline() { return ofNullable(root.getDictionaryObject(COSName.OUTLINES, COSDictionary.class)) .map(PDDocumentOutline::new).orElse(null); } /** * Sets the document outlines. * * @param outlines The new document outlines. */ public void setDocumentOutline(PDDocumentOutline outlines) { root.setItem(COSName.OUTLINES, outlines); } /** * Returns the document's article threads. */ public List getThreads() { COSArray array = (COSArray) root.getDictionaryObject(COSName.THREADS); if (array == null) { array = new COSArray(); root.setItem(COSName.THREADS, array); } List pdObjects = new ArrayList<>(); for (int i = 0; i < array.size(); i++) { pdObjects.add(new PDThread((COSDictionary) array.getObject(i))); } return new COSArrayList<>(pdObjects, array); } /** * Sets the list of threads for this pdf document. * * @param threads The list of threads, or null to clear it. */ public void setThreads(List threads) { root.setItem(COSName.THREADS, COSArrayList.converterToCOSArray(threads)); } /** * Get the metadata that is part of the document catalog. This will return null if there is no meta data for this * object. * * @return The metadata for this object. */ public PDMetadata getMetadata() { return ofNullable(root.getDictionaryObject(COSName.METADATA, COSStream.class)) .map(PDMetadata::new).orElse(null); } /** * Sets the metadata for this object. This can be null. * * @param meta The meta data for this object. */ public void setMetadata(PDMetadata meta) { root.setItem(COSName.METADATA, meta); } /** * Sets the Document Open Action for this object. * * @param action The action you want to perform. */ public void setOpenAction(PDDestinationOrAction action) { root.setItem(COSName.OPEN_ACTION, action); } /** * Get the Document Open Action for this object. * * @return The action to perform when the document is opened. * @throws IOException If there is an error creating the destination or action. */ public PDDestinationOrAction getOpenAction() throws IOException { COSBase openAction = root.getDictionaryObject(COSName.OPEN_ACTION); if (openAction == null) { return null; } else if (openAction instanceof COSDictionary) { return PDActionFactory.createAction((COSDictionary) openAction); } else if (openAction instanceof COSArray) { return PDDestination.create(openAction); } else { throw new IOException("Unknown OpenAction " + openAction); } } /** * @return The Additional Actions for this Document */ public PDDocumentCatalogAdditionalActions getActions() { COSDictionary addAction = root.getDictionaryObject(COSName.AA, COSDictionary.class); if (addAction == null) { addAction = new COSDictionary(); root.setItem(COSName.AA, addAction); } return new PDDocumentCatalogAdditionalActions(addAction); } /** * Sets the additional actions for the document. * * @param actions The actions that are associated with this document. */ public void setActions(PDDocumentCatalogAdditionalActions actions) { root.setItem(COSName.AA, actions); } /** * @return The names dictionary for this document or null if none exist. */ public PDDocumentNameDictionary getNames() { return ofNullable(root.getDictionaryObject(COSName.NAMES, COSDictionary.class)) .map(PDDocumentNameDictionary::new).orElse(null); } /** * @return The named destinations dictionary for this document or null if none exists. */ public PDDocumentNameDestinationDictionary getDests() { return ofNullable(root.getDictionaryObject(COSName.DESTS, COSDictionary.class)) .map(PDDocumentNameDestinationDictionary::new).orElse(null); } /** * Sets the names dictionary for the document. * * @param names The names dictionary that is associated with this document. */ public void setNames(PDDocumentNameDictionary names) { root.setItem(COSName.NAMES, names); } /** * Get info about doc's usage of tagged features. This will return null if there is no information. * * @return The new mark info. */ public PDMarkInfo getMarkInfo() { return ofNullable(root.getDictionaryObject(COSName.MARK_INFO, COSDictionary.class)) .map(PDMarkInfo::new).orElse(null); } /** * Set information about the doc's usage of tagged features. * * @param markInfo The new MarkInfo data. */ public void setMarkInfo(PDMarkInfo markInfo) { root.setItem(COSName.MARK_INFO, markInfo); } /** * Get the list of OutputIntents defined in the document. * * @return The list of PDOutputIntent */ public List getOutputIntents() { List retval = new ArrayList<>(); COSArray array = root.getDictionaryObject(COSName.OUTPUT_INTENTS, COSArray.class); if (array != null) { for (COSBase cosBase : array) { PDOutputIntent oi = new PDOutputIntent((COSDictionary) cosBase.getCOSObject()); retval.add(oi); } } return retval; } /** * Add an OutputIntent to the list. If there is not OutputIntent, the list is created and the first element added. * * @param outputIntent the OutputIntent to add. */ public void addOutputIntent(PDOutputIntent outputIntent) { COSArray array = root.getDictionaryObject(COSName.OUTPUT_INTENTS, COSArray.class); if (array == null) { array = new COSArray(); root.setItem(COSName.OUTPUT_INTENTS, array); } array.add(outputIntent.getCOSObject()); } /** * Replace the list of OutputIntents of the document. * * @param outputIntents the list of OutputIntents, if the list is empty all OutputIntents are removed. */ public void setOutputIntents(List outputIntents) { COSArray array = new COSArray(); for (PDOutputIntent intent : outputIntents) { array.add(intent.getCOSObject()); } root.setItem(COSName.OUTPUT_INTENTS, array); } /** * Returns the page display mode. */ public PageMode getPageMode() { String mode = root.getNameAsString(COSName.PAGE_MODE); if (mode != null) { try { return PageMode.fromString(mode); } catch (IllegalArgumentException ex) { LOG.debug(String.format("Unrecognized page mode %s", mode)); } } return PageMode.USE_NONE; } /** * Sets the page mode. * * @param mode The new page mode. */ public void setPageMode(PageMode mode) { root.setName(COSName.PAGE_MODE, mode.stringValue()); } /** * Returns the page layout. */ public PageLayout getPageLayout() { String mode = root.getNameAsString(COSName.PAGE_LAYOUT); if (mode != null) { try { return PageLayout.fromString(mode); } catch (IllegalArgumentException ex) { LOG.debug(String.format("Unrecognized page layout %s", mode)); } } return PageLayout.SINGLE_PAGE; } /** * Sets the page layout. * * @param layout The new page layout. */ public void setPageLayout(PageLayout layout) { root.setName(COSName.PAGE_LAYOUT, layout.stringValue()); } /** * Returns the document-level URI. */ public PDURIDictionary getURI() { return ofNullable(root.getDictionaryObject(COSName.URI, COSDictionary.class)) .map(PDURIDictionary::new).orElse(null); } /** * Sets the document level URI. * * @param uri The new document level URI. */ public void setURI(PDURIDictionary uri) { root.setItem(COSName.URI, uri); } /** * Get the document's structure tree root, or null if none exists. */ public PDStructureTreeRoot getStructureTreeRoot() { return ofNullable(root.getDictionaryObject(COSName.STRUCT_TREE_ROOT, COSDictionary.class)) .map(PDStructureTreeRoot::new).orElse(null); } /** * Sets the document's structure tree root. * * @param treeRoot The new structure tree. */ public void setStructureTreeRoot(PDStructureTreeRoot treeRoot) { root.setItem(COSName.STRUCT_TREE_ROOT, treeRoot); } /** * Returns the language for the document, or null. */ public String getLanguage() { return root.getString(COSName.LANG); } /** * Sets the Language for the document. * * @param language The new document language. */ public void setLanguage(String language) { root.setString(COSName.LANG, language); } /** * Returns the PDF specification version this document conforms to. * * @return the PDF version (e.g. "1.4") */ public String getVersion() { return root.getNameAsString(COSName.VERSION); } /** * Sets the PDF specification version this document conforms to. * * @param version the PDF version */ public void setVersion(String version) { root.setName(COSName.VERSION, version); } /** * Returns the page labels descriptor of the document. * * @return the page labels descriptor of the document. * @throws IOException If there is a problem retrieving the page labels. */ public PDPageLabels getPageLabels() throws IOException { COSDictionary dict = (COSDictionary) root.getDictionaryObject(COSName.PAGE_LABELS); return dict == null ? null : new PDPageLabels(dict); } /** * Sets the page label descriptor for the document. * * @param labels the new page label descriptor to set. */ public void setPageLabels(PDPageLabels labels) { root.setItem(COSName.PAGE_LABELS, labels); } /** * Get the optional content properties dictionary associated with this document. * * @return the optional properties dictionary or null if it is not present */ public PDOptionalContentProperties getOCProperties() { return ofNullable(root.getDictionaryObject(COSName.OCPROPERTIES, COSDictionary.class)) .map(PDOptionalContentProperties::new).orElse(null); } /** * Sets the optional content properties dictionary. * * @param ocProperties the optional properties dictionary */ public void setOCProperties(PDOptionalContentProperties ocProperties) { root.setItem(COSName.OCPROPERTIES, ocProperties); // optional content groups require PDF 1.5 if (ocProperties != null) { document.requireMinVersion(V1_5); } } /** * Looks up for the {@link PDPageDestination} referenced by the given {@link PDNamedDestination} * * @param namedDest * @return the destination or null if nothing is fond * @throws IOException */ public PDPageDestination findNamedDestinationPage(PDNamedDestination namedDest) throws IOException { PDPageDestination namesDest = ofNullable(getNames()).map(PDDocumentNameDictionary::getDests) .map(tree -> tree.getValue(namedDest.getNamedDestination())).orElse(null); if (namesDest == null) { // Look up /Dests dictionary from catalog PDDocumentNameDestinationDictionary nameDestDict = getDests(); if (nameDestDict != null) { return (PDPageDestination) nameDestDict .getDestination(namedDest.getNamedDestination()); } } return namesDest; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDDocumentInformation.java000066400000000000000000000202051320103431700273660ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import java.util.Calendar; import java.util.Set; import java.util.TreeSet; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; /** * This is the document metadata. Each getXXX method will return the entry if * it exists or null if it does not exist. If you pass in null for the setXXX * method then it will clear the value. * * @author Ben Litchfield * @author Gerardo Ortiz * */ public class PDDocumentInformation extends PDDictionaryWrapper { public PDDocumentInformation() { super(); } /** * Creates a new instance with a given COS dictionary. * * @param dictionary the dictionary */ public PDDocumentInformation(COSDictionary dictionary) { super(dictionary); } /** * Return the properties String value. *

* Allows to retrieve the * low level date for validation purposes. *

* * @param propertyKey the dictionaries key * @return the properties value */ public Object getPropertyStringValue(String propertyKey) { return getCOSObject().getString(propertyKey); } /** * This will get the title of the document. This will return null if no title exists. * * @return The title of the document. */ public String getTitle() { return getCOSObject().getString(COSName.TITLE); } /** * This will set the title of the document. * * @param title The new title for the document. */ public void setTitle( String title ) { getCOSObject().setString(COSName.TITLE, title); } /** * This will get the author of the document. This will return null if no author exists. * * @return The author of the document. */ public String getAuthor() { return getCOSObject().getString(COSName.AUTHOR); } /** * This will set the author of the document. * * @param author The new author for the document. */ public void setAuthor( String author ) { getCOSObject().setString(COSName.AUTHOR, author); } /** * This will get the subject of the document. This will return null if no subject exists. * * @return The subject of the document. */ public String getSubject() { return getCOSObject().getString(COSName.SUBJECT); } /** * This will set the subject of the document. * * @param subject The new subject for the document. */ public void setSubject( String subject ) { getCOSObject().setString(COSName.SUBJECT, subject); } /** * This will get the keywords of the document. This will return null if no keywords exists. * * @return The keywords of the document. */ public String getKeywords() { return getCOSObject().getString(COSName.KEYWORDS); } /** * This will set the keywords of the document. * * @param keywords The new keywords for the document. */ public void setKeywords( String keywords ) { getCOSObject().setString(COSName.KEYWORDS, keywords); } /** * This will get the creator of the document. This will return null if no creator exists. * * @return The creator of the document. */ public String getCreator() { return getCOSObject().getString(COSName.CREATOR); } /** * This will set the creator of the document. * * @param creator The new creator for the document. */ public void setCreator( String creator ) { getCOSObject().setString(COSName.CREATOR, creator); } /** * This will get the producer of the document. This will return null if no producer exists. * * @return The producer of the document. */ public String getProducer() { return getCOSObject().getString(COSName.PRODUCER); } /** * This will set the producer of the document. * * @param producer The new producer for the document. */ public void setProducer( String producer ) { getCOSObject().setString(COSName.PRODUCER, producer); } /** * This will get the creation date of the document. This will return null if no creation date exists. * * @return The creation date of the document. */ public Calendar getCreationDate() { return getCOSObject().getDate(COSName.CREATION_DATE); } /** * This will set the creation date of the document. * * @param date The new creation date for the document. */ public void setCreationDate( Calendar date ) { getCOSObject().setDate(COSName.CREATION_DATE, date); } /** * This will get the modification date of the document. This will return null if no modification date exists. * * @return The modification date of the document. */ public Calendar getModificationDate() { return getCOSObject().getDate(COSName.MOD_DATE); } /** * This will set the modification date of the document. * * @param date The new modification date for the document. */ public void setModificationDate( Calendar date ) { getCOSObject().setDate(COSName.MOD_DATE, date); } /** * This will get the trapped value for the document. * This will return null if one is not found. * * @return The trapped value for the document. */ public String getTrapped() { return getCOSObject().getNameAsString(COSName.TRAPPED); } /** * This will get the keys of all metadata getCOSObject()rmation fields for the document. * * @return all metadata key strings. * @since Apache PDFBox 1.3.0 */ public Set getMetadataKeys() { Set keys = new TreeSet(); for (COSName key : getCOSObject().keySet()) { keys.add(key.getName()); } return keys; } /** * This will get the value of a custom metadata getCOSObject()rmation field for the document. This will return null * if one is not found. * * @param fieldName Name of custom metadata field from pdf document. * * @return String Value of metadata field */ public String getCustomMetadataValue(String fieldName) { return getCOSObject().getString(fieldName); } /** * Set the custom metadata value. * * @param fieldName The name of the custom metadata field. * @param fieldValue The value to the custom metadata field. */ public void setCustomMetadataValue( String fieldName, String fieldValue ) { getCOSObject().setString(fieldName, fieldValue); } /** * This will set the trapped of the document. This will be * 'True', 'False', or 'Unknown'. * * @param value The new trapped value for the document. */ public void setTrapped( String value ) { if( value != null && !value.equals( "True" ) && !value.equals( "False" ) && !value.equals( "Unknown" ) ) { throw new RuntimeException( "Valid values for trapped are " + "'True', 'False', or 'Unknown'" ); } getCOSObject().setName(COSName.TRAPPED, value); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDDocumentNameDestinationDictionary.java000066400000000000000000000044401320103431700322140ustar00rootroot00000000000000/* * Copyright 2015 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDDestination; /** * This encapsulates the "dictionary of names and corresponding destinations" for the /Desta entry in the document * catalog. * * @author Tilman Hausherr */ public class PDDocumentNameDestinationDictionary implements COSObjectable { private final COSDictionary nameDictionary; /** * Constructor. * * @param dict The dictionary of names and corresponding destinations. */ public PDDocumentNameDestinationDictionary(COSDictionary dict) { this.nameDictionary = dict; } /** * Convert this standard java object to a COS object. * * @return The cos dictionary for this object. */ @Override public COSDictionary getCOSObject() { return nameDictionary; } /** * Returns the destination corresponding to the parameter. * * @param name The destination name. * @return The destination for that name, or null if there isn't any. * * @throws IOException if something goes wrong when creating the destination object. */ public PDDestination getDestination(String name) throws IOException { COSBase item = nameDictionary.getDictionaryObject(name); if (item instanceof COSDictionary) { return PDDestination.create(((COSDictionary) item).getDictionaryObject(COSName.D)); } return PDDestination.create(item); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDDocumentNameDictionary.java000066400000000000000000000104701320103431700300120ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import static java.util.Optional.ofNullable; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This class holds all of the name trees that are available at the document level. * * @author Ben Litchfield */ public class PDDocumentNameDictionary implements COSObjectable { private final COSDictionary nameDictionary; /** * @param cat The document catalog that this dictionary is part of. */ public PDDocumentNameDictionary(PDDocumentCatalog cat) { COSBase names = cat.getCOSObject().getDictionaryObject(COSName.NAMES); if (names != null) { nameDictionary = (COSDictionary) names; } else { nameDictionary = new COSDictionary(); cat.getCOSObject().setItem(COSName.NAMES, nameDictionary); } } /** * Constructor. * * @param names The names dictionary. */ public PDDocumentNameDictionary(COSDictionary names) { nameDictionary = names; } /** * Convert this standard java object to a COS object. * * @return The cos dictionary for this object. */ @Override public COSDictionary getCOSObject() { return nameDictionary; } /** * Get the destination named tree node. The value in this name tree will be PDDestination objects. * * @return The destination name tree node. */ public PDDestinationNameTreeNode getDests() { return ofNullable(nameDictionary.getDictionaryObject(COSName.DESTS, COSDictionary.class)) .map(PDDestinationNameTreeNode::new).orElse(null); } /** * Set the named destinations that are associated with this document. * * @param dests The destination names. */ public void setDests(PDDestinationNameTreeNode dests) { nameDictionary.setItem(COSName.DESTS, dests); } /** * Get the embedded files named tree node. The value in this name tree will be PDComplexFileSpecification objects. * * @return The embedded files name tree node. */ public PDEmbeddedFilesNameTreeNode getEmbeddedFiles() { PDEmbeddedFilesNameTreeNode retval = null; COSDictionary dic = (COSDictionary) nameDictionary .getDictionaryObject(COSName.EMBEDDED_FILES); if (dic != null) { retval = new PDEmbeddedFilesNameTreeNode(dic); } return retval; } /** * Set the named embedded files that are associated with this document. * * @param ef The new embedded files */ public void setEmbeddedFiles(PDEmbeddedFilesNameTreeNode ef) { nameDictionary.setItem(COSName.EMBEDDED_FILES, ef); } /** * Get the document level javascript entries. The value in this name tree will be PDTextStream. * * @return The document level named javascript. */ public PDJavascriptNameTreeNode getJavaScript() { return ofNullable( nameDictionary.getDictionaryObject(COSName.JAVA_SCRIPT, COSDictionary.class)) .map(PDJavascriptNameTreeNode::new).orElse(null); } /** * Set the named javascript entries that are associated with this document. * * @param js The new Javascript entries. */ public void setJavascript(PDJavascriptNameTreeNode js) { nameDictionary.setItem(COSName.JAVA_SCRIPT, js); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDEmbeddedFilesNameTreeNode.java000066400000000000000000000035241320103431700303120ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.pdmodel.common.PDNameTreeNode; import org.sejda.sambox.pdmodel.common.filespecification.PDComplexFileSpecification; /** * This class holds all of the name trees that are available at the document level. * * @author Ben Litchfield */ public class PDEmbeddedFilesNameTreeNode extends PDNameTreeNode { /** * Constructor. */ public PDEmbeddedFilesNameTreeNode() { super(); } /** * @param dic The COS dictionary. */ public PDEmbeddedFilesNameTreeNode( COSDictionary dic ) { super(dic); } @Override protected PDComplexFileSpecification convertCOSToPD(COSBase base) { return new PDComplexFileSpecification( (COSDictionary)base ); } @Override protected PDNameTreeNode createChildNode(COSDictionary dic) { return new PDEmbeddedFilesNameTreeNode(dic); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDJavascriptNameTreeNode.java000066400000000000000000000041601320103431700277410ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.pdmodel.common.PDNameTreeNode; import org.sejda.sambox.pdmodel.interactive.action.PDActionFactory; import org.sejda.sambox.pdmodel.interactive.action.PDActionJavaScript; /** * This class holds all of the name trees that are available at the document level. * * @author Ben Litchfield */ public class PDJavascriptNameTreeNode extends PDNameTreeNode { /** * Constructor. */ public PDJavascriptNameTreeNode() { super(); } /** * Constructor. * * @param dic The COS dictionary. */ public PDJavascriptNameTreeNode( COSDictionary dic ) { super(dic); } @Override protected PDActionJavaScript convertCOSToPD( COSBase base ) throws IOException { if (!(base instanceof COSDictionary)) { throw new IOException( "Error creating Javascript object, expected a COSDictionary and not " + base); } return (PDActionJavaScript)PDActionFactory.createAction((COSDictionary) base); } @Override protected PDNameTreeNode createChildNode(COSDictionary dic) { return new PDJavascriptNameTreeNode(dic); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDPage.java000066400000000000000000000555171320103431700242740ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import java.awt.Point; import java.awt.geom.Point2D; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.SequenceInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.sejda.sambox.contentstream.PDContentStream; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDMetadata; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.interactive.action.PDPageAdditionalActions; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotation; import org.sejda.sambox.pdmodel.interactive.pagenavigation.PDThreadBead; import org.sejda.sambox.pdmodel.interactive.pagenavigation.PDTransition; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A page in a PDF document. * * @author Ben Litchfield */ public class PDPage implements COSObjectable, PDContentStream { private static final Logger LOG = LoggerFactory.getLogger(PDPage.class); private final COSDictionary page; private PDResources pageResources; private ResourceCache resourceCache; private PDRectangle mediaBox; /** * Creates a new PDPage instance for embedding, with a size of U.S. Letter (8.5 x 11 inches). */ public PDPage() { this(PDRectangle.LETTER); } /** * Creates a new instance of PDPage for embedding. * * @param mediaBox The MediaBox of the page. */ public PDPage(PDRectangle mediaBox) { page = new COSDictionary(); page.setItem(COSName.TYPE, COSName.PAGE); page.setItem(COSName.MEDIA_BOX, mediaBox); } /** * Creates a new instance of PDPage for reading. * * @param pageDictionary A page dictionary in a PDF document. */ public PDPage(COSDictionary pageDictionary) { page = pageDictionary; } /** * Creates a new instance of PDPage for reading. * * @param pageDictionary A page dictionary in a PDF document. */ PDPage(COSDictionary pageDictionary, ResourceCache resourceCache) { page = pageDictionary; this.resourceCache = resourceCache; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return page; } /** * Returns the content streams which make up this page. * * @return content stream iterator */ public Iterator getContentStreams() { List streams = new ArrayList<>(); COSBase base = page.getDictionaryObject(COSName.CONTENTS); if (base instanceof COSStream) { streams.add(new PDStream((COSStream) base)); } else if (base instanceof COSArray && ((COSArray) base).size() > 0) { COSArray array = (COSArray) base; for (int i = 0; i < array.size(); i++) { COSStream stream = (COSStream) array.getObject(i); if (nonNull(stream)) { streams.add(new PDStream(stream)); } } } return streams.iterator(); } @Override public InputStream getContents() throws IOException { COSBase base = page.getDictionaryObject(COSName.CONTENTS); if (base instanceof COSStream) { return ((COSStream) base).getUnfilteredStream(); } else if (base instanceof COSArray && ((COSArray) base).size() > 0) { COSArray streams = (COSArray) base; byte[] delimiter = new byte[] { '\n' }; List inputStreams = new ArrayList<>(); for (int i = 0; i < streams.size(); i++) { COSBase strm = streams.getObject(i); if (strm instanceof COSStream) { COSStream stream = (COSStream) strm; inputStreams.add(stream.getUnfilteredStream()); inputStreams.add(new ByteArrayInputStream(delimiter)); } } return new SequenceInputStream(Collections.enumeration(inputStreams)); } return new ByteArrayInputStream(new byte[0]); } /** * Returns true if this page has contents. */ public boolean hasContents() { COSBase contents = page.getDictionaryObject(COSName.CONTENTS); if (contents instanceof COSStream) { return ((COSStream) contents).size() > 0; } else if (contents instanceof COSArray) { return ((COSArray) contents).size() > 0; } return false; } /** * A dictionary containing any resources required by the page. */ @Override public PDResources getResources() { if (pageResources == null) { pageResources = new PDResources(ofNullable( (COSDictionary) PDPageTree.getInheritableAttribute(page, COSName.RESOURCES)) .orElseGet(() -> { COSDictionary emptyRes = new COSDictionary(); // it's illegal for a page to not have resources, either direct or inherited. According // to the specs "If the page requires no resources, the value of this entry shall be an // empty dictionary." so we fix it. page.setItem(COSName.RESOURCES, emptyRes); return emptyRes; }), resourceCache); } return pageResources; } /** * This will set the resources for this page. * * @param resources The new resources for this page. */ public void setResources(PDResources resources) { pageResources = resources; if (resources != null) { page.setItem(COSName.RESOURCES, resources); } else { page.removeItem(COSName.RESOURCES); } } /** * This will get the key of this Page in the structural parent tree. * * @return the integer key of the page's entry in the structural parent tree */ public int getStructParents() { return page.getInt(COSName.STRUCT_PARENTS, 0); } /** * This will set the key for this page in the structural parent tree. * * @param structParents The new key for this page. */ public void setStructParents(int structParents) { page.setInt(COSName.STRUCT_PARENTS, structParents); } @Override public PDRectangle getBBox() { return getCropBox(); } @Override public Matrix getMatrix() { // todo: take into account user-space unit redefinition as scale? return new Matrix(); } /** * A rectangle, expressed in default user space units, defining the boundaries of the physical medium on which the * page is intended to be displayed or printed. */ public PDRectangle getMediaBox() { if (mediaBox == null) { COSArray array = (COSArray) PDPageTree.getInheritableAttribute(page, COSName.MEDIA_BOX); if (array != null) { mediaBox = new PDRectangle(array); } } if (mediaBox == null) { LOG.debug("Can't find MediaBox, will use U.S. Letter"); mediaBox = PDRectangle.LETTER; } return mediaBox; } public PDRectangle getMediaBoxRaw() { return PDRectangle .rectangleFrom(page.getDictionaryObject(COSName.MEDIA_BOX, COSArray.class)); } /** * This will set the mediaBox for this page. * * @param mediaBox The new mediaBox for this page. */ public void setMediaBox(PDRectangle mediaBox) { this.mediaBox = mediaBox; if (mediaBox == null) { page.removeItem(COSName.MEDIA_BOX); } else { page.setItem(COSName.MEDIA_BOX, mediaBox); } } /** * A rectangle, expressed in default user space units, defining the visible region of default user space. When the * page is displayed or printed, its contents are to be clipped (cropped) to this rectangle. */ public PDRectangle getCropBox() { COSArray array = (COSArray) PDPageTree.getInheritableAttribute(page, COSName.CROP_BOX); if (array != null) { return clipToMediaBox(new PDRectangle(array)); } return getMediaBox(); } public PDRectangle getCropBoxRaw() { return PDRectangle .rectangleFrom(page.getDictionaryObject(COSName.CROP_BOX, COSArray.class)); } /** * This will set the CropBox for this page. * * @param cropBox The new CropBox for this page. */ public void setCropBox(PDRectangle cropBox) { if (cropBox == null) { page.removeItem(COSName.CROP_BOX); } else { page.setItem(COSName.CROP_BOX, cropBox.getCOSObject()); } } /** * A rectangle, expressed in default user space units, defining the region to which the contents of the page should * be clipped when output in a production environment. The default is the CropBox. * * @return The BleedBox attribute. */ public PDRectangle getBleedBox() { COSArray array = page.getDictionaryObject(COSName.BLEED_BOX, COSArray.class); if (nonNull(array) && inMediaBoxBounds(new PDRectangle(array))) { return new PDRectangle(array); } return getCropBox(); } public PDRectangle getBleedBoxRaw() { return PDRectangle .rectangleFrom(page.getDictionaryObject(COSName.BLEED_BOX, COSArray.class)); } /** * This will set the BleedBox for this page. * * @param bleedBox The new BleedBox for this page. */ public void setBleedBox(PDRectangle bleedBox) { if (bleedBox == null) { page.removeItem(COSName.BLEED_BOX); } else { page.setItem(COSName.BLEED_BOX, bleedBox); } } /** * A rectangle, expressed in default user space units, defining the intended dimensions of the finished page after * trimming. The default is the CropBox. * * @return The TrimBox attribute. */ public PDRectangle getTrimBox() { COSArray array = page.getDictionaryObject(COSName.TRIM_BOX, COSArray.class); if (nonNull(array) && inMediaBoxBounds(new PDRectangle(array))) { return new PDRectangle(array); } return getCropBox(); } public PDRectangle getTrimBoxRaw() { return PDRectangle .rectangleFrom(page.getDictionaryObject(COSName.TRIM_BOX, COSArray.class)); } /** * This will set the TrimBox for this page. * * @param trimBox The new TrimBox for this page. */ public void setTrimBox(PDRectangle trimBox) { if (trimBox == null) { page.removeItem(COSName.TRIM_BOX); } else { page.setItem(COSName.TRIM_BOX, trimBox); } } /** * A rectangle, expressed in default user space units, defining the extent of the page's meaningful content * (including potential white space) as intended by the page's creator The default is the CropBox. * * @return The ArtBox attribute. */ public PDRectangle getArtBox() { COSArray array = page.getDictionaryObject(COSName.ART_BOX, COSArray.class); if (nonNull(array) && inMediaBoxBounds(new PDRectangle(array))) { return new PDRectangle(array); } return getCropBox(); } public PDRectangle getArtBoxRaw() { return PDRectangle.rectangleFrom(page.getDictionaryObject(COSName.ART_BOX, COSArray.class)); } /** * This will set the ArtBox for this page. * * @param artBox The new ArtBox for this page. */ public void setArtBox(PDRectangle artBox) { if (artBox == null) { page.removeItem(COSName.ART_BOX); } else { page.setItem(COSName.ART_BOX, artBox); } } /** * Clips the given box to the bounds of the media box. */ private PDRectangle clipToMediaBox(PDRectangle box) { PDRectangle mediaBox = getMediaBox(); PDRectangle result = new PDRectangle(); result.setLowerLeftX(Math.max(mediaBox.getLowerLeftX(), box.getLowerLeftX())); result.setLowerLeftY(Math.max(mediaBox.getLowerLeftY(), box.getLowerLeftY())); result.setUpperRightX(Math.min(mediaBox.getUpperRightX(), box.getUpperRightX())); result.setUpperRightY(Math.min(mediaBox.getUpperRightY(), box.getUpperRightY())); return result; } /** * @return true if the given box fits into the media box */ private boolean inMediaBoxBounds(PDRectangle box) { PDRectangle mediaBox = getMediaBox(); return mediaBox.getLowerLeftX() <= box.getLowerLeftX() && mediaBox.getLowerLeftY() <= box.getLowerLeftY() && mediaBox.getUpperRightX() >= box.getUpperRightX() && mediaBox.getUpperRightY() >= box.getUpperRightY(); } /** * Returns the rotation angle in degrees by which the page should be rotated clockwise when displayed or printed. * Valid values in a PDF must be a multiple of 90. * * @return The rotation angle in degrees in normalized form (0, 90, 180 or 270) or 0 if invalid or not set at this * level. */ public int getRotation() { COSBase obj = PDPageTree.getInheritableAttribute(page, COSName.ROTATE); if (obj instanceof COSNumber) { int rotationAngle = ((COSNumber) obj).intValue(); if (rotationAngle % 90 == 0) { return (rotationAngle % 360 + 360) % 360; } } return 0; } /** * This will set the rotation for this page. * * @param rotation The new rotation for this page in degrees. */ public void setRotation(int rotation) { page.setInt(COSName.ROTATE, rotation); } /** * This will set the contents of this page. * * @param contents The new contents of the page. */ public void setContents(PDStream contents) { page.setItem(COSName.CONTENTS, contents); } /** * This will set the contents of this page. * * @param contents Array of new contents of the page. */ public void setContents(List contents) { COSArray array = new COSArray(); for (PDStream stream : contents) { array.add(stream); } page.setItem(COSName.CONTENTS, array); } /** * This will get a list of PDThreadBead objects, which are article threads in the document. This will return an * empty list of there are no thread beads. * * @return A list of article threads on this page. */ public List getThreadBeads() { COSArray beads = page.getDictionaryObject(COSName.B, COSArray.class); if (beads == null) { return new COSArrayList<>(page, COSName.B); } List actuals = new ArrayList<>(); for (int i = 0; i < beads.size(); i++) { COSBase item = beads.getObject(i); PDThreadBead bead = ofNullable(item).filter(d -> d instanceof COSDictionary) .map(COSDictionary.class::cast).map(PDThreadBead::new).orElseGet(() -> { LOG.warn("Ignored thread bead expected to be a dictionary but was {}", item); return null; }); if (nonNull(bead)) { actuals.add(bead); } } return new COSArrayList<>(actuals, beads); } /** * This will set the list of thread beads. * * @param beads A list of PDThreadBead objects or null. */ public void setThreadBeads(List beads) { page.setItem(COSName.B, COSArrayList.converterToCOSArray(beads)); } /** * Get the metadata that is part of the document catalog. This will return null if there is no meta data for this * object. * * @return The metadata for this object. */ public PDMetadata getMetadata() { return ofNullable(page.getDictionaryObject(COSName.METADATA, COSStream.class)) .map(PDMetadata::new).orElse(null); } /** * Set the metadata for this object. This can be null. * * @param meta The meta data for this object. */ public void setMetadata(PDMetadata meta) { page.setItem(COSName.METADATA, meta); } /** * Get the page actions. * * @return The Actions for this Page */ public PDPageAdditionalActions getActions() { COSDictionary addAct = page.getDictionaryObject(COSName.AA, COSDictionary.class); if (addAct == null) { addAct = new COSDictionary(); page.setItem(COSName.AA, addAct); } return new PDPageAdditionalActions(addAct); } /** * Set the page actions. * * @param actions The actions for the page. */ public void setActions(PDPageAdditionalActions actions) { page.setItem(COSName.AA, actions); } /** * @return The page transition associated with this page or null if no transition is defined */ public PDTransition getTransition() { return ofNullable(page.getDictionaryObject(COSName.TRANS, COSDictionary.class)) .map(PDTransition::new).orElse(null); } /** * @param transition The new transition to set on this page. */ public void setTransition(PDTransition transition) { page.setItem(COSName.TRANS, transition); } /** * Convenient method to set a transition and the display duration * * @param transition The new transition to set on this page. * @param duration The maximum length of time, in seconds, that the page shall be displayed during presentations * before the viewer application shall automatically advance to the next page. */ public void setTransition(PDTransition transition, float duration) { page.setItem(COSName.TRANS, transition); page.setItem(COSName.DUR, new COSFloat(duration)); } /** * This will return a list of the Annotations for this page. * * @return List of the PDAnnotation objects, never null. */ public List getAnnotations() { COSArray annots = page.getDictionaryObject(COSName.ANNOTS, COSArray.class); if (annots == null) { return new COSArrayList<>(page, COSName.ANNOTS); } List actuals = new ArrayList<>(); for (int i = 0; i < annots.size(); i++) { COSBase item = annots.getObject(i); PDAnnotation annotation = ofNullable(item).filter(d -> d instanceof COSDictionary) .map(COSDictionary.class::cast).map(PDAnnotation::createAnnotation) .orElseGet(() -> { LOG.warn("Ignored annotation expected to be a dictionary but was {}", item); return null; }); if (nonNull(annotation)) { actuals.add(annotation); } } return new COSArrayList<>(actuals, annots); } /** * Assuming the rotated crop box is plane where the lower left corner has coordinates 0,0 and assuming the given * point belongs to this plane, it returns the unrotated media box coordinates where this point should be drawn * * @param point * @return */ public Point cropBoxCoordinatesToDraw(Point2D point) { PDRectangle cropBox = getCropBox(); double x = point.getX() + cropBox.getLowerLeftX(); double y = point.getY() + cropBox.getLowerLeftY(); if (getRotation() == 90) { x = cropBox.getUpperRightX() - point.getY(); y = cropBox.getLowerLeftY() + point.getX(); } else if (getRotation() == 180) { x = cropBox.getUpperRightX() - point.getX(); y = cropBox.getUpperRightY() - point.getY(); } else if (getRotation() == 270) { x = cropBox.getLowerLeftX() + point.getY(); y = cropBox.getUpperRightY() - point.getX(); } return new Point((int) x, (int) y); } /** * This will set the list of annotations. * * @param annotations The new list of annotations. */ public void setAnnotations(List annotations) { page.setItem(COSName.ANNOTS, COSArrayList.converterToCOSArray(annotations)); } @Override public boolean equals(Object other) { return other instanceof PDPage && ((PDPage) other).getCOSObject() == this.getCOSObject(); } @Override public int hashCode() { return page.hashCode(); } /** * @return the resource cache associated with this page, or null if there is none. */ public ResourceCache getResourceCache() { return resourceCache; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDPageContentStream.java000066400000000000000000001556731320103431700270070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import static java.util.Objects.nonNull; import static org.sejda.io.CountingWritableByteChannel.from; import static org.sejda.util.RequireUtils.requireState; import java.awt.Color; import java.awt.geom.AffineTransform; import java.io.Closeable; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.text.NumberFormat; import java.util.Locale; import java.util.Stack; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.output.ContentStreamWriter; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDPropertyList; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceCMYK; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceN; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; import org.sejda.sambox.pdmodel.graphics.color.PDICCBased; import org.sejda.sambox.pdmodel.graphics.color.PDPattern; import org.sejda.sambox.pdmodel.graphics.color.PDSeparation; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; import org.sejda.sambox.pdmodel.graphics.image.PDImageXObject; import org.sejda.sambox.pdmodel.graphics.image.PDInlineImage; import org.sejda.sambox.pdmodel.graphics.shading.PDShading; import org.sejda.sambox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.sejda.sambox.pdmodel.graphics.state.RenderingMode; import org.sejda.sambox.util.Matrix; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Provides the ability to write to a page content stream. * * @author Ben Litchfield */ public final class PDPageContentStream implements Closeable { /** * This is to choose what to do with the stream: overwrite, append or prepend. */ public static enum AppendMode { /** * Overwrite the existing page content streams. */ OVERWRITE, /** * Append the content stream after all existing page content streams. */ APPEND, /** * Insert before all other page content streams. */ PREPEND; public boolean isOverwrite() { return this == OVERWRITE; } public boolean isPrepend() { return this == PREPEND; } } private static final Logger LOG = LoggerFactory.getLogger(PDPageContentStream.class); private final PDDocument document; private ContentStreamWriter writer; private PDResources resources; private boolean inTextMode = false; private final Stack fontStack = new Stack<>(); private final Stack nonStrokingColorSpaceStack = new Stack<>(); private final Stack strokingColorSpaceStack = new Stack<>(); // number format private final NumberFormat formatDecimal = NumberFormat.getNumberInstance(Locale.US); /** * Create a new PDPage content stream. * * @param document The document the page is part of. * @param sourcePage The page to write the contents to. * @throws IOException If there is an error writing to the page contents. */ public PDPageContentStream(PDDocument document, PDPage sourcePage) throws IOException { this(document, sourcePage, AppendMode.OVERWRITE, true, false); } /** * Create a new PDPage content stream. * * @param document The document the page is part of. * @param sourcePage The page to write the contents to. * @param appendContent Indicates whether content will be overwritten, appended or prepended. * @param compress Tell if the content stream should compress the page contents. * @throws IOException If there is an error writing to the page contents. */ public PDPageContentStream(PDDocument document, PDPage sourcePage, AppendMode appendContent, boolean compress) throws IOException { this(document, sourcePage, appendContent, compress, false); } /** * Create a new PDPage content stream. * * @param document The document the page is part of. * @param sourcePage The page to write the contents to. * @param appendContent Indicates whether content will be overwritten, appended or prepended. * @param compress Tell if the content stream should compress the page contents. * @param resetContext Tell if the graphic context should be reset. This is only relevant when the appendContent * parameter is set to {@link AppendMode#APPEND}. You should use this when appending to an existing stream, because * the existing stream may have changed graphic properties (e.g. scaling, rotation). * @throws IOException If there is an error writing to the page contents. */ public PDPageContentStream(PDDocument document, PDPage sourcePage, AppendMode appendContent, boolean compress, boolean resetContext) throws IOException { this.document = document; COSName filter = compress ? COSName.FLATE_DECODE : null; // If request specifies the need to append/prepend to the document if (!appendContent.isOverwrite() && sourcePage.hasContents()) { // Create a pdstream to append new content PDStream contentsToAppend = new PDStream(); // Add new stream to contents array COSBase contents = sourcePage.getCOSObject().getDictionaryObject(COSName.CONTENTS); COSArray array; if (contents instanceof COSArray) { // If contents is already an array, a new stream is simply appended to it array = (COSArray) contents; } else { // Creates a new array and adds the current stream plus a new one to it array = new COSArray(); array.add(contents); } if (appendContent.isPrepend()) { array.add(0, contentsToAppend.getCOSObject()); } else { array.add(contentsToAppend); } // save the initial/unmodified graphics context if (resetContext) { // create a new stream to encapsulate the existing stream PDStream saveGraphics = new PDStream(); this.writer = new ContentStreamWriter( from(saveGraphics.createOutputStream(filter))); // save the initial/unmodified graphics context saveGraphicsState(); close(); // insert the new stream at the beginning array.add(0, saveGraphics.getCOSObject()); } // Sets the compoundStream as page contents sourcePage.getCOSObject().setItem(COSName.CONTENTS, array); this.writer = new ContentStreamWriter( from(contentsToAppend.createOutputStream(filter))); // restore the initial/unmodified graphics context if (resetContext) { restoreGraphicsState(); } } else { if (sourcePage.hasContents()) { LOG.warn("You are overwriting an existing content, you should use the append mode"); } PDStream contents = new PDStream(); sourcePage.setContents(contents); this.writer = new ContentStreamWriter(from(contents.createOutputStream(filter))); } // this has to be done here, as the resources will be set to null when reseting the content stream resources = sourcePage.getResources(); if (resources == null) { resources = new PDResources(); sourcePage.setResources(resources); } // configure NumberFormat formatDecimal.setMaximumFractionDigits(5); formatDecimal.setGroupingUsed(false); } /** * Create a new appearance stream. Note that this is not actually a "page" content stream. * * @param doc The document the page is part of. * @param appearance The appearance stream to write to. * @throws IOException If there is an error writing to the page contents. */ public PDPageContentStream(PDDocument doc, PDFormXObject appearance) { this(doc, appearance, new ContentStreamWriter(from(appearance.getStream().createOutputStream()))); } /** * Create a new appearance stream. Note that this is not actually a "page" content stream. * * @param doc The document the appearance is part of. * @param appearance The appearance stream to add to. * @param writer The writer to write the apperances * @throws IOException If there is an error writing to the page contents. */ public PDPageContentStream(PDDocument doc, PDFormXObject appearance, ContentStreamWriter writer) { this.document = doc; this.writer = writer; this.resources = appearance.getResources(); formatDecimal.setMaximumFractionDigits(4); formatDecimal.setGroupingUsed(false); } /** * Begin some text operations. * * @throws IOException If there is an error writing to the stream or if you attempt to nest beginText calls. * @throws IllegalStateException If the method was not allowed to be called at this time. */ public void beginText() throws IOException { if (inTextMode) { throw new IllegalStateException("Error: Nested beginText() calls are not allowed."); } writeOperator("BT"); inTextMode = true; } /** * End some text operations. * * @throws IOException If there is an error writing to the stream or if you attempt to nest endText calls. * @throws IllegalStateException If the method was not allowed to be called at this time. */ public void endText() throws IOException { if (!inTextMode) { throw new IllegalStateException( "Error: You must call beginText() before calling endText."); } writeOperator("ET"); inTextMode = false; } public void endTextIfRequired() throws IOException { if (inTextMode) { endText(); } } /** * Set the font and font size to draw text with. * * @param font The font to use. * @param fontSize The font size to draw the text. * @throws IOException If there is an error writing the font information. */ public void setFont(PDFont font, float fontSize) throws IOException { if (fontStack.isEmpty()) { fontStack.add(font); } else { fontStack.setElementAt(font, fontStack.size() - 1); } if (font.willBeSubset()) { document.getFontsToSubset().add(font); } writeOperand(resources.add(font)); writeOperand(fontSize); writeOperator("Tf"); } /** * Shows the given text at the location specified by the current text matrix. * * @param text The Unicode text to show. * @throws IOException If an io exception occurs. */ public void showText(String text) throws IOException { if (!inTextMode) { throw new IllegalStateException("Must call beginText() before showText()"); } if (fontStack.isEmpty()) { throw new IllegalStateException("Must call setFont() before showText()"); } PDFont font = fontStack.peek(); // Unicode code points to keep when subsetting if (font.willBeSubset()) { for (int offset = 0; offset < text.length();) { int codePoint = text.codePointAt(offset); font.addToSubset(codePoint); offset += Character.charCount(codePoint); } } COSString.newInstance(font.encode(text)).accept(writer); writer.writeSpace(); writeOperator("Tj"); } /** * Sets the text leading. * * @param leading The leading in unscaled text units. * @throws IOException If there is an error writing to the stream. */ public void setLeading(double leading) throws IOException { writeOperand((float) leading); writeOperator("TL"); } /** * Move to the start of the next line of text. Requires the leading to have been set. * * @throws IOException If there is an error writing to the stream. */ public void newLine() throws IOException { if (!inTextMode) { throw new IllegalStateException("Must call beginText() before newLine()"); } writeOperator("T*"); } /** * The Td operator. Move to the start of the next line, offset from the start of the current line by (tx, ty). * * @param tx The x translation. * @param ty The y translation. * @throws IOException If there is an error writing to the stream. * @throws IllegalStateException If the method was not allowed to be called at this time. */ public void newLineAtOffset(float tx, float ty) throws IOException { if (!inTextMode) { throw new IllegalStateException( "Error: must call beginText() before newLineAtOffset()"); } writeOperand(tx); writeOperand(ty); writeOperator("Td"); } /** * The Tm operator. Sets the text matrix to the given values. A current text matrix will be replaced with the new * one. * * @param matrix the transformation matrix * @throws IOException If there is an error writing to the stream. * @throws IllegalStateException If the method was not allowed to be called at this time. */ public void setTextMatrix(Matrix matrix) throws IOException { if (!inTextMode) { throw new IllegalStateException("Error: must call beginText() before setTextMatrix"); } writeAffineTransform(matrix.createAffineTransform()); writeOperator("Tm"); } /** * Draw an image at the x,y coordinates, with the default size of the image. * * @param image The image to draw. * @param x The x-coordinate to draw the image. * @param y The y-coordinate to draw the image. * * @throws IOException If there is an error writing to the stream. */ public void drawImage(PDImageXObject image, float x, float y) throws IOException { drawImage(image, x, y, image.getWidth(), image.getHeight()); } public void drawImage(PDFormXObject image, float x, float y) throws IOException { drawImage(image, x, y, image.getBBox().getWidth(), image.getBBox().getHeight()); } /** * Draw an image at the x,y coordinates, with the given size. * * @param image The image to draw. * @param x The x-coordinate to draw the image. * @param y The y-coordinate to draw the image. * @param width The width to draw the image. * @param height The height to draw the image. * * @throws IOException If there is an error writing to the stream. * @throws IllegalStateException If the method was called within a text block. */ public void drawImage(PDImageXObject image, float x, float y, float width, float height) throws IOException { draw(image, new Matrix(new AffineTransform(width, 0, 0, height, x, y)), null); } public void drawImage(PDFormXObject image, float x, float y, float width, float height) throws IOException { draw(image, new Matrix(new AffineTransform(width, 0, 0, height, x, y)), null); } /** * Draw an image at the origin with the given transformation matrix. * * @param image The image to draw. * @param matrix The transformation matrix to apply to the image. * @param state the graphic state to use * * @throws IOException If there is an error writing to the stream. * @throws IllegalStateException If the method was called within a text block. */ public void drawImage(PDImageXObject image, Matrix matrix, PDExtendedGraphicsState state) throws IOException { draw(image, matrix, state); } public void drawImage(PDFormXObject image, Matrix matrix, PDExtendedGraphicsState state) throws IOException { draw(image, matrix, state); } private void draw(PDXObject image, Matrix matrix, PDExtendedGraphicsState state) throws IOException { requireState(!inTextMode, "Cannot draw image within a text block."); saveGraphicsState(); transform(matrix); if (nonNull(state)) { setGraphicsStateParameters(state); } if (image instanceof PDImageXObject) { writeOperand(resources.add((PDImageXObject) image)); } else if (image instanceof PDFormXObject) { writeOperand(resources.add((PDFormXObject) image)); } else { throw new IllegalArgumentException("Unsupported xobject type"); } writeOperator("Do"); restoreGraphicsState(); } /** * Draw an inline image at the x,y coordinates, with the default size of the image. * * @param inlineImage The inline image to draw. * @param x The x-coordinate to draw the inline image. * @param y The y-coordinate to draw the inline image. * * @throws IOException If there is an error writing to the stream. */ public void drawImage(PDInlineImage inlineImage, float x, float y) throws IOException { drawImage(inlineImage, x, y, inlineImage.getWidth(), inlineImage.getHeight()); } /** * Draw an inline image at the x,y coordinates and a certain width and height. * * @param inlineImage The inline image to draw. * @param x The x-coordinate to draw the inline image. * @param y The y-coordinate to draw the inline image. * @param width The width of the inline image to draw. * @param height The height of the inline image to draw. * * @throws IOException If there is an error writing to the stream. * @throws IllegalStateException If the method was called within a text block. */ public void drawImage(PDInlineImage inlineImage, float x, float y, float width, float height) throws IOException { if (inTextMode) { throw new IllegalStateException("Error: drawImage is not allowed within a text block."); } saveGraphicsState(); transform(new Matrix(width, 0, 0, height, x, y)); // create the image dictionary StringBuilder sb = new StringBuilder(); sb.append("BI"); sb.append("\n /W "); sb.append(inlineImage.getWidth()); sb.append("\n /H "); sb.append(inlineImage.getHeight()); sb.append("\n /CS "); sb.append("/"); sb.append(inlineImage.getColorSpace().getName()); if (inlineImage.getDecode() != null && inlineImage.getDecode().size() > 0) { sb.append("\n /D "); sb.append("["); for (COSBase base : inlineImage.getDecode()) { sb.append(((COSNumber) base).intValue()); sb.append(" "); } sb.append("]"); } if (inlineImage.isStencil()) { sb.append("\n /IM true"); } sb.append("\n /BPC "); sb.append(inlineImage.getBitsPerComponent()); // image dictionary write(sb.toString()); this.writer.writeEOL(); // binary data writeOperator(Operator.ID_OPERATOR); writeBytes(inlineImage.getData()); this.writer.writeEOL(); writeOperator(Operator.EI_OPERATOR); restoreGraphicsState(); } /** * Draws the given Form XObject at the current location. * * @param form Form XObject * @throws IOException if the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void drawForm(PDFormXObject form) throws IOException { if (inTextMode) { throw new IllegalStateException("Error: drawForm is not allowed within a text block."); } writeOperand(resources.add(form)); writeOperator("Do"); } /** * The cm operator. Concatenates the given matrix with the CTM. * * @param matrix the transformation matrix * @throws IOException If there is an error writing to the stream. */ public void transform(Matrix matrix) throws IOException { writeAffineTransform(matrix.createAffineTransform()); writeOperator("cm"); } /** * q operator. Saves the current graphics state. * * @throws IOException If an error occurs while writing to the stream. */ public void saveGraphicsState() throws IOException { if (!fontStack.isEmpty()) { fontStack.push(fontStack.peek()); } if (!strokingColorSpaceStack.isEmpty()) { strokingColorSpaceStack.push(strokingColorSpaceStack.peek()); } if (!nonStrokingColorSpaceStack.isEmpty()) { nonStrokingColorSpaceStack.push(nonStrokingColorSpaceStack.peek()); } writeOperator("q"); } /** * Q operator. Restores the current graphics state. * * @throws IOException If an error occurs while writing to the stream. */ public void restoreGraphicsState() throws IOException { if (!fontStack.isEmpty()) { fontStack.pop(); } if (!strokingColorSpaceStack.isEmpty()) { strokingColorSpaceStack.pop(); } if (!nonStrokingColorSpaceStack.isEmpty()) { nonStrokingColorSpaceStack.pop(); } writeOperator("Q"); } private COSName getName(PDColorSpace colorSpace) { if (colorSpace instanceof PDDeviceGray || colorSpace instanceof PDDeviceRGB || colorSpace instanceof PDDeviceCMYK) { return COSName.getPDFName(colorSpace.getName()); } else { return resources.add(colorSpace); } } public void setTextRenderingMode(RenderingMode renderingMode) throws IOException { writeOperand(renderingMode.intValue()); writeOperator("Tr"); } /** * Sets the stroking color and, if necessary, the stroking color space. * * @param color Color in a specific color space. * @throws IOException If an IO error occurs while writing to the stream. */ public void setStrokingColor(PDColor color) throws IOException { if (strokingColorSpaceStack.isEmpty() || strokingColorSpaceStack.peek() != color.getColorSpace() && color.getColorSpace() != null) { writeOperand(getName(color.getColorSpace())); writeOperator("CS"); setStrokingColorSpaceStack(color.getColorSpace()); } for (float value : color.getComponents()) { writeOperand(value); } if (color.getColorSpace() instanceof PDPattern) { writeOperand(color.getPatternName()); } if (color.getColorSpace() instanceof PDPattern || color.getColorSpace() instanceof PDSeparation || color.getColorSpace() instanceof PDDeviceN || color.getColorSpace() instanceof PDICCBased) { writeOperator("SCN"); } else { writeOperator("SC"); } } /** * Set the stroking color using an AWT color. Conversion uses the default sRGB color space. * * @param color The color to set. * @throws IOException If an IO error occurs while writing to the stream. */ public void setStrokingColor(Color color) throws IOException { float[] components = new float[] { color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f }; PDColor pdColor = new PDColor(components, PDDeviceRGB.INSTANCE); setStrokingColor(pdColor); } /** * Set the stroking color in the DeviceRGB color space. Range is 0..255. * * @param r The red value * @param g The green value. * @param b The blue value. * @throws IOException If an IO error occurs while writing to the stream. * @throws IllegalArgumentException If the parameters are invalid. */ public void setStrokingColor(int r, int g, int b) throws IOException { if (isOutside255Interval(r) || isOutside255Interval(g) || isOutside255Interval(b)) { throw new IllegalArgumentException("Parameters must be within 0..255, but are " + String.format("(%d,%d,%d)", r, g, b)); } writeOperand(r / 255f); writeOperand(g / 255f); writeOperand(b / 255f); writeOperator("RG"); setStrokingColorSpaceStack(PDDeviceRGB.INSTANCE); } /** * Set the stroking color in the DeviceCMYK color space. Range is 0..1 * * @param c The cyan value. * @param m The magenta value. * @param y The yellow value. * @param k The black value. * @throws IOException If an IO error occurs while writing to the stream. * @throws IllegalArgumentException If the parameters are invalid. */ public void setStrokingColor(float c, float m, float y, float k) throws IOException { if (isOutsideOneInterval(c) || isOutsideOneInterval(m) || isOutsideOneInterval(y) || isOutsideOneInterval(k)) { throw new IllegalArgumentException("Parameters must be within 0..1, but are " + String.format("(%.2f,%.2f,%.2f,%.2f)", c, m, y, k)); } writeOperand(c); writeOperand(m); writeOperand(y); writeOperand(k); writeOperator("K"); setStrokingColorSpaceStack(PDDeviceCMYK.INSTANCE); } /** * Set the stroking color in the DeviceGray color space. Range is 0..1. * * @param g The gray value. * @throws IOException If an IO error occurs while writing to the stream. * @throws IllegalArgumentException If the parameter is invalid. */ public void setStrokingColor(double g) throws IOException { if (isOutsideOneInterval(g)) { throw new IllegalArgumentException("Parameter must be within 0..1, but is " + g); } writeOperand((float) g); writeOperator("G"); setStrokingColorSpaceStack(PDDeviceGray.INSTANCE); } /** * Sets the non-stroking color and, if necessary, the non-stroking color space. * * @param color Color in a specific color space. * @throws IOException If an IO error occurs while writing to the stream. */ public void setNonStrokingColor(PDColor color) throws IOException { if (nonStrokingColorSpaceStack.isEmpty() || nonStrokingColorSpaceStack.peek() != color.getColorSpace()) { writeOperand(getName(color.getColorSpace())); writeOperator("cs"); setNonStrokingColorSpaceStack(color.getColorSpace()); } for (float value : color.getComponents()) { writeOperand(value); } if (color.getColorSpace() instanceof PDPattern) { writeOperand(color.getPatternName()); } if (color.getColorSpace() instanceof PDPattern || color.getColorSpace() instanceof PDSeparation || color.getColorSpace() instanceof PDDeviceN || color.getColorSpace() instanceof PDICCBased) { writeOperator("scn"); } else { writeOperator("sc"); } } /** * Set the non-stroking color using an AWT color. Conversion uses the default sRGB color space. * * @param color The color to set. * @throws IOException If an IO error occurs while writing to the stream. */ public void setNonStrokingColor(Color color) throws IOException { float[] components = new float[] { color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f }; PDColor pdColor = new PDColor(components, PDDeviceRGB.INSTANCE); setNonStrokingColor(pdColor); } /** * Set the non-stroking color in the DeviceRGB color space. Range is 0..255. * * @param r The red value. * @param g The green value. * @param b The blue value. * @throws IOException If an IO error occurs while writing to the stream. * @throws IllegalArgumentException If the parameters are invalid. */ public void setNonStrokingColor(int r, int g, int b) throws IOException { if (isOutside255Interval(r) || isOutside255Interval(g) || isOutside255Interval(b)) { throw new IllegalArgumentException("Parameters must be within 0..255, but are " + String.format("(%d,%d,%d)", r, g, b)); } writeOperand(r / 255f); writeOperand(g / 255f); writeOperand(b / 255f); writeOperator("rg"); setNonStrokingColorSpaceStack(PDDeviceRGB.INSTANCE); } /** * Set the non-stroking color in the DeviceCMYK color space. Range is 0..255. * * @param c The cyan value. * @param m The magenta value. * @param y The yellow value. * @param k The black value. * @throws IOException If an IO error occurs while writing to the stream. * @throws IllegalArgumentException If the parameters are invalid. */ public void setNonStrokingColor(int c, int m, int y, int k) throws IOException { if (isOutside255Interval(c) || isOutside255Interval(m) || isOutside255Interval(y) || isOutside255Interval(k)) { throw new IllegalArgumentException("Parameters must be within 0..255, but are " + String.format("(%d,%d,%d,%d)", c, m, y, k)); } setNonStrokingColor(c / 255f, m / 255f, y / 255f, k / 255f); } /** * Set the non-stroking color in the DeviceRGB color space. Range is 0..1. * * @param c The cyan value. * @param m The magenta value. * @param y The yellow value. * @param k The black value. * @throws IOException If an IO error occurs while writing to the stream. */ public void setNonStrokingColor(double c, double m, double y, double k) throws IOException { if (isOutsideOneInterval(c) || isOutsideOneInterval(m) || isOutsideOneInterval(y) || isOutsideOneInterval(k)) { throw new IllegalArgumentException("Parameters must be within 0..1, but are " + String.format("(%.2f,%.2f,%.2f,%.2f)", c, m, y, k)); } writeOperand((float) c); writeOperand((float) m); writeOperand((float) y); writeOperand((float) k); writeOperator("k"); setNonStrokingColorSpaceStack(PDDeviceCMYK.INSTANCE); } /** * Set the non-stroking color in the DeviceGray color space. Range is 0..255. * * @param g The gray value. * @throws IOException If an IO error occurs while writing to the stream. * @throws IllegalArgumentException If the parameter is invalid. */ public void setNonStrokingColor(int g) throws IOException { if (isOutside255Interval(g)) { throw new IllegalArgumentException("Parameter must be within 0..255, but is " + g); } setNonStrokingColor(g / 255f); } /** * Set the non-stroking color in the DeviceGray color space. Range is 0..1. * * @param g The gray value. * @throws IOException If an IO error occurs while writing to the stream. * @throws IllegalArgumentException If the parameter is invalid. */ public void setNonStrokingColor(double g) throws IOException { if (isOutsideOneInterval(g)) { throw new IllegalArgumentException("Parameter must be within 0..1, but is " + g); } writeOperand((float) g); writeOperator("g"); setNonStrokingColorSpaceStack(PDDeviceGray.INSTANCE); } /** * Add a rectangle to the current path. * * @param x The lower left x coordinate. * @param y The lower left y coordinate. * @param width The width of the rectangle. * @param height The height of the rectangle. * @throws IOException If the content stream could not be written. * @throws IllegalStateException If the method was called within a text block. */ public void addRect(float x, float y, float width, float height) throws IOException { if (inTextMode) { throw new IllegalStateException("Error: addRect is not allowed within a text block."); } writeOperand(x); writeOperand(y); writeOperand(width); writeOperand(height); writeOperator("re"); } /** * Append a cubic Bézier curve to the current path. The curve extends from the current point to the point (x3, y3), * using (x1, y1) and (x2, y2) as the Bézier control points. * * @param x1 x coordinate of the point 1 * @param y1 y coordinate of the point 1 * @param x2 x coordinate of the point 2 * @param y2 y coordinate of the point 2 * @param x3 x coordinate of the point 3 * @param y3 y coordinate of the point 3 * @throws IOException If the content stream could not be written. * @throws IllegalStateException If the method was called within a text block. */ public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException { if (inTextMode) { throw new IllegalStateException("Error: curveTo is not allowed within a text block."); } writeOperand(x1); writeOperand(y1); writeOperand(x2); writeOperand(y2); writeOperand(x3); writeOperand(y3); writeOperator("c"); } /** * Append a cubic Bézier curve to the current path. The curve extends from the current point to the point (x3, y3), * using the current point and (x2, y2) as the Bézier control points. * * @param x2 x coordinate of the point 2 * @param y2 y coordinate of the point 2 * @param x3 x coordinate of the point 3 * @param y3 y coordinate of the point 3 * @throws IllegalStateException If the method was called within a text block. * @throws IOException If the content stream could not be written. */ public void curveTo2(float x2, float y2, float x3, float y3) throws IOException { if (inTextMode) { throw new IllegalStateException("Error: curveTo2 is not allowed within a text block."); } writeOperand(x2); writeOperand(y2); writeOperand(x3); writeOperand(y3); writeOperator("v"); } /** * Append a cubic Bézier curve to the current path. The curve extends from the current point to the point (x3, y3), * using (x1, y1) and (x3, y3) as the Bézier control points. * * @param x1 x coordinate of the point 1 * @param y1 y coordinate of the point 1 * @param x3 x coordinate of the point 3 * @param y3 y coordinate of the point 3 * @throws IOException If the content stream could not be written. * @throws IllegalStateException If the method was called within a text block. */ public void curveTo1(float x1, float y1, float x3, float y3) throws IOException { if (inTextMode) { throw new IllegalStateException("Error: curveTo1 is not allowed within a text block."); } writeOperand(x1); writeOperand(y1); writeOperand(x3); writeOperand(y3); writeOperator("y"); } /** * Move the current position to the given coordinates. * * @param x The x coordinate. * @param y The y coordinate. * @throws IOException If the content stream could not be written. * @throws IllegalStateException If the method was called within a text block. */ public void moveTo(float x, float y) throws IOException { if (inTextMode) { throw new IllegalStateException("Error: moveTo is not allowed within a text block."); } writeOperand(x); writeOperand(y); writeOperator("m"); } /** * Draw a line from the current position to the given coordinates. * * @param x The x coordinate. * @param y The y coordinate. * @throws IOException If the content stream could not be written. * @throws IllegalStateException If the method was called within a text block. */ public void lineTo(float x, float y) throws IOException { if (inTextMode) { throw new IllegalStateException("Error: lineTo is not allowed within a text block."); } writeOperand(x); writeOperand(y); writeOperator("l"); } /** * Stroke the path. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void stroke() throws IOException { if (inTextMode) { throw new IllegalStateException("Error: stroke is not allowed within a text block."); } writeOperator("S"); } /** * Close and stroke the path. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void closeAndStroke() throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: closeAndStroke is not allowed within a text block."); } writeOperator("s"); } /** * Fills the path using the nonzero winding rule. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void fill() throws IOException { if (inTextMode) { throw new IllegalStateException("Error: fill is not allowed within a text block."); } writeOperator("f"); } /** * Fills the path using the even-odd winding number rule. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void fillEvenOdd() throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: fillEvenOdd is not allowed within a text block."); } writeOperator("f*"); } /** * Fill and then stroke the path, using the nonzero winding number rule to determine the region to fill. This shall * produce the same result as constructing two identical path objects, painting the first with {@link #fill() } and * the second with {@link #stroke() }. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void fillAndStroke() throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: fillAndStroke is not allowed within a text block."); } writeOperator("B"); } /** * Fill and then stroke the path, using the even-odd rule to determine the region to fill. This shall produce the * same result as constructing two identical path objects, painting the first with {@link #fillEvenOdd() } and the * second with {@link #stroke() }. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void fillAndStrokeEvenOdd() throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: fillAndStrokeEvenOdd is not allowed within a text block."); } writeOperator("B*"); } /** * Close, fill, and then stroke the path, using the nonzero winding number rule to determine the region to fill. * This shall have the same effect as the sequence {@link #closePath() } and then {@link #fillAndStroke() }. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void closeAndFillAndStroke() throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: closeAndFillAndStroke is not allowed within a text block."); } writeOperator("b"); } /** * Close, fill, and then stroke the path, using the even-odd rule to determine the region to fill. This shall have * the same effect as the sequence {@link #closePath() } and then {@link #fillAndStrokeEvenOdd() }. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void closeAndFillAndStrokeEvenOdd() throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: closeAndFillAndStrokeEvenOdd is not allowed within a text block."); } writeOperator("b*"); } /** * Fills the clipping area with the given shading. * * @param shading Shading resource * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void shadingFill(PDShading shading) throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: shadingFill is not allowed within a text block."); } writeOperand(resources.add(shading)); writeOperator("sh"); } /** * Closes the current subpath. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void closePath() throws IOException { if (inTextMode) { throw new IllegalStateException("Error: closePath is not allowed within a text block."); } writeOperator("h"); } /** * Intersects the current clipping path with the current path, using the nonzero rule. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void clip() throws IOException { if (inTextMode) { throw new IllegalStateException("Error: clip is not allowed within a text block."); } writeOperator("W"); // end path without filling or stroking writeOperator("n"); } /** * Intersects the current clipping path with the current path, using the even-odd rule. * * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void clipEvenOdd() throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: clipEvenOdd is not allowed within a text block."); } writeOperator("W*"); // end path without filling or stroking writeOperator("n"); } /** * Set line width to the given value. * * @param lineWidth The width which is used for drwaing. * @throws IOException If the content stream could not be written * @throws IllegalStateException If the method was called within a text block. */ public void setLineWidth(float lineWidth) throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: setLineWidth is not allowed within a text block."); } writeOperand(lineWidth); writeOperator("w"); } /** * Set the line join style. * * @param lineJoinStyle 0 for miter join, 1 for round join, and 2 for bevel join. * @throws IOException If the content stream could not be written. * @throws IllegalStateException If the method was called within a text block. * @throws IllegalArgumentException If the parameter is not a valid line join style. */ public void setLineJoinStyle(int lineJoinStyle) throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: setLineJoinStyle is not allowed within a text block."); } if (lineJoinStyle >= 0 && lineJoinStyle <= 2) { writeOperand(lineJoinStyle); writeOperator("j"); } else { throw new IllegalArgumentException("Error: unknown value for line join style"); } } /** * Set the line cap style. * * @param lineCapStyle 0 for butt cap, 1 for round cap, and 2 for projecting square cap. * @throws IOException If the content stream could not be written. * @throws IllegalStateException If the method was called within a text block. * @throws IllegalArgumentException If the parameter is not a valid line cap style. */ public void setLineCapStyle(int lineCapStyle) throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: setLineCapStyle is not allowed within a text block."); } if (lineCapStyle >= 0 && lineCapStyle <= 2) { writeOperand(lineCapStyle); writeOperator("J"); } else { throw new IllegalArgumentException("Error: unknown value for line cap style"); } } /** * Set the line dash pattern. * * @param pattern The pattern array * @param phase The phase of the pattern * @throws IOException If the content stream could not be written. * @throws IllegalStateException If the method was called within a text block. */ public void setLineDashPattern(float[] pattern, float phase) throws IOException { if (inTextMode) { throw new IllegalStateException( "Error: setLineDashPattern is not allowed within a text block."); } write("["); for (float value : pattern) { writeOperand(value); } write("] "); writeOperand(phase); writeOperator("d"); } /** * Set the miter limit. * * @param miterLimit the new miter limit. * @throws IOException If the content stream could not be written. */ public void setMiterLimit(float miterLimit) throws IOException { requireState(!inTextMode, "setMiterLimit is not allowed within a text block"); requireState(miterLimit > 0, "A miter limit <= 0 is invalid and will not render in Acrobat Reader"); writeOperand(miterLimit); writeOperator("M"); } /** * Begin a marked content sequence. * * @param tag the tag * @throws IOException If the content stream could not be written */ public void beginMarkedContent(COSName tag) throws IOException { writeOperand(tag); writeOperator("BMC"); } /** * Begin a marked content sequence with a reference to an entry in the page resources' Properties dictionary. * * @param tag the tag * @param propertyList property list * @throws IOException If the content stream could not be written */ public void beginMarkedContent(COSName tag, PDPropertyList propertyList) throws IOException { writeOperand(tag); writeOperand(resources.add(propertyList)); writeOperator("BDC"); } /** * End a marked content sequence. * * @throws IOException If the content stream could not be written */ public void endMarkedContent() throws IOException { writeOperator("EMC"); } /** * Set an extended graphics state. * * @param state The extended graphics state. * @throws IOException If the content stream could not be written. */ public void setGraphicsStateParameters(PDExtendedGraphicsState state) throws IOException { writeOperand(resources.add(state)); writeOperator("gs"); } /** * Write a comment line. * * @param comment * @throws IOException If the content stream could not be written. * @throws IllegalArgumentException If the comment contains a newline. This is not allowed, because the next line * could be ordinary PDF content. */ public void addComment(String comment) throws IOException { if (comment.indexOf('\n') >= 0 || comment.indexOf('\r') >= 0) { throw new IllegalArgumentException("comment should not include a newline"); } writer.writer().write((byte) '%'); write(comment); writer.writeEOL(); } protected void writeOperand(float real) throws IOException { // PDFbox up to 1st Aug has a new fast or fallback way to write things here, which breaks things // temporary reverted to old way of doing things, see if new commits fix this write(formatDecimal.format(real)); writer.writeSpace(); } private void writeOperand(int integer) throws IOException { write(formatDecimal.format(integer)); writer.writeSpace(); } /** * Writes a COSName to the content stream. */ private void writeOperand(COSName name) throws IOException { name.accept(writer); writer.writeSpace(); } /** * Writes a string to the content stream as ASCII. */ private void writeOperator(String text) throws IOException { write(text); writer.writeEOL(); } /** * Writes a string to the content stream as ASCII. */ private void write(String text) throws IOException { writer.writeContent(text.getBytes(StandardCharsets.US_ASCII)); } /** * Writes binary data to the content stream. */ private void writeBytes(byte[] data) throws IOException { writer.writeContent(data); } /** * Writes an AffineTransform to the content stream as an array. */ private void writeAffineTransform(AffineTransform transform) throws IOException { double[] values = new double[6]; transform.getMatrix(values); for (double v : values) { writeOperand((float) v); } } /** * Close the content stream. This must be called when you are done with this object. * * @throws IOException If the underlying stream has a problem being written to. */ @Override public void close() throws IOException { IOUtils.close(writer); } private boolean isOutside255Interval(int val) { return val < 0 || val > 255; } private boolean isOutsideOneInterval(double val) { return val < 0 || val > 1; } private void setStrokingColorSpaceStack(PDColorSpace colorSpace) { if (strokingColorSpaceStack.isEmpty()) { strokingColorSpaceStack.add(colorSpace); } else { strokingColorSpaceStack.setElementAt(colorSpace, strokingColorSpaceStack.size() - 1); } } private void setNonStrokingColorSpaceStack(PDColorSpace colorSpace) { if (nonStrokingColorSpaceStack.isEmpty()) { nonStrokingColorSpaceStack.add(colorSpace); } else { nonStrokingColorSpaceStack.setElementAt(colorSpace, nonStrokingColorSpaceStack.size() - 1); } } /** * Set the text rendering mode. This determines whether showing text shall cause glyph outlines to be stroked, * filled, used as a clipping boundary, or some combination of the three. * * @param rm The text rendering mode. * @throws IOException If the content stream could not be written. */ public void setRenderingMode(RenderingMode rm) throws IOException { writeOperand(rm.intValue()); writeOperator("Tr"); } /** * Set the character spacing. The value shall be added to the horizontal or vertical component of the glyph's * displacement, depending on the writing mode. * * @param spacing character spacing * @throws IOException If the content stream could not be written. */ public void setCharacterSpacing(float spacing) throws IOException { writeOperand(spacing); writeOperator("Tc"); } /** * Set the word spacing. The value shall be added to the horizontal or vertical component of the ASCII SPACE * character, depending on the writing mode. * * @param spacing word spacing * @throws IOException If the content stream could not be written. */ public void setWordSpacing(float spacing) throws IOException { writeOperand(spacing); writeOperator("Tw"); } /** * Set the horizontal scaling to scale / 100. * * @param scale number specifying the percentage of the normal width. Default value: 100 (normal width). * @throws IOException If the content stream could not be written. */ public void setHorizontalScaling(float scale) throws IOException { writeOperand(scale); writeOperator("Tz"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDPageTree.java000066400000000000000000000425251320103431700251070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toList; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Queue; import java.util.Spliterator; import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSObjectable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The page tree, which defines the ordering of pages in the document in an efficient manner. * * @author John Hewson */ public class PDPageTree implements COSObjectable, Iterable { private static final Logger LOG = LoggerFactory.getLogger(PDPageTree.class); private final COSDictionary root; private final PDDocument document; /** * Constructor for embedding. */ public PDPageTree() { root = new COSDictionary(); root.setItem(COSName.TYPE, COSName.PAGES); root.setItem(COSName.KIDS, new COSArray()); root.setItem(COSName.COUNT, COSInteger.ZERO); document = null; } /** * Constructor for reading. * * @param root A page tree root. */ public PDPageTree(COSDictionary root) { this(root, null); } /** * Constructor for reading. * * @param root A page tree root. * @param document The document which contains "root". */ PDPageTree(COSDictionary root, PDDocument document) { requireNotNullArg(root, "Page tree root cannot be null"); // repair bad PDFs which contain a Page dict instead of a page tree, see PDFBOX-3154 if (COSName.PAGE.equals(root.getCOSName(COSName.TYPE))) { COSArray kids = new COSArray(); kids.add(root); this.root = new COSDictionary(); this.root.setItem(COSName.KIDS, kids); this.root.setInt(COSName.COUNT, 1); } else { this.root = root; } root.setItem(COSName.TYPE, COSName.PAGES); this.document = document; } /** * Returns the given attribute, inheriting from parent tree nodes if necessary. * * @param node page object * @param key the key to look up * @return COS value for the given key */ public static COSBase getInheritableAttribute(COSDictionary node, COSName key) { COSBase value = node.getDictionaryObject(key); if (value != null) { return value; } COSDictionary parent = (COSDictionary) node.getDictionaryObject(COSName.PARENT, COSName.P); if (parent != null) { return getInheritableAttribute(parent, key); } return null; } /** * Returns an iterator which walks all pages in the tree, in order. */ @Override public Iterator iterator() { return new PageIterator(root); } /** * @return a sequential {@code Stream} over the pages of this page tree. */ public Stream stream() { return StreamSupport.stream(Spliterators.spliterator(iterator(), getCount(), Spliterator.ORDERED | Spliterator.NONNULL), false); } /** * @return a sequential {@code Stream} over the nodes of this page tree. */ public Stream streamNodes() { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new NodesIterator(root), Spliterator.ORDERED | Spliterator.NONNULL), false); } /** * Helper to get kids from malformed PDFs. * * @param node page tree node * @return list of kids */ private List getKids(COSDictionary node) { COSArray kids = node.getDictionaryObject(COSName.KIDS, COSArray.class); if (nonNull(kids)) { // we collect only non null, non COSNull COSDictionary kids return kids.stream().map(COSBase::getCOSObject).filter(i -> i != COSNull.NULL) .filter(Objects::nonNull).filter(n -> n instanceof COSDictionary) .map(n -> (COSDictionary) n).collect(toList()); } return new ArrayList<>(); } /** * Iterator which walks all pages in the tree, in order. */ private final class PageIterator implements Iterator { private final Queue queue = new ArrayDeque<>(); private PageIterator(COSDictionary node) { enqueueKids(node); } private void enqueueKids(COSDictionary node) { if (isPageTreeNode(node)) { getKids(node).forEach(this::enqueueKids); } else { queue.add(node); } } @Override public boolean hasNext() { return !queue.isEmpty(); } @Override public PDPage next() { COSDictionary next = queue.poll(); sanitizeType(next); ResourceCache resourceCache = document != null ? document.getResourceCache() : null; return new PDPage(next, resourceCache); } @Override public void remove() { throw new UnsupportedOperationException(); } } /** * Iterator which walks all the nodes in the tree. */ private final class NodesIterator implements Iterator { private final Queue queue = new ArrayDeque<>(); private NodesIterator(COSDictionary node) { enqueueKids(node); } private void enqueueKids(COSDictionary node) { queue.add(node); if (isPageTreeNode(node)) { getKids(node).forEach(this::enqueueKids); } } @Override public boolean hasNext() { return !queue.isEmpty(); } @Override public COSDictionary next() { return queue.poll(); } @Override public void remove() { throw new UnsupportedOperationException(); } } /** * Returns the page at the given index. * * @param index zero-based index */ public PDPage get(int index) { COSDictionary dict = get(index + 1, root, 0); sanitizeType(dict); ResourceCache resourceCache = document != null ? document.getResourceCache() : null; return new PDPage(dict, resourceCache); } private static void sanitizeType(COSDictionary dictionary) { if (isNull(dictionary.getCOSName(COSName.TYPE))) { LOG.warn("Missing required 'Page' type for page"); dictionary.setName(COSName.TYPE, COSName.PAGE.getName()); } COSName type = dictionary.getCOSName(COSName.TYPE); if (!COSName.PAGE.equals(type)) { LOG.error("Expected 'Page' but found '{}'", type.getName()); dictionary.setName(COSName.TYPE, COSName.PAGE.getName()); } } /** * Returns the given COS page using a depth-first search. * * @param pageNum 1-based page number * @param node page tree node to search * @param encountered number of pages encountered so far * @return COS dictionary of the Page object */ private COSDictionary get(int pageNum, COSDictionary node, int encountered) { if (pageNum < 0) { throw new PageNotFoundException("Index out of bounds: " + pageNum + " in " + getSourcePath(), pageNum, getSourcePath()); } if (isPageTreeNode(node)) { int count = node.getInt(COSName.COUNT, 0); if (pageNum <= encountered + count) { // it's a kid of this node for (COSDictionary kid : getKids(node)) { // which kid? if (isPageTreeNode(kid)) { int kidCount = kid.getInt(COSName.COUNT, 0); if (pageNum <= encountered + kidCount) { // it's this kid return get(pageNum, kid, encountered); } encountered += kidCount; } else { // single page encountered++; if (pageNum == encountered) { // it's this page return get(pageNum, kid, encountered); } } } throw new PageNotFoundException( "Unable to find page " + pageNum + " in " + getSourcePath(), pageNum, getSourcePath()); } throw new PageNotFoundException( "Index out of bounds: " + pageNum + " in " + getSourcePath(), pageNum, getSourcePath()); } if (encountered == pageNum) { return node; } throw new PageNotFoundException("Unable to find page " + pageNum + " in " + getSourcePath(), pageNum, getSourcePath()); } private String getSourcePath() { return ofNullable(getCOSObject().id()) .map(i -> i.ownerIdentifier).orElse("Unknown"); } /** * @return true if the node is a page tree node (i.e. and intermediate). */ public static boolean isPageTreeNode(COSDictionary node) { // some files such as PDFBOX-2250-229205.pdf don't have Pages set as the Type, so we have // to check for the presence of Kids too return nonNull(node) && (node.getCOSName(COSName.TYPE) == COSName.PAGES || node.containsKey(COSName.KIDS)); } /** * Returns the index of the given page, or -1 if it does not exist. * * @param page The page to search for. * @return the zero-based index of the given page, or -1 if the page is not found. */ public int indexOf(PDPage page) { SearchContext context = new SearchContext(page); if (findPage(context, root)) { return context.index; } return -1; } private boolean findPage(SearchContext context, COSDictionary node) { for (COSDictionary kid : getKids(node)) { if (context.found) { break; } if (isPageTreeNode(kid)) { findPage(context, kid); } else { context.visitPage(kid); } } return context.found; } private static final class SearchContext { private final COSDictionary searched; private int index = -1; private boolean found; private SearchContext(PDPage page) { this.searched = page.getCOSObject(); } private void visitPage(COSDictionary current) { index++; found = searched.equals(current); } } /** * Returns the number of leaf nodes (page objects) that are descendants of this root within the page tree. */ public int getCount() { return root.getInt(COSName.COUNT, 0); } @Override public COSDictionary getCOSObject() { return root; } /** * Removes the page with the given index from the page tree. * * @param index zero-based page index */ public void remove(int index) { COSDictionary node = get(index + 1, root, 0); remove(node); } /** * Removes the given page from the page tree. * * @param page The page to remove. */ public void remove(PDPage page) { remove(page.getCOSObject()); } /** * Removes the given COS page. */ private void remove(COSDictionary node) { // remove from parent's kids COSDictionary parent = (COSDictionary) node.getDictionaryObject(COSName.PARENT, COSName.P); COSArray kids = parent.getDictionaryObject(COSName.KIDS, COSArray.class); if (kids.removeObject(node)) { // update ancestor counts do { node = (COSDictionary) node.getDictionaryObject(COSName.PARENT, COSName.P); if (node != null) { node.setInt(COSName.COUNT, node.getInt(COSName.COUNT) - 1); } } while (node != null); } } /** * Adds the given page to this page tree. * * @param page The page to add. */ public void add(PDPage page) { // set parent COSDictionary node = page.getCOSObject(); node.setItem(COSName.PARENT, root); // todo: re-balance tree? (or at least group new pages into tree nodes of e.g. 20) // add to parent's kids COSArray kids = root.getDictionaryObject(COSName.KIDS, COSArray.class); kids.add(node); // update ancestor counts do { node = (COSDictionary) node.getDictionaryObject(COSName.PARENT, COSName.P); if (node != null) { node.setInt(COSName.COUNT, node.getInt(COSName.COUNT) + 1); } } while (node != null); } /** * Insert a page before another page within a page tree. * * @param newPage the page to be inserted. * @param nextPage the page that is to be after the new page. * @throws IllegalArgumentException if one attempts to insert a page that isn't part of a page tree. */ public void insertBefore(PDPage newPage, PDPage nextPage) { COSDictionary nextPageDict = nextPage.getCOSObject(); COSDictionary parentDict = nextPageDict.getDictionaryObject(COSName.PARENT, COSDictionary.class); COSArray kids = parentDict.getDictionaryObject(COSName.KIDS, COSArray.class); boolean found = false; for (int i = 0; i < kids.size(); ++i) { COSDictionary pageDict = (COSDictionary) kids.getObject(i); if (pageDict.equals(nextPage.getCOSObject())) { kids.add(i, newPage.getCOSObject()); newPage.getCOSObject().setItem(COSName.PARENT, parentDict); found = true; break; } } if (!found) { throw new IllegalArgumentException("attempted to insert before orphan page"); } increaseParents(parentDict); } /** * Insert a page after another page within a page tree. * * @param newPage the page to be inserted. * @param prevPage the page that is to be before the new page. * @throws IllegalArgumentException if one attempts to insert a page that isn't part of a page tree. */ public void insertAfter(PDPage newPage, PDPage prevPage) { COSDictionary prevPageDict = prevPage.getCOSObject(); COSDictionary parentDict = prevPageDict.getDictionaryObject(COSName.PARENT, COSDictionary.class); COSArray kids = parentDict.getDictionaryObject(COSName.KIDS, COSArray.class); boolean found = false; for (int i = 0; i < kids.size(); ++i) { COSDictionary pageDict = (COSDictionary) kids.getObject(i); if (pageDict.equals(prevPage.getCOSObject())) { kids.add(i + 1, newPage.getCOSObject()); newPage.getCOSObject().setItem(COSName.PARENT, parentDict); found = true; break; } } if (!found) { throw new IllegalArgumentException("attempted to insert before orphan page"); } increaseParents(parentDict); } private void increaseParents(COSDictionary parentDict) { do { int cnt = parentDict.getInt(COSName.COUNT); parentDict.setInt(COSName.COUNT, cnt + 1); parentDict = (COSDictionary) parentDict.getDictionaryObject(COSName.PARENT); } while (parentDict != null); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDResources.java000066400000000000000000000577001320103431700253660ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import static java.util.Optional.ofNullable; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.input.ExistingIndirectCOSObject; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDPropertyList; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.font.PDFontFactory; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDPattern; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; import org.sejda.sambox.pdmodel.graphics.image.PDImageXObject; import org.sejda.sambox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup; import org.sejda.sambox.pdmodel.graphics.pattern.PDAbstractPattern; import org.sejda.sambox.pdmodel.graphics.shading.PDShading; import org.sejda.sambox.pdmodel.graphics.state.PDExtendedGraphicsState; /** * A set of resources available at the page/pages/stream level. * * @author Ben Litchfield * @author John Hewson */ public final class PDResources implements COSObjectable { private final COSDictionary resources; private final ResourceCache cache; // PDFBOX-3442 cache fonts that are not indirect objects, as these aren't cached in ResourceCache // and this would result in huge memory footprint in text extraction private final Map> directFontCache = new HashMap<>(); /** * Constructor for embedding. */ public PDResources() { resources = new COSDictionary(); cache = null; } /** * Constructor for reading. * * @param resourceDictionary The cos dictionary for this resource. */ public PDResources(COSDictionary resourceDictionary) { if (resourceDictionary == null) { throw new IllegalArgumentException("resourceDictionary is null"); } resources = resourceDictionary; cache = null; } /** * Constructor for reading. * * @param resourceDictionary The cos dictionary for this resource. * @param resourceCache The document's resource cache, may be null. */ public PDResources(COSDictionary resourceDictionary, ResourceCache resourceCache) { if (resourceDictionary == null) { throw new IllegalArgumentException("resourceDictionary is null"); } resources = resourceDictionary; cache = resourceCache; } /** * Returns the underlying dictionary. */ @Override public COSDictionary getCOSObject() { return resources; } /** * Returns the font resource with the given name, or null if none exists. * * @param name Name of the font resource. * @throws java.io.IOException if something went wrong. */ public PDFont getFont(COSName name) throws IOException { COSObjectKey key = getIndirectKey(COSName.FONT, name); if (cache != null && key != null) { PDFont cached = cache.getFont(key); if (cached != null) { return cached; } } else if (key == null) { SoftReference ref = directFontCache.get(name); if (ref != null) { PDFont cached = ref.get(); if (cached != null) { return cached; } } } PDFont font = null; COSDictionary dict = (COSDictionary) get(COSName.FONT, name); if (dict != null) { font = PDFontFactory.createFont(dict); } if (cache != null && key != null) { cache.put(key, font); } else if (key == null) { directFontCache.put(name, new SoftReference<>(font)); } return font; } /** * Returns the color space resource with the given name, or null if none exists. * * @param name Name of the color space resource. * @throws java.io.IOException if something went wrong. */ public PDColorSpace getColorSpace(COSName name) throws IOException { return getColorSpace(name, false); } /** * Returns the color space resource with the given name, or null if none exists. This method is for PDFBox internal * use only, others should use {@link getColorSpace(COSName)}. * * @param name Name of the color space resource. * @param wasDefault if current color space was used by a default color space. This parameter is to * @return a new color space. * @throws IOException if something went wrong. */ public PDColorSpace getColorSpace(COSName name, boolean wasDefault) throws IOException { COSObjectKey key = getIndirectKey(COSName.COLORSPACE, name); if (cache != null && key != null) { PDColorSpace cached = cache.getColorSpace(key); if (cached != null) { return cached; } } // get the instance PDColorSpace colorSpace; COSBase object = get(COSName.COLORSPACE, name); if (object != null) { colorSpace = PDColorSpace.create(object, this, wasDefault); } else { colorSpace = PDColorSpace.create(name, this, wasDefault); } // we can't cache PDPattern, because it holds page resources, see PDFBOX-2370 if (cache != null && !(colorSpace instanceof PDPattern)) { cache.put(key, colorSpace); } return colorSpace; } /** * Returns true if the given color space name exists in these resources. * * @param name Name of the color space resource. */ public boolean hasColorSpace(COSName name) { return get(COSName.COLORSPACE, name) != null; } /** * Returns the extended graphics state resource with the given name, or null if none exists. * * @param name Name of the graphics state resource. */ public PDExtendedGraphicsState getExtGState(COSName name) { COSObjectKey key = getIndirectKey(COSName.EXT_G_STATE, name); if (cache != null && key != null) { PDExtendedGraphicsState cached = cache.getExtGState(key); if (cached != null) { return cached; } } // get the instance PDExtendedGraphicsState extGState = null; COSDictionary dict = (COSDictionary) get(COSName.EXT_G_STATE, name); if (dict != null) { extGState = new PDExtendedGraphicsState(dict); } if (cache != null) { cache.put(key, extGState); } return extGState; } /** * Returns the shading resource with the given name, or null if none exists. * * @param name Name of the shading resource. * @throws java.io.IOException if something went wrong. */ public PDShading getShading(COSName name) throws IOException { COSObjectKey key = getIndirectKey(COSName.SHADING, name); if (cache != null && key != null) { PDShading cached = cache.getShading(key); if (cached != null) { return cached; } } // get the instance PDShading shading = null; COSDictionary dict = (COSDictionary) get(COSName.SHADING, name); if (dict != null) { shading = PDShading.create(dict); } if (cache != null) { cache.put(key, shading); } return shading; } /** * Returns the pattern resource with the given name, or null if none exists. * * @param name Name of the pattern resource. * @throws java.io.IOException if something went wrong. */ public PDAbstractPattern getPattern(COSName name) throws IOException { COSObjectKey key = getIndirectKey(COSName.PATTERN, name); if (cache != null && key != null) { PDAbstractPattern cached = cache.getPattern(key); if (cached != null) { return cached; } } // get the instance PDAbstractPattern pattern = null; COSDictionary dict = (COSDictionary) get(COSName.PATTERN, name); if (dict != null) { pattern = PDAbstractPattern.create(dict); } if (cache != null) { cache.put(key, pattern); } return pattern; } /** * Returns the property list resource with the given name, or null if none exists. * * @param name Name of the property list resource. */ public PDPropertyList getProperties(COSName name) { COSObjectKey key = getIndirectKey(COSName.PROPERTIES, name); if (cache != null && key != null) { PDPropertyList cached = cache.getProperties(key); if (cached != null) { return cached; } } // get the instance PDPropertyList propertyList = null; COSDictionary dict = (COSDictionary) get(COSName.PROPERTIES, name); if (dict != null) { propertyList = PDPropertyList.create(dict); } if (cache != null) { cache.put(key, propertyList); } return propertyList; } /** * Tells whether the XObject resource with the given name is an image. * * @param name Name of the XObject resource. * @return true if it is an image XObject, false if not. */ public boolean isImageXObject(COSName name) { // get the instance return Optional.ofNullable(get(COSName.XOBJECT, name)).map(COSBase::getCOSObject) .filter(s -> s instanceof COSStream).map(s -> (COSStream) s) .map(s -> COSName.IMAGE.equals(s.getCOSName(COSName.SUBTYPE))).orElse(false); } /** * Tells whether the XObject resource with the given name is an form. * * @param name Name of the XObject resource. * @return true if it is an form XObject, false if not. */ public boolean isFormXObject(COSName name) { // get the instance return Optional.ofNullable(get(COSName.XOBJECT, name)).map(COSBase::getCOSObject) .map(s -> (COSStream) s) .map(s -> COSName.FORM.equals(s.getCOSName(COSName.SUBTYPE))).orElse(false); } /** * Returns the XObject resource with the given name, or null if none exists. * * @param name Name of the XObject resource. * @throws java.io.IOException if something went wrong. */ public PDXObject getXObject(COSName name) throws IOException { COSObjectKey key = getIndirectKey(COSName.XOBJECT, name); if (cache != null && key != null) { PDXObject cached = cache.getXObject(key); if (cached != null) { return cached; } } // get the instance PDXObject xobject; COSBase value = get(COSName.XOBJECT, name); if (value == null) { xobject = null; } else { xobject = PDXObject.createXObject(value.getCOSObject(), this); } if (cache != null && isAllowedCache(xobject)) { cache.put(key, xobject); } return xobject; } private boolean isAllowedCache(PDXObject xobject) { if (xobject instanceof PDImageXObject) { COSBase colorSpace = xobject.getCOSObject().getDictionaryObject(COSName.COLORSPACE); if (colorSpace instanceof COSName) { // don't cache if it might use page resources, see PDFBOX-2370 and PDFBOX-3484 COSName colorSpaceName = (COSName) colorSpace; if (colorSpaceName.equals(COSName.DEVICECMYK) && hasColorSpace(COSName.DEFAULT_CMYK)) { return false; } if (colorSpaceName.equals(COSName.DEVICERGB) && hasColorSpace(COSName.DEFAULT_RGB)) { return false; } if (colorSpaceName.equals(COSName.DEVICEGRAY) && hasColorSpace(COSName.DEFAULT_GRAY)) { return false; } if (hasColorSpace(colorSpaceName)) { return false; } } } return true; } /** * Returns the resource with the given name and kind as an indirect object, or null. */ private COSObjectKey getIndirectKey(COSName kind, COSName name) { COSBase found = Optional .ofNullable(resources.getDictionaryObject(kind, COSDictionary.class)) .map(d -> d.getItem(name)).orElse(null); if (found instanceof ExistingIndirectCOSObject) { return ((ExistingIndirectCOSObject) found).id().objectIdentifier; } if (found != null && found.id() != null) { return found.id().objectIdentifier; } return null; } /** * Returns the resource with the given name and kind, or null. */ private COSBase get(COSName kind, COSName name) { return ofNullable(resources.getDictionaryObject(kind, COSDictionary.class)) .map(d -> d.getDictionaryObject(name)).orElse(null); } /** * Returns the names of the color space resources, if any. */ public Iterable getColorSpaceNames() { return getNames(COSName.COLORSPACE); } /** * Returns the names of the XObject resources, if any. */ public Iterable getXObjectNames() { return getNames(COSName.XOBJECT); } /** * Returns the names of the font resources, if any. */ public Iterable getFontNames() { return getNames(COSName.FONT); } /** * Returns the names of the property list resources, if any. */ public Iterable getPropertiesNames() { return getNames(COSName.PROPERTIES); } /** * Returns the names of the shading resources, if any. */ public Iterable getShadingNames() { return getNames(COSName.SHADING); } /** * Returns the names of the pattern resources, if any. */ public Iterable getPatternNames() { return getNames(COSName.PATTERN); } /** * Returns the names of the extended graphics state resources, if any. */ public Iterable getExtGStateNames() { return getNames(COSName.EXT_G_STATE); } /** * Returns the resource names of the given kind. */ private Iterable getNames(COSName kind) { return ofNullable(resources.getDictionaryObject(kind, COSDictionary.class)) .map(COSDictionary::keySet).orElseGet(Collections::emptySet); } /** * Adds the given font to the resources of the current page and returns the name for the new resources. Returns the * existing resource name if the given item already exists. * * @param font the font to add * @return the name of the resource in the resources dictionary */ public COSName add(PDFont font) { return add(COSName.FONT, "F", font); } /** * Adds the given color space to the resources of the current page and returns the name for the new resources. * Returns the existing resource name if the given item already exists. * * @param colorSpace the color space to add * @return the name of the resource in the resources dictionary */ public COSName add(PDColorSpace colorSpace) { return add(COSName.COLORSPACE, "cs", colorSpace); } /** * Adds the given extended graphics state to the resources of the current page and returns the name for the new * resources. Returns the existing resource name if the given item already exists. * * @param extGState the extended graphics state to add * @return the name of the resource in the resources dictionary */ public COSName add(PDExtendedGraphicsState extGState) { return add(COSName.EXT_G_STATE, "gs", extGState); } /** * Adds the given shading to the resources of the current page and returns the name for the new resources. Returns * the existing resource name if the given item already exists. * * @param shading the shading to add * @return the name of the resource in the resources dictionary */ public COSName add(PDShading shading) { return add(COSName.SHADING, "sh", shading); } /** * Adds the given pattern to the resources of the current page and returns the name for the new resources. Returns * the existing resource name if the given item already exists. * * @param pattern the pattern to add * @return the name of the resource in the resources dictionary */ public COSName add(PDAbstractPattern pattern) { return add(COSName.PATTERN, "p", pattern); } /** * Adds the given property list to the resources of the current page and returns the name for the new resources. * Returns the existing resource name if the given item already exists. * * @param properties the property list to add * @return the name of the resource in the resources dictionary */ public COSName add(PDPropertyList properties) { if (properties instanceof PDOptionalContentGroup) { return add(COSName.PROPERTIES, "oc", properties); } return add(COSName.PROPERTIES, "Prop", properties); } /** * Adds the given image to the resources of the current page and returns the name for the new resources. Returns the * existing resource name if the given item already exists. * * @param image the image to add * @return the name of the resource in the resources dictionary */ public COSName add(PDImageXObject image) { return add(COSName.XOBJECT, "Im", image); } /** * Adds the given form to the resources of the current page and returns the name for the new resources. Returns the * existing resource name if the given item already exists. * * @param form the form to add * @return the name of the resource in the resources dictionary */ public COSName add(PDFormXObject form) { return add(COSName.XOBJECT, "Form", form); } /** * Adds the given XObject to the resources of the current page and returns the name for the new resources. Returns * the existing resource name if the given item already exists. * * @param xobject the XObject to add * @param prefix the prefix to be used when creating the resource name * @return the name of the resource in the resources dictionary */ public COSName add(PDXObject xobject, String prefix) { return add(COSName.XOBJECT, prefix, xobject); } /** * Adds the given resource if it does not already exist. */ private COSName add(COSName kind, String prefix, COSObjectable object) { // return the existing key if the item exists already COSDictionary dict = resources.getDictionaryObject(kind, COSDictionary.class); return Optional.ofNullable(dict).map(d -> d.getKeyForValue(object.getCOSObject())) .orElseGet(() -> { COSName name = createKey(kind, prefix); put(kind, name, object); return name; }); } /** * Returns a unique key for a new resource. */ private COSName createKey(COSName kind, String prefix) { COSDictionary dict = resources.getDictionaryObject(kind, COSDictionary.class); if (dict == null) { return COSName.getPDFName(prefix + 1); } // find a unique key String key; int n = dict.keySet().size(); do { ++n; key = prefix + n; } while (dict.containsKey(key)); return COSName.getPDFName(key); } /** * Sets the value of a given named resource. */ private void put(COSName kind, COSName name, COSObjectable object) { COSDictionary dict = resources.getDictionaryObject(kind, COSDictionary.class); if (dict == null) { dict = new COSDictionary(); resources.setItem(kind, dict); } dict.setItem(name, object); } /** * Sets the font resource with the given name. * * @param name the name of the resource * @param font the font to be added */ public void put(COSName name, PDFont font) { put(COSName.FONT, name, font); } /** * Sets the color space resource with the given name. * * @param name the name of the resource * @param colorSpace the color space to be added */ public void put(COSName name, PDColorSpace colorSpace) { put(COSName.COLORSPACE, name, colorSpace); } /** * Sets the extended graphics state resource with the given name. * * @param name the name of the resource * @param extGState the extended graphics state to be added */ public void put(COSName name, PDExtendedGraphicsState extGState) { put(COSName.EXT_G_STATE, name, extGState); } /** * Sets the shading resource with the given name. * * @param name the name of the resource * @param shading the shading to be added */ public void put(COSName name, PDShading shading) { put(COSName.SHADING, name, shading); } /** * Sets the pattern resource with the given name. * * @param name the name of the resource * @param pattern the pattern to be added */ public void put(COSName name, PDAbstractPattern pattern) { put(COSName.PATTERN, name, pattern); } /** * Sets the property list resource with the given name. * * @param name the name of the resource * @param properties the property list to be added */ public void put(COSName name, PDPropertyList properties) { put(COSName.PROPERTIES, name, properties); } /** * Sets the XObject resource with the given name. * * @param name the name of the resource * @param xobject the XObject to be added */ public void put(COSName name, PDXObject xobject) { put(COSName.XOBJECT, name, xobject); } /** * Returns the resource cache associated with the Resources, or null if there is none. */ public ResourceCache getResourceCache() { return cache; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PDStructureElementNameTreeNode.java000066400000000000000000000034771320103431700311570ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.pdmodel.common.PDNameTreeNode; import org.sejda.sambox.pdmodel.documentinterchange.logicalstructure.PDStructureElement; /** * todo: JavaDoc * * @author John Hewson */ public class PDStructureElementNameTreeNode extends PDNameTreeNode { /** * Constructor. */ public PDStructureElementNameTreeNode() { super(); } /** * Constructor. * * @param dic The COS dictionary. */ public PDStructureElementNameTreeNode(COSDictionary dic) { super(dic); } @Override protected PDStructureElement convertCOSToPD(COSBase base) throws IOException { return new PDStructureElement((COSDictionary) base); } @Override protected PDNameTreeNode createChildNode(COSDictionary dic) { return new PDStructureElementNameTreeNode(dic); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PageLayout.java000066400000000000000000000050141320103431700252310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; /** * A name object specifying the page layout shall be used when the document is opened. * * @author John Hewson */ public enum PageLayout { /** Display one page at a time. */ SINGLE_PAGE("SinglePage"), /** Display the pages in one column. */ ONE_COLUMN("OneColumn"), /** Display the pages in two columns), with odd numbered pages on the left. */ TWO_COLUMN_LEFT("TwoColumnLeft"), /** Display the pages in two columns), with odd numbered pages on the right. */ TWO_COLUMN_RIGHT("TwoColumnRight"), /** Display the pages two at a time), with odd-numbered pages on the left. */ TWO_PAGE_LEFT("TwoPageLeft"), /** Display the pages two at a time), with odd-numbered pages on the right. */ TWO_PAGE_RIGHT("TwoPageRight"); public static PageLayout fromString(String value) { if (value.equals("SinglePage")) { return SINGLE_PAGE; } else if (value.equals("OneColumn")) { return ONE_COLUMN; } else if (value.equals("TwoColumnLeft")) { return TWO_COLUMN_LEFT; } else if(value.equals("TwoColumnRight")) { return TWO_COLUMN_RIGHT; } else if (value.equals("TwoPageLeft")) { return TWO_PAGE_LEFT; } else if (value.equals("TwoPageRight")) { return TWO_PAGE_RIGHT; } throw new IllegalArgumentException(value); } private final String value; PageLayout(String value) { this.value = value; } /** * Returns the string value, as used in a PDF file. */ public String stringValue() { return value; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PageMode.java000066400000000000000000000046051320103431700246450ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; /** * A name object specifying how the document shall be displayed when opened. * * @author John Hewson */ public enum PageMode { /** Neither the outline nor the thumbnails are displayed. */ USE_NONE("UseNone"), /** Show bookmarks when pdf is opened. */ USE_OUTLINES("UseOutlines"), /** Show thumbnails when pdf is opened. */ USE_THUMBS("UseThumbs"), /** Full screen mode with no menu bar, window controls. */ FULL_SCREEN("FullScreen"), /** Optional content group panel is visible when opened. */ USE_OPTIONAL_CONTENT("UseOC"), /** Attachments panel is visible. */ USE_ATTACHMENTS("UseAttachments"); public static PageMode fromString(String value) { if (value.equals("UseNone")) { return USE_NONE; } else if (value.equals("UseOutlines")) { return USE_OUTLINES; } else if (value.equals("UseThumbs")) { return USE_THUMBS; } else if (value.equals("FullScreen")) { return FULL_SCREEN; } else if (value.equals("UseOC")) { return USE_OPTIONAL_CONTENT; } else if (value.equals("UseAttachments")) { return USE_ATTACHMENTS; } throw new IllegalArgumentException(value); } private final String value; PageMode(String value) { this.value = value; } /** * Returns the string value, as used in a PDF file. */ public String stringValue() { return value; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/PageNotFoundException.java000066400000000000000000000023611320103431700273710ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; public class PageNotFoundException extends RuntimeException { private int page; private String sourcePath; public PageNotFoundException(String message, int page, String sourcePath) { super(message); this.page = page; this.sourcePath = sourcePath; } public int getPage() { return page; } public String getSourcePath() { return sourcePath; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/ResourceCache.java000066400000000000000000000072121320103431700256740ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel; import java.io.IOException; import org.sejda.sambox.cos.COSObjectKey; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDPropertyList; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.pattern.PDAbstractPattern; import org.sejda.sambox.pdmodel.graphics.shading.PDShading; import org.sejda.sambox.pdmodel.graphics.state.PDExtendedGraphicsState; /** * A document-wide cache for page resources. * * @author John Hewson */ public interface ResourceCache { /** * Returns the font resource for the given key object, if it is in the cache. */ PDFont getFont(COSObjectKey key) throws IOException; /** * Returns the color space resource for the given key object, if it is in the cache. */ PDColorSpace getColorSpace(COSObjectKey key) throws IOException; /** * Returns the external graphics state resource for the given key object, if it is in the cache. */ PDExtendedGraphicsState getExtGState(COSObjectKey key); /** * Returns the shading resource for the given key object, if it is in the cache. */ PDShading getShading(COSObjectKey key) throws IOException; /** * Returns the pattern resource for the given key object, if it is in the cache. */ PDAbstractPattern getPattern(COSObjectKey key) throws IOException; /** * Returns the property list resource for the given key object, if it is in the cache. */ PDPropertyList getProperties(COSObjectKey key); /** * Returns the XObject resource for the given key object, if it is in the cache. */ PDXObject getXObject(COSObjectKey key) throws IOException; /** * Puts the given key font resource in the cache. */ void put(COSObjectKey key, PDFont font) throws IOException; /** * Puts the given key color space resource in the cache. */ void put(COSObjectKey key, PDColorSpace colorSpace) throws IOException; /** * Puts the given key extended graphics state resource in the cache. */ void put(COSObjectKey key, PDExtendedGraphicsState extGState); /** * Puts the given key shading resource in the cache. */ void put(COSObjectKey key, PDShading shading) throws IOException; /** * Puts the given key pattern resource in the cache. */ void put(COSObjectKey key, PDAbstractPattern pattern) throws IOException; /** * Puts the given key property list resource in the cache. */ void put(COSObjectKey key, PDPropertyList propertyList); /** * Puts the given key XObject resource in the cache. */ void put(COSObjectKey key, PDXObject xobject) throws IOException; /** * Clears the cache */ void clear(); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/000077500000000000000000000000001320103431700236045ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/COSDictionaryMap.java000066400000000000000000000153571320103431700275720ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSString; /** * This is a Map that will automatically sync the contents to a COSDictionary. * * @author Ben Litchfield */ public class COSDictionaryMap implements Map { private final COSDictionary map; private final Map actuals; /** * Constructor for this map. * * @param actualsMap The map with standard java objects as values. * @param dicMap The map with COSBase objects as values. */ public COSDictionaryMap(Map actualsMap, COSDictionary dicMap) { actuals = actualsMap; map = dicMap; } /** * {@inheritDoc} */ @Override public int size() { return map.size(); } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return size() == 0; } /** * {@inheritDoc} */ @Override public boolean containsKey(Object key) { return actuals.containsKey(key); } /** * {@inheritDoc} */ @Override public boolean containsValue(Object value) { return actuals.containsValue(value); } /** * {@inheritDoc} */ @Override public V get(Object key) { return actuals.get(key); } /** * {@inheritDoc} */ @Override public V put(K key, V value) { COSObjectable object = (COSObjectable) value; map.setItem(COSName.getPDFName((String) key), object.getCOSObject()); return actuals.put(key, value); } /** * {@inheritDoc} */ @Override public V remove(Object key) { map.removeItem(COSName.getPDFName((String) key)); return actuals.remove(key); } /** * {@inheritDoc} */ @Override public void putAll(Map t) { throw new RuntimeException("Not yet implemented"); } /** * {@inheritDoc} */ @Override public void clear() { map.clear(); actuals.clear(); } /** * {@inheritDoc} */ @Override public Set keySet() { return actuals.keySet(); } /** * {@inheritDoc} */ @Override public Collection values() { return actuals.values(); } /** * {@inheritDoc} */ @Override public Set> entrySet() { return Collections.unmodifiableSet(actuals.entrySet()); } /** * {@inheritDoc} */ @Override public boolean equals(Object o) { boolean retval = false; if (o instanceof COSDictionaryMap) { COSDictionaryMap other = (COSDictionaryMap) o; retval = other.map.equals(this.map); } return retval; } /** * {@inheritDoc} */ @Override public String toString() { return actuals.toString(); } /** * {@inheritDoc} */ @Override public int hashCode() { return map.hashCode(); } /** * This will take a map<String,COSObjectable> and convert it into a COSDictionary. * * @param someMap A map containing COSObjectables * * @return A proper COSDictionary */ public static COSDictionary convert(Map someMap) { COSDictionary dic = new COSDictionary(); for (Entry entry : someMap.entrySet()) { String name = entry.getKey(); COSObjectable object = (COSObjectable) entry.getValue(); dic.setItem(COSName.getPDFName(name), object.getCOSObject()); } return dic; } /** * This will take a COS dictionary and convert it into COSDictionaryMap. All cos objects will be converted to their * primitive form. * * @param map The COS mappings. * @return A standard java map. * @throws IOException If there is an error during the conversion. */ public static COSDictionaryMap convertBasicTypesToMap(COSDictionary map) throws IOException { COSDictionaryMap retval = null; if (map != null) { Map actualMap = new HashMap(); for (COSName key : map.keySet()) { COSBase cosObj = map.getDictionaryObject(key); Object actualObject = null; if (cosObj instanceof COSString) { actualObject = ((COSString) cosObj).getString(); } else if (cosObj instanceof COSInteger) { actualObject = ((COSInteger) cosObj).intValue(); } else if (cosObj instanceof COSName) { actualObject = ((COSName) cosObj).getName(); } else if (cosObj instanceof COSFloat) { actualObject = ((COSFloat) cosObj).floatValue(); } else if (cosObj instanceof COSBoolean) { actualObject = ((COSBoolean) cosObj).getValue() ? Boolean.TRUE : Boolean.FALSE; } else { throw new IOException("Error:unknown type of object to convert:" + cosObj); } actualMap.put(key.getName(), actualObject); } retval = new COSDictionaryMap(actualMap, map); } return retval; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDDestinationOrAction.java000066400000000000000000000021441320103431700306140ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import org.sejda.sambox.cos.COSObjectable; /** * This is an interface used for some elements such as the document * OpenAction that can be either a Destination or an Action. * * @author Ben Litchfield * */ public interface PDDestinationOrAction extends COSObjectable { } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDDictionaryWrapper.java000066400000000000000000000036331320103431700303460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSObjectable; /** * A wrapper for a COS dictionary. * * @author Johannes Koch * */ public class PDDictionaryWrapper implements COSObjectable { private final COSDictionary dictionary; public PDDictionaryWrapper() { this.dictionary = new COSDictionary(); } /** * Creates a new instance with a given COS dictionary. * * @param dictionary the dictionary */ public PDDictionaryWrapper(COSDictionary dictionary) { this.dictionary = dictionary; } @Override public final COSDictionary getCOSObject() { return this.dictionary; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof PDDictionaryWrapper) { return this.dictionary.equals(((PDDictionaryWrapper) obj).dictionary); } return false; } @Override public int hashCode() { return this.dictionary.hashCode(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDMetadata.java000066400000000000000000000054271320103431700264230ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; /** * This class represents metadata for various objects in a PDF document. * * @author Ben Litchfield */ public class PDMetadata extends PDStream { /** * This will create a new PDMetadata object. */ public PDMetadata() { getCOSObject().setName(COSName.TYPE, "Metadata"); getCOSObject().setName(COSName.SUBTYPE, "XML"); } /** * Constructor. Reads all data from the input stream and embeds it into the * document, this will close the InputStream. * * @param str The stream parameter. * @param filtered True if the stream already has a filter applied. * @throws IOException If there is an error creating the stream in the document. */ public PDMetadata(InputStream str) throws IOException { super(str); getCOSObject().setName(COSName.TYPE, "Metadata"); getCOSObject().setName(COSName.SUBTYPE, "XML"); } /** * Constructor. * * @param str The stream parameter. */ public PDMetadata( COSStream str ) { super( str ); } /** * Extract the XMP metadata. * To persist changes back to the PDF you must call importXMPMetadata. * * @return A stream to get the xmp data from. * * @throws IOException If there is an error parsing the XMP data. */ public InputStream exportXMPMetadata() throws IOException { return createInputStream(); } /** * Import an XMP stream into the PDF document. * * @param xmp The XMP data. * * @throws IOException If there is an error generating the XML document. */ public void importXMPMetadata( byte[] xmp ) throws IOException { try (OutputStream os = createOutputStream()) { os.write(xmp); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDNameTreeNode.java000066400000000000000000000277361320103431700272200ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class represents a node in a name tree. * * @author Ben Litchfield */ public abstract class PDNameTreeNode implements COSObjectable { private static final Logger LOG = LoggerFactory.getLogger(PDNameTreeNode.class); private final COSDictionary node; private PDNameTreeNode parent; /** * Constructor. */ protected PDNameTreeNode() { node = new COSDictionary(); } /** * Constructor. * * @param dict The dictionary that holds the name information. */ protected PDNameTreeNode(COSDictionary dict) { node = dict; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return node; } /** * Returns the parent node. * * @return parent node */ public PDNameTreeNode getParent() { return parent; } /** * Sets the parent to the given node. * * @param parentNode the node to be set as parent */ public void setParent(PDNameTreeNode parentNode) { parent = parentNode; calculateLimits(); } /** * Determines if this is a root node or not. * * @return true if this is a root node */ public boolean isRootNode() { return parent == null; } /** * Return the children of this node. This list will contain PDNameTreeNode objects. * * @return The list of children or null if there are no children. */ public List> getKids() { if (nonNull(node)) { COSArray kids = (COSArray) node.getDictionaryObject(COSName.KIDS); if (kids != null) { List> pdObjects = new ArrayList<>(); for (int i = 0; i < kids.size(); i++) { pdObjects.add(createChildNode((COSDictionary) kids.getObject(i))); } return new COSArrayList<>(pdObjects, kids); } } return null; } /** * Set the children of this named tree. * * @param kids The children of this named tree. */ public void setKids(List> kids) { if (kids != null && kids.size() > 0) { for (PDNameTreeNode kidsNode : kids) { kidsNode.setParent(this); } node.setItem(COSName.KIDS, COSArrayList.converterToCOSArray(kids)); // root nodes with kids don't have Names if (isRootNode()) { node.removeItem(COSName.NAMES); } } else { node.removeItem(COSName.KIDS); node.removeItem(COSName.LIMITS); } calculateLimits(); } private void calculateLimits() { if (isRootNode()) { node.removeItem(COSName.LIMITS); } else { List> kids = getKids(); if (kids != null && kids.size() > 0) { PDNameTreeNode firstKid = kids.get(0); PDNameTreeNode lastKid = kids.get(kids.size() - 1); String lowerLimit = firstKid.getLowerLimit(); setLowerLimit(lowerLimit); String upperLimit = lastKid.getUpperLimit(); setUpperLimit(upperLimit); } else { try { Map names = getNames(); if (names != null && names.size() > 0) { Set strings = names.keySet(); String[] keys = strings.toArray(new String[strings.size()]); String lowerLimit = keys[0]; setLowerLimit(lowerLimit); String upperLimit = keys[keys.length - 1]; setUpperLimit(upperLimit); } else { node.removeItem(COSName.LIMITS); } } catch (IOException exception) { node.removeItem(COSName.LIMITS); LOG.error("Error while calculating the Limits of a PageNameTreeNode:", exception); } } } } /** * The name to retrieve. * * @param name The name in the tree. * @return The value of the name in the tree. * @throws IOException If an there is a problem creating the destinations. */ public T getValue(String name) { try { Map names = getNames(); if (isNull(names)) { List> kids = getKids(); if (kids != null) { for (PDNameTreeNode childNode : kids) { if (childNode.couldContain(name)) { T value = childNode.getValue(name); if (nonNull(value)) { return value; } } } } else { LOG.warn("NameTreeNode does not have \"names\" nor \"kids\" objects."); } } else { return names.get(name); } } catch (IOException e) { LOG.warn("NameTreeNode couldn't get the names map", e); } return null; } /** * @param name * @return true if the given name can be part of this node/branch */ private boolean couldContain(String name) { if (isNull(node) || isNull(getLowerLimit()) || isNull(getUpperLimit())) { LOG.warn("Missing required name tree node Limits array"); return false; } return getLowerLimit().compareTo(name) <= 0 && getUpperLimit().compareTo(name) >= 0; } /** * This will return a map of names. The key will be a string, and the value will depend on where this class is being * used. * * @return ordered map of cos objects or null if dictionary contains no 'Names' entry * @throws IOException If there is an error while creating the sub types. */ public Map getNames() throws IOException { COSArray namesArray = (COSArray) node.getDictionaryObject(COSName.NAMES); if (namesArray != null) { Map names = new LinkedHashMap<>(); for (int i = 0; i < namesArray.size(); i += 2) { COSString key = (COSString) namesArray.getObject(i); COSBase cosValue = namesArray.getObject(i + 1); names.put(key.getString(), convertCOSToPD(cosValue)); } return Collections.unmodifiableMap(names); } return null; } /** * Method to convert the COS value in the name tree to the PD Model object. The default implementation will simply * return the given COSBase object. Subclasses should do something specific. * * @param base The COS object to convert. * @return The converted PD Model object. * @throws IOException If there is an error during creation. */ protected abstract T convertCOSToPD(COSBase base) throws IOException; /** * Create a child node object. * * @param dic The dictionary for the child node object to refer to. * @return The new child node object. */ protected abstract PDNameTreeNode createChildNode(COSDictionary dic); /** * Set the names of for this node. The keys should be java.lang.String and the values must be a COSObjectable. This * method will set the appropriate upper and lower limits based on the keys in the map. * * @param names map of names to objects, or null */ public void setNames(Map names) { if (names == null) { node.setItem(COSName.NAMES, (COSObjectable) null); node.setItem(COSName.LIMITS, (COSObjectable) null); } else { COSArray array = new COSArray(); List keys = new ArrayList<>(names.keySet()); Collections.sort(keys); for (String key : keys) { array.add(COSString.parseLiteral(key)); array.add(names.get(key)); } node.setItem(COSName.NAMES, array); calculateLimits(); } } /** * Get the highest value for a key in the name map. * * @return The highest value for a key in the map. */ public String getUpperLimit() { String retval = null; COSArray arr = (COSArray) node.getDictionaryObject(COSName.LIMITS); if (arr != null) { retval = arr.getString(1); } return retval; } /** * Set the highest value for the key in the map. * * @param upper The new highest value for a key in the map. */ private void setUpperLimit(String upper) { COSArray arr = (COSArray) node.getDictionaryObject(COSName.LIMITS); if (arr == null) { arr = new COSArray(); arr.add(null); arr.add(null); node.setItem(COSName.LIMITS, arr); } arr.setString(1, upper); } /** * Get the lowest value for a key in the name map. * * @return The lowest value for a key in the map. */ public String getLowerLimit() { COSArray arr = (COSArray) node.getDictionaryObject(COSName.LIMITS); if (nonNull(arr)) { return arr.getString(0); } return null; } /** * Set the lowest value for the key in the map. * * @param lower The new lowest value for a key in the map. */ private void setLowerLimit(String lower) { COSArray arr = (COSArray) node.getDictionaryObject(COSName.LIMITS); if (arr == null) { arr = new COSArray(); arr.add(null); arr.add(null); node.setItem(COSName.LIMITS, arr); } arr.setString(0, lower); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDNumberTreeNode.java000066400000000000000000000246731320103431700275650ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import static java.util.Objects.nonNull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class represents a PDF Number tree. See the PDF Reference 1.7 section 7.9.7 for more details. * * @author Ben Litchfield, * @author Igor Podolskiy */ public class PDNumberTreeNode implements COSObjectable { private static final Logger LOG = LoggerFactory.getLogger(PDNumberTreeNode.class); private final COSDictionary node; private Class valueType = null; /** * Constructor. * * @param valueClass The PD Model type of object that is the value. */ public PDNumberTreeNode(Class valueClass) { node = new COSDictionary(); valueType = valueClass; } /** * Constructor. * * @param dict The dictionary that holds the name information. * @param valueClass The PD Model type of object that is the value. */ public PDNumberTreeNode(COSDictionary dict, Class valueClass) { node = dict; valueType = valueClass; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return node; } /** * Return the children of this node. This list will contain PDNumberTreeNode objects. * * @return The list of children or null if there are no children. */ public List getKids() { COSArray kids = node.getDictionaryObject(COSName.KIDS, COSArray.class); if (nonNull(kids)) { List pdObjects = new ArrayList<>(); for (int i = 0; i < kids.size(); i++) { pdObjects.add(createChildNode((COSDictionary) kids.getObject(i))); } return new COSArrayList<>(pdObjects, kids); } return null; } /** * Set the children of this number tree. * * @param kids The children of this number tree. */ public void setKids(List kids) { if (kids != null && !kids.isEmpty()) { PDNumberTreeNode firstKid = kids.get(0); PDNumberTreeNode lastKid = kids.get(kids.size() - 1); Integer lowerLimit = firstKid.getLowerLimit(); this.setLowerLimit(lowerLimit); Integer upperLimit = lastKid.getUpperLimit(); this.setUpperLimit(upperLimit); } else if (node.getDictionaryObject(COSName.NUMS) == null) { // Remove limits if there are no kids and no numbers set. node.removeItem(COSName.LIMITS); } node.setItem(COSName.KIDS, COSArrayList.converterToCOSArray(kids)); } /** * Returns the value corresponding to an index in the number tree. * * @param index The index in the number tree. * * @return The value corresponding to the index. * * @throws IOException If there is a problem creating the values. */ public Object getValue(Integer index) throws IOException { Map names = getNumbers(); if (nonNull(names)) { return names.get(index); } Object retval = null; List kids = getKids(); if (nonNull(kids)) { for (int i = 0; i < kids.size() && retval == null; i++) { PDNumberTreeNode childNode = kids.get(i); if (childNode.getLowerLimit().compareTo(index) <= 0 && childNode.getUpperLimit().compareTo(index) >= 0) { retval = childNode.getValue(index); } } } else { LOG.warn("NumberTreeNode does not have \"nums\" nor \"kids\" objects."); } return retval; } /** * This will return a map of numbers. The key will be a java.lang.Integer, the value will depend on where this class * is being used. * * @return A map of COS objects. * * @throws IOException If there is a problem creating the values. */ public Map getNumbers() throws IOException { Map indices = null; COSArray namesArray = (COSArray) node.getDictionaryObject(COSName.NUMS); if (namesArray != null) { indices = new HashMap<>(); for (int i = 0; i < namesArray.size(); i += 2) { COSInteger key = (COSInteger) namesArray.getObject(i); COSBase cosValue = namesArray.getObject(i + 1); COSObjectable pdValue = convertCOSToPD(cosValue); indices.put(key.intValue(), pdValue); } indices = Collections.unmodifiableMap(indices); } return indices; } /** * Method to convert the COS value in the name tree to the PD Model object. The default implementation will simply * use reflection to create the correct object type. Subclasses can do whatever they want. * * @param base The COS object to convert. * @return The converted PD Model object. * @throws IOException If there is an error during creation. */ protected COSObjectable convertCOSToPD(COSBase base) throws IOException { try { return valueType.getConstructor(base.getClass()).newInstance(base); } catch (Throwable t) { throw new IOException("Error while trying to create value in number tree", t); } } /** * Create a child node object. * * @param dic The dictionary for the child node object to refer to. * @return The new child node object. */ protected PDNumberTreeNode createChildNode(COSDictionary dic) { return new PDNumberTreeNode(dic, valueType); } /** * Set the names of for this node. The keys should be java.lang.String and the values must be a COSObjectable. This * method will set the appropriate upper and lower limits based on the keys in the map. * * @param numbers The map of names to objects. */ public void setNumbers(Map numbers) { if (numbers == null) { node.setItem(COSName.NUMS, (COSObjectable) null); node.setItem(COSName.LIMITS, (COSObjectable) null); } else { List keys = new ArrayList<>(numbers.keySet()); Collections.sort(keys); COSArray array = new COSArray(); for (Integer key : keys) { array.add(COSInteger.get(key)); COSObjectable obj = numbers.get(key); array.add(obj); } Integer lower = null; Integer upper = null; if (!keys.isEmpty()) { lower = keys.get(0); upper = keys.get(keys.size() - 1); } setUpperLimit(upper); setLowerLimit(lower); node.setItem(COSName.NUMS, array); } } /** * Get the highest value for a key in the name map. * * @return The highest value for a key in the map. */ public Integer getUpperLimit() { Integer retval = null; COSArray arr = (COSArray) node.getDictionaryObject(COSName.LIMITS); if (arr != null && arr.get(0) != null) { retval = arr.getInt(1); } return retval; } /** * Set the highest value for the key in the map. * * @param upper The new highest value for a key in the map. */ private void setUpperLimit(Integer upper) { COSArray arr = (COSArray) node.getDictionaryObject(COSName.LIMITS); if (arr == null) { arr = new COSArray(); arr.add(null); arr.add(null); node.setItem(COSName.LIMITS, arr); } if (upper != null) { arr.set(1, COSInteger.get(upper)); } else { arr.set(1, null); } } /** * Get the lowest value for a key in the name map. * * @return The lowest value for a key in the map. */ public Integer getLowerLimit() { Integer retval = null; COSArray arr = (COSArray) node.getDictionaryObject(COSName.LIMITS); if (arr != null && arr.get(0) != null) { retval = arr.getInt(0); } return retval; } /** * Set the lowest value for the key in the map. * * @param lower The new lowest value for a key in the map. */ private void setLowerLimit(Integer lower) { COSArray arr = (COSArray) node.getDictionaryObject(COSName.LIMITS); if (arr == null) { arr = new COSArray(); arr.add(null); arr.add(null); node.setItem(COSName.LIMITS, arr); } if (lower != null) { arr.set(0, COSInteger.get(lower)); } else { arr.set(0, null); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDObjectStream.java000066400000000000000000000072501320103431700272610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.PDDocument; /** * A PDStream represents a stream in a PDF document. Streams are tied to a single * PDF document. * * @author Ben Litchfield */ public class PDObjectStream extends PDStream { /** * Constructor. * * @param str The stream parameter. */ public PDObjectStream( COSStream str ) { super( str ); } /** * This will create a new PDObjectStream object. * * @param document The document that the stream will be part of. * @return A new stream object. */ public static PDObjectStream createStream( PDDocument document ) { COSStream cosStream = new COSStream(); PDObjectStream strm = new PDObjectStream( cosStream ); strm.getCOSObject().setItem(COSName.TYPE, COSName.OBJ_STM); return strm; } /** * Get the type of this object, should always return "ObjStm". * * @return The type of this object. */ public String getType() { return getCOSObject().getNameAsString(COSName.TYPE); } /** * Get the number of compressed object. * * @return The number of compressed objects. */ public int getNumberOfObjects() { return getCOSObject().getInt(COSName.N, 0); } /** * Set the number of objects. * * @param n The new number of objects. */ public void setNumberOfObjects( int n ) { getCOSObject().setInt(COSName.N, n); } /** * The byte offset (in the decoded stream) of the first compressed object. * * @return The byte offset to the first object. */ public int getFirstByteOffset() { return getCOSObject().getInt(COSName.FIRST, 0); } /** * The byte offset (in the decoded stream) of the first compressed object. * * @param n The byte offset to the first object. */ public void setFirstByteOffset( int n ) { getCOSObject().setInt(COSName.FIRST, n); } /** * A reference to an object stream, of which the current object stream is * considered an extension. * * @return The object that this stream is an extension. */ public PDObjectStream getExtends() { PDObjectStream retval = null; COSStream stream = (COSStream) getCOSObject().getDictionaryObject(COSName.EXTENDS); if( stream != null ) { retval = new PDObjectStream( stream ); } return retval; } /** * A reference to an object stream, of which the current object stream is * considered an extension. * * @param stream The object stream extension. */ public void setExtends( PDObjectStream stream ) { getCOSObject().setItem(COSName.EXTENDS, stream); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDPageLabelRange.java000066400000000000000000000115731320103431700274730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * Contains information for a page label range. * * @author Igor Podolskiy * * @see PDPageLabels * */ public class PDPageLabelRange implements COSObjectable { private COSDictionary root; // Page label dictonary (PDF32000-1:2008 Section 12.4.2, Table 159) private static final COSName KEY_START = COSName.ST; private static final COSName KEY_PREFIX = COSName.P; private static final COSName KEY_STYLE = COSName.S; // Style entry values (PDF32000-1:2008 Section 12.4.2, Table 159) /** * Decimal page numbering style (1, 2, 3, ...). */ public static final String STYLE_DECIMAL = "D"; /** * Roman numbers (upper case) numbering style (I, II, III, IV, ...). */ public static final String STYLE_ROMAN_UPPER = "R"; /** * Roman numbers (lower case) numbering style (i, ii, iii, iv, ...). */ public static final String STYLE_ROMAN_LOWER = "r"; /** * Letter (upper case) numbering style (A, B, ..., Z, AA, BB, ..., ZZ, AAA, * ...). */ public static final String STYLE_LETTERS_UPPER = "A"; /** * Letter (lower case) numbering style (a, b, ..., z, aa, bb, ..., zz, aaa, * ...). */ public static final String STYLE_LETTERS_LOWER = "a"; /** * Creates a new empty page label range object. */ public PDPageLabelRange() { this(new COSDictionary()); } /** * Creates a new page label range object from the given dictionary. * * @param dict * the base dictionary for the new object. */ public PDPageLabelRange(COSDictionary dict) { root = dict; } /** * Returns the underlying dictionary. * * @return the underlying dictionary. */ @Override public COSDictionary getCOSObject() { return root; } /** * Returns the numbering style for this page range. * * @return one of the STYLE_* constants */ public String getStyle() { return root.getNameAsString(KEY_STYLE); } /** * Sets the numbering style for this page range. * * @param style * one of the STYLE_* constants or {@code null} if no page * numbering is desired. */ public void setStyle(String style) { if (style != null) { root.setName(KEY_STYLE, style); } else { root.removeItem(KEY_STYLE); } } /** * Returns the start value for page numbering in this page range. * * @return a positive integer the start value for numbering. */ public int getStart() { return root.getInt(KEY_START, 1); } /** * Sets the start value for page numbering in this page range. * * @param start * a positive integer representing the start value. * @throws IllegalArgumentException * if {@code start} is not a positive integer */ public void setStart(int start) { if (start <= 0) { throw new IllegalArgumentException( "The page numbering start value must be a positive integer"); } root.setInt(KEY_START, start); } /** * Returns the page label prefix for this page range. * * @return the page label prefix for this page range, or {@code null} if no * prefix has been defined. */ public String getPrefix() { return root.getString(KEY_PREFIX); } /** * Sets the page label prefix for this page range. * * @param prefix * the page label prefix for this page range, or {@code null} to * unset the prefix. */ public void setPrefix(String prefix) { if (prefix != null) { root.setString(KEY_PREFIX, prefix); } else { root.removeItem(KEY_PREFIX); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDPageLabels.java000066400000000000000000000124621320103431700266770ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import static java.util.Objects.nonNull; import static org.sejda.util.RequireUtils.requireArg; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.PDDocumentCatalog; /** * Represents the page label dictionary of a document. * * @author Igor Podolskiy */ public class PDPageLabels implements COSObjectable { private Map labels = new TreeMap<>(); /** * Creates an empty page label dictionary for the given document. * *

* Note that the page label dictionary won't be automatically added to the document; you will still need to do it * manually (see {@link PDDocumentCatalog#setPageLabels(PDPageLabels)}. *

* * @see PDDocumentCatalog#setPageLabels(PDPageLabels) */ public PDPageLabels() { PDPageLabelRange defaultRange = new PDPageLabelRange(); defaultRange.setStyle(PDPageLabelRange.STYLE_DECIMAL); labels.put(0, defaultRange); } /** * Creates an page label dictionary for a document using the information in the given COS dictionary. * *

* Note that the page label dictionary won't be automatically added to the document; you will still need to do it * manually (see {@link PDDocumentCatalog#setPageLabels(PDPageLabels)}. *

* * @param dict an existing page label dictionary * @see PDDocumentCatalog#setPageLabels(PDPageLabels) * @throws IOException If something goes wrong during the number tree conversion. */ public PDPageLabels(COSDictionary dict) throws IOException { if (nonNull(dict)) { findLabels(new PDNumberTreeNode(dict, COSDictionary.class)); } } private void findLabels(PDNumberTreeNode node) throws IOException { if (node.getKids() != null) { List kids = node.getKids(); for (PDNumberTreeNode kid : kids) { findLabels(kid); } } else if (node.getNumbers() != null) { Map numbers = node.getNumbers(); for (Entry i : numbers.entrySet()) { if (i.getKey() >= 0) { labels.put(i.getKey(), new PDPageLabelRange((COSDictionary) i.getValue())); } } } } /** * Returns the number of page label ranges. * *

* This will be always >= 1, as the required default entry for the page range starting at the first page is added * automatically by this implementation (see PDF32000-1:2008, p. 375). *

* * @return the number of page label ranges. */ public int getPageRangeCount() { return labels.size(); } /** * Returns the page label range starting at the given page, or {@code null} if no such range is defined. * * @param startPage the 0-based page index representing the start page of the page range the item is defined for. * @return the page label range or {@code null} if no label range is defined for the given start page. */ public PDPageLabelRange getPageLabelRange(int startPage) { return labels.get(startPage); } /** * Sets the page label range beginning at the specified start page. * * @param startPage the 0-based index of the page representing the start of the page label range. * @param item the page label item to set. * @throws IllegalArgumentException if the startPage parameter is < 0. */ public void setLabelItem(int startPage, PDPageLabelRange item) { requireArg(startPage >= 0, "Cannot set a label starting from a negative page number"); labels.put(startPage, item); } @Override public COSBase getCOSObject() { COSDictionary dict = new COSDictionary(); COSArray arr = new COSArray(); for (Entry i : labels.entrySet()) { arr.add(COSInteger.get(i.getKey())); arr.add(i.getValue()); } dict.setItem(COSName.NUMS, arr); return dict; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDRange.java000066400000000000000000000075141320103431700257360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; /** * This class will be used to signify a range. a(min) <= a* <= a(max) * * @author Ben Litchfield */ public class PDRange implements COSObjectable { private COSArray rangeArray; private int startingIndex; /** * Constructor with an initial range of 0..1. */ public PDRange() { rangeArray = new COSArray(); rangeArray.add( new COSFloat( 0.0f ) ); rangeArray.add( new COSFloat( 1.0f ) ); startingIndex = 0; } /** * Constructor assumes a starting index of 0. * * @param range The array that describes the range. */ public PDRange( COSArray range ) { rangeArray = range; } /** * Constructor with an index into an array. Because some arrays specify * multiple ranges ie [ 0,1, 0,2, 2,3 ] It is convenient for this * class to take an index into an array. So if you want this range to * represent 0,2 in the above example then you would say new PDRange( array, 1 ). * * @param range The array that describes the index * @param index The range index into the array for the start of the range. */ public PDRange( COSArray range, int index ) { rangeArray = range; startingIndex = index; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSBase getCOSObject() { return rangeArray; } /** * This will get the underlying array value. * * @return The cos object that this object wraps. */ public COSArray getCOSArray() { return rangeArray; } /** * This will get the minimum value of the range. * * @return The min value. */ public float getMin() { COSNumber min = (COSNumber)rangeArray.getObject( startingIndex*2 ); return min.floatValue(); } /** * This will set the minimum value for the range. * * @param min The new minimum for the range. */ public void setMin( float min ) { rangeArray.set( startingIndex*2, new COSFloat( min ) ); } /** * This will get the maximum value of the range. * * @return The max value. */ public float getMax() { COSNumber max = (COSNumber)rangeArray.getObject( startingIndex*2+1 ); return max.floatValue(); } /** * This will set the maximum value for the range. * * @param max The new maximum for the range. */ public void setMax( float max ) { rangeArray.set( startingIndex*2+1, new COSFloat( max ) ); } /** * {@inheritDoc} */ @Override public String toString() { return "PDRange{" + getMin() + ", " + getMax() + '}'; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDRectangle.java000066400000000000000000000262761320103431700266140ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import static java.util.Objects.nonNull; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.util.Matrix; /** * A rectangle in a PDF document. * * @author Ben Litchfield */ public class PDRectangle implements COSObjectable { /** user space units per inch */ private static final float POINTS_PER_INCH = 72; /** user space units per millimeter */ private static final float POINTS_PER_MM = 1 / (10 * 2.54f) * POINTS_PER_INCH; /** A rectangle the size of U.S. Letter, 8.5" x 11". */ public static final PDRectangle LETTER = new PDRectangle(8.5f * POINTS_PER_INCH, 11f * POINTS_PER_INCH); /** A rectangle the size of U.S. Legal, 8.5" x 14". */ public static final PDRectangle LEGAL = new PDRectangle(8.5f * POINTS_PER_INCH, 14f * POINTS_PER_INCH); /** A rectangle the size of A0 Paper. */ public static final PDRectangle A0 = new PDRectangle(841 * POINTS_PER_MM, 1189 * POINTS_PER_MM); /** A rectangle the size of A1 Paper. */ public static final PDRectangle A1 = new PDRectangle(594 * POINTS_PER_MM, 841 * POINTS_PER_MM); /** A rectangle the size of A2 Paper. */ public static final PDRectangle A2 = new PDRectangle(420 * POINTS_PER_MM, 594 * POINTS_PER_MM); /** A rectangle the size of A3 Paper. */ public static final PDRectangle A3 = new PDRectangle(297 * POINTS_PER_MM, 420 * POINTS_PER_MM); /** A rectangle the size of A4 Paper. */ public static final PDRectangle A4 = new PDRectangle(210 * POINTS_PER_MM, 297 * POINTS_PER_MM); /** A rectangle the size of A5 Paper. */ public static final PDRectangle A5 = new PDRectangle(148 * POINTS_PER_MM, 210 * POINTS_PER_MM); /** A rectangle the size of A6 Paper. */ public static final PDRectangle A6 = new PDRectangle(105 * POINTS_PER_MM, 148 * POINTS_PER_MM); private final COSArray rectArray = new COSArray(); /** * Initializes to 0,0,0,0 */ public PDRectangle() { this(0.0f, 0.0f, 0.0f, 0.0f); } /** * @param width The width of the rectangle. * @param height The height of the rectangle. */ public PDRectangle(float width, float height) { this(0.0f, 0.0f, width, height); } /** * @param x the x coordinate of the rectangle * @param y the y coordinate of the rectangle * @param width The width of the rectangle. * @param height The height of the rectangle. */ public PDRectangle(float x, float y, float width, float height) { rectArray.add(new COSFloat(x)); rectArray.add(new COSFloat(y)); rectArray.add(new COSFloat(x + width)); rectArray.add(new COSFloat(y + height)); } public PDRectangle(Rectangle2D rectange) { this((float) rectange.getX(), (float) rectange.getY(), (float) rectange.getWidth(), (float) rectange.getHeight()); } /** * @param box the bounding box to be used for the rectangle */ public PDRectangle(BoundingBox box) { rectArray.add(new COSFloat(box.getLowerLeftX())); rectArray.add(new COSFloat(box.getLowerLeftY())); rectArray.add(new COSFloat(box.getUpperRightX())); rectArray.add(new COSFloat(box.getUpperRightY())); } /** * @param array An array of numbers as specified in the PDF Reference for a rectangle type. */ public PDRectangle(COSArray array) { float[] values = array.trimToSize(4).toFloatArray(); // we have to start with the lower left corner rectArray.add(new COSFloat(Math.min(values[0], values[2]))); rectArray.add(new COSFloat(Math.min(values[1], values[3]))); rectArray.add(new COSFloat(Math.max(values[0], values[2]))); rectArray.add(new COSFloat(Math.max(values[1], values[3]))); } /** * Method to determine if the x/y point is inside this rectangle. * * @param x The x-coordinate to test. * @param y The y-coordinate to test. * @return True if the point is inside this rectangle. */ public boolean contains(float x, float y) { float llx = getLowerLeftX(); float urx = getUpperRightX(); float lly = getLowerLeftY(); float ury = getUpperRightY(); return x >= llx && x <= urx && y >= lly && y <= ury; } /** * This will create a translated rectangle based off of this rectangle, such that the new rectangle retains the same * dimensions(height/width), but the lower left x,y values are zero.
* 100, 100, 400, 400 (llx, lly, urx, ury )
* will be translated to 0,0,300,300 * * @return A new rectangle that has been translated back to the origin. */ public PDRectangle createRetranslatedRectangle() { PDRectangle retval = new PDRectangle(); retval.setUpperRightX(getWidth()); retval.setUpperRightY(getHeight()); return retval; } /** * This will get the lower left x coordinate. * * @return The lower left x. */ public float getLowerLeftX() { return ((COSNumber) rectArray.get(0)).floatValue(); } /** * This will set the lower left x coordinate. * * @param value The lower left x. */ public void setLowerLeftX(float value) { rectArray.set(0, new COSFloat(value)); } /** * This will get the lower left y coordinate. * * @return The lower left y. */ public float getLowerLeftY() { return ((COSNumber) rectArray.get(1)).floatValue(); } /** * This will set the lower left y coordinate. * * @param value The lower left y. */ public void setLowerLeftY(float value) { rectArray.set(1, new COSFloat(value)); } /** * This will get the upper right x coordinate. * * @return The upper right x . */ public float getUpperRightX() { return ((COSNumber) rectArray.get(2)).floatValue(); } /** * This will set the upper right x coordinate. * * @param value The upper right x . */ public void setUpperRightX(float value) { rectArray.set(2, new COSFloat(value)); } /** * This will get the upper right y coordinate. * * @return The upper right y. */ public float getUpperRightY() { return ((COSNumber) rectArray.get(3)).floatValue(); } /** * This will set the upper right y coordinate. * * @param value The upper right y. */ public void setUpperRightY(float value) { rectArray.set(3, new COSFloat(value)); } /** * This will get the width of this rectangle as calculated by upperRightX - lowerLeftX. * * @return The width of this rectangle. */ public float getWidth() { return getUpperRightX() - getLowerLeftX(); } /** * This will get the height of this rectangle as calculated by upperRightY - lowerLeftY. * * @return The height of this rectangle. */ public float getHeight() { return getUpperRightY() - getLowerLeftY(); } /** * Returns a path which represents this rectangle having been transformed by the given matrix. Note that the * resulting path need not be rectangular. */ public GeneralPath transform(Matrix matrix) { float x1 = getLowerLeftX(); float y1 = getLowerLeftY(); float x2 = getUpperRightX(); float y2 = getUpperRightY(); Point2D.Float p0 = matrix.transformPoint(x1, y1); Point2D.Float p1 = matrix.transformPoint(x2, y1); Point2D.Float p2 = matrix.transformPoint(x2, y2); Point2D.Float p3 = matrix.transformPoint(x1, y2); GeneralPath path = new GeneralPath(); path.moveTo(p0.getX(), p0.getY()); path.lineTo(p1.getX(), p1.getY()); path.lineTo(p2.getX(), p2.getY()); path.lineTo(p3.getX(), p3.getY()); path.closePath(); return path; } @Override public COSArray getCOSObject() { return rectArray; } /** * Returns a general path equivalent to this rectangle. This method avoids the problems caused by Rectangle2D not * working well with -ve rectangles. */ public GeneralPath toGeneralPath() { float x1 = getLowerLeftX(); float y1 = getLowerLeftY(); float x2 = getUpperRightX(); float y2 = getUpperRightY(); GeneralPath path = new GeneralPath(); path.moveTo(x1, y1); path.lineTo(x2, y1); path.lineTo(x2, y2); path.lineTo(x1, y2); path.closePath(); return path; } /** * @return a new rectangle at the same coordinates but rotated clockwise by 90 degrees */ public PDRectangle rotate() { return new PDRectangle(getLowerLeftX(), getLowerLeftY(), getHeight(), getWidth()); } /** * @return a new rectangle at the same coordinates but rotated clockwise by in degrees */ public PDRectangle rotate(int degrees) { PDRectangle ret = this; for (int i = 0; i < Math.abs((degrees % 360) / 90); i++) { ret = ret.rotate(); } return ret; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof PDRectangle)) { return false; } return rectArray.equals(((PDRectangle) o).rectArray); } @Override public int hashCode() { return rectArray.hashCode(); } /** * This will return a string representation of this rectangle. * * @return This object as a string. */ @Override public String toString() { return "[" + getLowerLeftX() + "," + getLowerLeftY() + "," + getUpperRightX() + "," + getUpperRightY() + "]"; } /** * @param array * @return a {@link PDRectangle} if the the array is not null and has enough elements, null otherwise */ public static PDRectangle rectangleFrom(COSArray array) { if (nonNull(array) && array.size() >= 4) { return new PDRectangle(array); } return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDStream.java000066400000000000000000000325771320103431700261440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.filespecification.FileSpecifications; import org.sejda.sambox.pdmodel.common.filespecification.PDFileSpecification; import org.sejda.util.IOUtils; /** * A PDStream represents a stream in a PDF document. Streams are tied to a single PDF document. * * @author Ben Litchfield */ public class PDStream implements COSObjectable { private COSStream stream; /** * This will create a new PDStream object. */ public PDStream() { stream = new COSStream(); } /** * Constructor. * * @param str The stream parameter. */ public PDStream(COSStream str) { stream = str; } /** * Constructor. Reads all data from the input stream and embeds it into the document, this will close the * InputStream. * * @param input The stream parameter. * @throws IOException If there is an error creating the stream in the document. */ public PDStream(InputStream input) throws IOException { this(input, (COSBase) null); } /** * Constructor. Reads all data from the input stream and embeds it into the document with the given filter applied. * This method closes the InputStream. * * @param input The stream parameter. * @param filter Filter to apply to the stream. * @throws IOException If there is an error creating the stream in the document. */ public PDStream(InputStream input, COSName filter) throws IOException { this(input, (COSBase) filter); } /** * Constructor. Reads all data from the input stream and embeds it into the document with the given filters applied. * This method closes the InputStream. * * @param input The stream parameter. * @param filters Filters to apply to the stream. * @throws IOException If there is an error creating the stream in the document. */ public PDStream(InputStream input, COSArray filters) throws IOException { this(input, (COSBase) filters); } /** * Constructor. Reads all data from the input stream and embeds it into the document, this will close the * InputStream. * * @param input The stream parameter. * @param filter Filter to apply to the stream. * @throws IOException If there is an error creating the stream in the document. */ public PDStream(InputStream input, COSBase filter) throws IOException { stream = new COSStream(); try (OutputStream output = stream.createFilteredStream(filter)) { org.apache.commons.io.IOUtils.copy(input, output); } finally { IOUtils.close(input); } } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSStream getCOSObject() { return stream; } /** * This will get a stream that can be written to. * * @return An output stream to write data to. * * @throws IOException If an IO error occurs during writing. */ public OutputStream createOutputStream() { return stream.createUnfilteredStream(); } /** * This will get a stream that can be written to, with the given filter. * * @param filter the filter to be used. * @return An output stream to write data to. * @throws IOException If an IO error occurs during writing. */ public OutputStream createOutputStream(COSName filter) { return stream.createFilteredStream(filter); } /** * This will get a stream that can be read from. * * @return An input stream that can be read from. * * @throws IOException If an IO error occurs during reading. */ public InputStream createInputStream() throws IOException { return stream.getUnfilteredStream(); } /** * This will get the length of the filtered/compressed stream. This is readonly in the PD Model and will be managed * by this class. * * @return The length of the filtered stream. */ public int getLength() { return stream.getInt(COSName.LENGTH, 0); } /** * This will get the list of filters that are associated with this stream. Or null if there are none. * * @return A list of all encoding filters to apply to this stream. */ public List getFilters() { COSBase filters = stream.getFilters(); if (filters instanceof COSName) { COSName name = (COSName) filters; return new COSArrayList<>(name, name, stream, COSName.FILTER); } else if (filters instanceof COSArray) { return (List) ((COSArray) filters).toList(); } return null; } /** * This will set the filters that are part of this stream. * * @param filters The filters that are part of this stream. */ public void setFilters(List filters) { COSBase obj = COSArrayList.converterToCOSArray(filters); stream.setItem(COSName.FILTER, obj); } /** * Get the list of decode parameters. Each entry in the list will refer to an entry in the filters list. * * @return The list of decode parameters. * * @throws IOException if there is an error retrieving the parameters. */ public List getDecodeParms() throws IOException { List retval = null; COSBase dp = stream.getDictionaryObject(COSName.DECODE_PARMS); if (dp == null) { // See PDF Ref 1.5 implementation note 7, the DP is sometimes used // instead. dp = stream.getDictionaryObject(COSName.DP); } if (dp instanceof COSDictionary) { Map map = COSDictionaryMap.convertBasicTypesToMap((COSDictionary) dp); retval = new COSArrayList(map, dp, stream, COSName.DECODE_PARMS); } else if (dp instanceof COSArray) { COSArray array = (COSArray) dp; List actuals = new ArrayList(); for (int i = 0; i < array.size(); i++) { actuals.add(COSDictionaryMap .convertBasicTypesToMap((COSDictionary) array.getObject(i))); } retval = new COSArrayList(actuals, array); } return retval; } /** * This will set the list of decode parameterss. * * @param decodeParams The list of decode parameterss. */ public void setDecodeParms(List decodeParams) { stream.setItem(COSName.DECODE_PARMS, COSArrayList.converterToCOSArray(decodeParams)); } /** * This will get the file specification for this stream. This is only required for external files. * * @return The file specification. */ public PDFileSpecification getFile() { return FileSpecifications.fileSpecificationFor(stream.getDictionaryObject(COSName.F)); } /** * Set the file specification. * * @param f The file specification. */ public void setFile(PDFileSpecification f) { stream.setItem(COSName.F, f); } /** * This will get the list of filters that are associated with this stream. Or null if there are none. * * @return A list of all encoding filters to apply to this stream. */ public List getFileFilters() { List retval = null; COSBase filters = stream.getDictionaryObject(COSName.F_FILTER); if (filters instanceof COSName) { COSName name = (COSName) filters; retval = new COSArrayList(name.getName(), name, stream, COSName.F_FILTER); } else if (filters instanceof COSArray) { retval = COSArrayList.convertCOSNameCOSArrayToList((COSArray) filters); } return retval; } /** * This will set the filters that are part of this stream. * * @param filters The filters that are part of this stream. */ public void setFileFilters(List filters) { COSBase obj = COSArrayList.convertStringListToCOSNameCOSArray(filters); stream.setItem(COSName.F_FILTER, obj); } /** * Get the list of decode parameters. Each entry in the list will refer to an entry in the filters list. * * @return The list of decode parameters. * * @throws IOException if there is an error retrieving the parameters. */ public List getFileDecodeParams() throws IOException { List retval = null; COSBase dp = stream.getDictionaryObject(COSName.F_DECODE_PARMS); if (dp instanceof COSDictionary) { Map map = COSDictionaryMap.convertBasicTypesToMap((COSDictionary) dp); retval = new COSArrayList(map, dp, stream, COSName.F_DECODE_PARMS); } else if (dp instanceof COSArray) { COSArray array = (COSArray) dp; List actuals = new ArrayList(); for (int i = 0; i < array.size(); i++) { actuals.add(COSDictionaryMap .convertBasicTypesToMap((COSDictionary) array.getObject(i))); } retval = new COSArrayList(actuals, array); } return retval; } /** * This will set the list of decode params. * * @param decodeParams The list of decode params. */ public void setFileDecodeParams(List decodeParams) { stream.setItem("FDecodeParams", COSArrayList.converterToCOSArray(decodeParams)); } /** * This will copy the stream into a byte array. * * @return The byte array of the filteredStream * @throws IOException When getFilteredStream did not work */ public byte[] toByteArray() throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); try (InputStream is = createInputStream()) { org.apache.commons.io.IOUtils.copy(is, output); } return output.toByteArray(); } /** * Get the metadata that is part of the document catalog. This will return null if there is no meta data for this * object. * * @return The metadata for this object. * @throws IllegalStateException if the value of the metadata entry is different from a stream or null */ public PDMetadata getMetadata() { PDMetadata retval = null; COSBase mdStream = stream.getDictionaryObject(COSName.METADATA); if (mdStream != null) { if (mdStream instanceof COSStream) { retval = new PDMetadata((COSStream) mdStream); } else if (mdStream instanceof COSNull) { // null is authorized } else { throw new IllegalStateException( "Expected a COSStream but was a " + mdStream.getClass().getSimpleName()); } } return retval; } /** * Set the metadata for this object. This can be null. * * @param meta The meta data for this object. */ public void setMetadata(PDMetadata meta) { stream.setItem(COSName.METADATA, meta); } /** * Get the decoded stream length. * * @since Apache PDFBox 1.1.0 * @see PDFBOX-636 * @return the decoded stream length */ public int getDecodedStreamLength() { return this.stream.getInt(COSName.DL); } /** * Set the decoded stream length. * * @since Apache PDFBox 1.1.0 * @see PDFBOX-636 * @param decodedStreamLength the decoded stream length */ public void setDecodedStreamLength(int decodedStreamLength) { this.stream.setInt(COSName.DL, decodedStreamLength); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/PDTypedDictionaryWrapper.java000066400000000000000000000035231320103431700313520ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * A wrapper for a COS dictionary including Type information. * * @author Johannes Koch * */ public class PDTypedDictionaryWrapper extends PDDictionaryWrapper { /** * Creates a new instance with a given type. * * @param type the type (Type) */ public PDTypedDictionaryWrapper(String type) { super(); this.getCOSObject().setName(COSName.TYPE, type); } /** * Creates a new instance with a given COS dictionary. * * @param dictionary the dictionary */ public PDTypedDictionaryWrapper(COSDictionary dictionary) { super(dictionary); } /** * Gets the type. * * @return the type */ public String getType() { return this.getCOSObject().getNameAsString(COSName.TYPE); } // There is no setType(String) method because changing the Type would most // probably also change the type of PD object. } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/filespecification/000077500000000000000000000000001320103431700272645ustar00rootroot00000000000000FileSpecifications.java000066400000000000000000000041721320103431700336170ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/filespecification/* * 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. */ package org.sejda.sambox.pdmodel.common.filespecification; import static java.util.Objects.nonNull; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Factory methods for {@link PDFileSpecification} * * @author Andrea Vacondio * */ public final class FileSpecifications { private static final Logger LOG = LoggerFactory.getLogger(PDFileSpecification.class); private FileSpecifications() { // hide } /** * Factory method for a a file specification that can be either a COSString or a COSDictionary. * * @param base The cos object that describes the fs. * @return The file specification for the COSBase object or null in case of invalid input. */ public static PDFileSpecification fileSpecificationFor(COSBase base) { if (nonNull(base)) { if (base instanceof COSString) { return new PDSimpleFileSpecification((COSString) base); } if (base instanceof COSDictionary) { return new PDComplexFileSpecification((COSDictionary) base); } LOG.warn("Invalid file specification type {}", base.getClass()); } return null; } } PDComplexFileSpecification.java000066400000000000000000000252571320103431700352170ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/filespecification/* * 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. */ package org.sejda.sambox.pdmodel.common.filespecification; import static java.util.Optional.ofNullable; import java.util.Optional; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; /** * This represents a file specification. * * @author Ben Litchfield * */ public class PDComplexFileSpecification implements PDFileSpecification { private final COSDictionary fileSpecificationDictionary; private COSDictionary embeddedFileDictionary; /** * @param dict The file specification dictionary. */ public PDComplexFileSpecification(COSDictionary dict) { this.fileSpecificationDictionary = Optional.ofNullable(dict).orElseGet(COSDictionary::new); this.fileSpecificationDictionary.setItem(COSName.TYPE, COSName.FILESPEC); } @Override public COSDictionary getCOSObject() { return fileSpecificationDictionary; } private COSDictionary getEFDictionary() { if (embeddedFileDictionary == null && fileSpecificationDictionary != null) { embeddedFileDictionary = (COSDictionary) fileSpecificationDictionary .getDictionaryObject(COSName.EF); } return embeddedFileDictionary; } private COSBase getObjectFromEFDictionary(COSName key) { COSDictionary ef = getEFDictionary(); if (ef != null) { return ef.getDictionaryObject(key); } return null; } /** *

* Preferred method for getting the filename. It will determinate the recommended file name. *

*

* First of all we try to get the unicode filename if it exist. If it doesn't exist we take a look at the DOS, MAC * UNIX filenames. If no one exist the required F entry will be returned. *

* * @return The preferred file name. */ public String getFilename() { String filename = getFileUnicode(); if (filename == null) { filename = getFileDos(); } if (filename == null) { filename = getFileMac(); } if (filename == null) { filename = getFileUnix(); } if (filename == null) { filename = getFile(); } return filename; } /** * This will get the unicode file name. * * @return The file name. */ public String getFileUnicode() { return fileSpecificationDictionary.getString(COSName.UF); } /** * This will set unicode file name. * * @param file The name of the file. */ public void setFileUnicode(String file) { fileSpecificationDictionary.setString(COSName.UF, file); } @Override public String getFile() { return fileSpecificationDictionary.getString(COSName.F); } @Override public void setFile(String file) { fileSpecificationDictionary.setString(COSName.F, file); } /** * This will get the name representing a Dos file. * * @return The file name. */ public String getFileDos() { return fileSpecificationDictionary.getString(COSName.DOS); } /** * This will set name representing a dos file. * * @param file The name of the file. */ public void setFileDos(String file) { fileSpecificationDictionary.setString(COSName.DOS, file); } /** * This will get the name representing a Mac file. * * @return The file name. */ public String getFileMac() { return fileSpecificationDictionary.getString(COSName.MAC); } /** * This will set name representing a Mac file. * * @param file The name of the file. */ public void setFileMac(String file) { fileSpecificationDictionary.setString(COSName.MAC, file); } /** * This will get the name representing a Unix file. * * @return The file name. */ public String getFileUnix() { return fileSpecificationDictionary.getString(COSName.UNIX); } /** * This will set name representing a Unix file. * * @param file The name of the file. */ public void setFileUnix(String file) { fileSpecificationDictionary.setString(COSName.UNIX, file); } /** * Tell if the underlying file is volatile and should not be cached by the reader application. Default: false * * @param fileIsVolatile The new value for the volatility of the file. */ public void setVolatile(boolean fileIsVolatile) { fileSpecificationDictionary.setBoolean(COSName.V, fileIsVolatile); } /** * Get if the file is volatile. Default: false * * @return True if the file is volatile attribute is set. */ public boolean isVolatile() { return fileSpecificationDictionary.getBoolean(COSName.V, false); } /** * Get the embedded file. * * @return The embedded file for this file spec. */ public PDEmbeddedFile getEmbeddedFile() { return embeddedFileFor(COSName.F); } /** * Set the embedded file for this spec. * * @param file The file to be embedded. */ public void setEmbeddedFile(PDEmbeddedFile file) { COSDictionary ef = getEFDictionary(); if (ef == null && file != null) { ef = new COSDictionary(); fileSpecificationDictionary.setItem(COSName.EF, ef); } if (ef != null) { ef.setItem(COSName.F, file); } } /** * Get the embedded dos file. * * @return The embedded dos file for this file spec. */ public PDEmbeddedFile getEmbeddedFileDos() { return embeddedFileFor(COSName.DOS); } /** * Set the embedded dos file for this spec. * * @param file The dos file to be embedded. */ public void setEmbeddedFileDos(PDEmbeddedFile file) { COSDictionary ef = getEFDictionary(); if (ef == null && file != null) { ef = new COSDictionary(); fileSpecificationDictionary.setItem(COSName.EF, ef); } if (ef != null) { ef.setItem(COSName.DOS, file); } } /** * Get the embedded Mac file. * * @return The embedded Mac file for this file spec. */ public PDEmbeddedFile getEmbeddedFileMac() { return embeddedFileFor(COSName.MAC); } /** * Set the embedded Mac file for this spec. * * @param file The Mac file to be embedded. */ public void setEmbeddedFileMac(PDEmbeddedFile file) { COSDictionary ef = getEFDictionary(); if (ef == null && file != null) { ef = new COSDictionary(); fileSpecificationDictionary.setItem(COSName.EF, ef); } if (ef != null) { ef.setItem(COSName.MAC, file); } } /** * Get the embedded Unix file. * * @return The embedded file for this file spec. */ public PDEmbeddedFile getEmbeddedFileUnix() { return embeddedFileFor(COSName.UNIX); } /** * Set the embedded Unix file for this spec. * * @param file The Unix file to be embedded. */ public void setEmbeddedFileUnix(PDEmbeddedFile file) { COSDictionary ef = getEFDictionary(); if (ef == null && file != null) { ef = new COSDictionary(); fileSpecificationDictionary.setItem(COSName.EF, ef); } if (ef != null) { ef.setItem(COSName.UNIX, file); } } /** * Get the embedded unicode file. * * @return The embedded unicode file for this file spec. */ public PDEmbeddedFile getEmbeddedFileUnicode() { return embeddedFileFor(COSName.UF); } private PDEmbeddedFile embeddedFileFor(COSName key) { return ofNullable(getObjectFromEFDictionary(key)).filter(s -> s instanceof COSStream) .map(s -> (COSStream) s).map(PDEmbeddedFile::new).orElse(null); } /** * Set the embedded Unicode file for this spec. * * @param file The Unicode file to be embedded. */ public void setEmbeddedFileUnicode(PDEmbeddedFile file) { COSDictionary ef = getEFDictionary(); if (ef == null && file != null) { ef = new COSDictionary(); fileSpecificationDictionary.setItem(COSName.EF, ef); } if (ef != null) { ef.setItem(COSName.UF, file); } } /** * * @return the first/best available alternative of the embedded file */ public PDEmbeddedFile getBestEmbeddedFile() { return ofNullable(getEmbeddedFileUnicode()).orElseGet(() -> { return ofNullable(getEmbeddedFileDos()).orElseGet(() -> { return ofNullable(getEmbeddedFileMac()).orElseGet(() -> { return ofNullable(getEmbeddedFileUnix()).orElseGet(() -> { return getEmbeddedFile(); }); }); }); }); } /** * Set the file description. * * @param description The file description */ public void setFileDescription(String description) { fileSpecificationDictionary.setString(COSName.DESC, description); } /** * This will get the description. * * @return The file description. */ public String getFileDescription() { return fileSpecificationDictionary.getString(COSName.DESC); } /** * Set the collection item dictionary * * @param the collection item dictionary */ public void setCollectionItem(COSDictionary dictionary) { fileSpecificationDictionary.setItem(COSName.CI, dictionary); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/filespecification/PDEmbeddedFile.java000066400000000000000000000157721320103431700326600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.filespecification; import java.io.IOException; import java.io.InputStream; import java.util.Calendar; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDStream; /** * This represents an embedded file in a file specification. * * @author Ben Litchfield */ public class PDEmbeddedFile extends PDStream { /** * Constructor. * * @param str The stream parameter. */ public PDEmbeddedFile(COSStream str) { super(str); } /** * Constructor. * * @param str {@inheritDoc} * * @throws IOException {@inheritDoc} */ public PDEmbeddedFile(InputStream str) throws IOException { super(str); getCOSObject().setItem(COSName.TYPE, COSName.EMBEDDED_FILE); } public PDEmbeddedFile(InputStream input, COSName filter) throws IOException { super(input, filter); getCOSObject().setItem(COSName.TYPE, COSName.EMBEDDED_FILE); } /** * Set the subtype for this embedded file. This should be a mime type value. Optional. * * @param mimeType The mimeType for the file. */ public void setSubtype(String mimeType) { getCOSObject().setName(COSName.SUBTYPE, mimeType); } /** * Get the subtype(mimetype) for the embedded file. * * @return The type of embedded file. */ public String getSubtype() { return getCOSObject().getNameAsString(COSName.SUBTYPE); } /** * Get the size of the embedded file. * * @return The size of the embedded file. */ public int getSize() { return getCOSObject().getEmbeddedInt("Params", "Size"); } /** * Set the size of the embedded file. * * @param size The size of the embedded file. */ public void setSize(long size) { getCOSObject().setEmbeddedInt("Params", "Size", size); } /** * Get the creation date of the embedded file. * * @return The Creation date. * @throws IOException If there is an error while constructing the date. */ public Calendar getCreationDate() { return getCOSObject().getEmbeddedDate("Params", "CreationDate"); } /** * Set the creation date. * * @param creation The new creation date. */ public void setCreationDate(Calendar creation) { getCOSObject().setEmbeddedDate("Params", "CreationDate", creation); } /** * Get the mod date of the embedded file. * * @return The mod date. * @throws IOException If there is an error while constructing the date. */ public Calendar getModDate() { return getCOSObject().getEmbeddedDate("Params", "ModDate"); } /** * Set the mod date. * * @param mod The new creation mod. */ public void setModDate(Calendar mod) { getCOSObject().setEmbeddedDate("Params", "ModDate", mod); } /** * Get the check sum of the embedded file. * * @return The check sum of the file. */ public String getCheckSum() { return getCOSObject().getEmbeddedString("Params", "CheckSum"); } /** * Set the check sum. * * @param checksum The checksum of the file. */ public void setCheckSum(String checksum) { getCOSObject().setEmbeddedString("Params", "CheckSum", checksum); } /** * Get the mac subtype. * * @return The mac subtype. */ public String getMacSubtype() { String retval = null; COSDictionary params = (COSDictionary) getCOSObject().getDictionaryObject(COSName.PARAMS); if (params != null) { retval = params.getEmbeddedString("Mac", "Subtype"); } return retval; } /** * Set the mac subtype. * * @param macSubtype The mac subtype. */ public void setMacSubtype(String macSubtype) { COSDictionary params = (COSDictionary) getCOSObject().getDictionaryObject(COSName.PARAMS); if (params == null && macSubtype != null) { params = new COSDictionary(); getCOSObject().setItem(COSName.PARAMS, params); } if (params != null) { params.setEmbeddedString("Mac", "Subtype", macSubtype); } } /** * Get the mac Creator. * * @return The mac Creator. */ public String getMacCreator() { String retval = null; COSDictionary params = (COSDictionary) getCOSObject().getDictionaryObject(COSName.PARAMS); if (params != null) { retval = params.getEmbeddedString("Mac", "Creator"); } return retval; } /** * Set the mac Creator. * * @param macCreator The mac Creator. */ public void setMacCreator(String macCreator) { COSDictionary params = (COSDictionary) getCOSObject().getDictionaryObject(COSName.PARAMS); if (params == null && macCreator != null) { params = new COSDictionary(); getCOSObject().setItem(COSName.PARAMS, params); } if (params != null) { params.setEmbeddedString("Mac", "Creator", macCreator); } } /** * Get the mac ResFork. * * @return The mac ResFork. */ public String getMacResFork() { String retval = null; COSDictionary params = (COSDictionary) getCOSObject().getDictionaryObject(COSName.PARAMS); if (params != null) { retval = params.getEmbeddedString("Mac", "ResFork"); } return retval; } /** * Set the mac ResFork. * * @param macResFork The mac ResFork. */ public void setMacResFork(String macResFork) { COSDictionary params = (COSDictionary) getCOSObject().getDictionaryObject(COSName.PARAMS); if (params == null && macResFork != null) { params = new COSDictionary(); getCOSObject().setItem(COSName.PARAMS, params); } if (params != null) { params.setEmbeddedString("Mac", "ResFork", macResFork); } } } PDFileSpecification.java000066400000000000000000000022661320103431700336620ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/filespecification/* * 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. */ package org.sejda.sambox.pdmodel.common.filespecification; import org.sejda.sambox.cos.COSObjectable; /** * This represents a file specification. * * @author Ben Litchfield */ public interface PDFileSpecification extends COSObjectable { /** * @return The file name. */ String getFile(); /** * @param file The name of the file. */ void setFile(String file); } PDSimpleFileSpecification.java000066400000000000000000000034701320103431700350320ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/filespecification/* * 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. */ package org.sejda.sambox.pdmodel.common.filespecification; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSString; /** * A file specification that is just a string. * * @author Ben Litchfield */ public class PDSimpleFileSpecification implements PDFileSpecification { private COSString file; public PDSimpleFileSpecification() { file = COSString.parseLiteral(""); } /** * Constructor. * * @param fileName The file that this spec represents. */ public PDSimpleFileSpecification( COSString fileName ) { file = fileName; } @Override public String getFile() { return file.getString(); } @Override public void setFile( String fileName ) { file = COSString.parseLiteral(fileName); } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSBase getCOSObject() { return file; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/000077500000000000000000000000001320103431700254315ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/PDFunction.java000066400000000000000000000245311320103431700303120ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDRange; import org.sejda.sambox.pdmodel.common.PDStream; /** * This class represents a function in a PDF document. * * @author Ben Litchfield * */ public abstract class PDFunction implements COSObjectable { private PDStream functionStream = null; private COSDictionary functionDictionary = null; private COSArray domain = null; private COSArray range = null; private int numberOfInputValues = -1; private int numberOfOutputValues = -1; /** * Constructor. * * @param function The function stream. * */ public PDFunction(COSBase function) { if (function instanceof COSStream) { functionStream = new PDStream((COSStream) function); functionStream.getCOSObject().setItem(COSName.TYPE, COSName.FUNCTION); } else if (function instanceof COSDictionary) { functionDictionary = (COSDictionary) function; } } /** * Returns the function type. * * Possible values are: * * 0 - Sampled function 2 - Exponential interpolation function 3 - Stitching function 4 - PostScript calculator * function * * @return the function type. */ public abstract int getFunctionType(); /** * Returns the stream. * * @return The stream for this object. */ @Override public COSDictionary getCOSObject() { if (functionStream != null) { return functionStream.getCOSObject(); } return functionDictionary; } /** * Returns the underlying PDStream. * * @return The stream. */ protected PDStream getPDStream() { return functionStream; } /** * Create the correct PD Model function based on the COS base function. * * @param function The COS function dictionary. * * @return The PDModel Function object. * * @throws IOException If we are unable to create the PDFunction object. */ public static PDFunction create(COSBase function) throws IOException { if (function == COSName.IDENTITY) { return new PDFunctionTypeIdentity(null); } COSBase base = function.getCOSObject(); if (!(base instanceof COSDictionary)) { throw new IOException("Expected dictionary but found " + base.getClass().getSimpleName() + " for Function"); } COSDictionary functionDictionary = (COSDictionary) base; int functionType = functionDictionary.getInt(COSName.FUNCTION_TYPE); switch (functionType) { case 0: return new PDFunctionType0(functionDictionary); case 2: return new PDFunctionType2(functionDictionary); case 3: return new PDFunctionType3(functionDictionary); case 4: return new PDFunctionType4(functionDictionary); default: throw new IOException("Error: Unknown function type " + functionType); } } /** * This will get the number of output parameters that have a range specified. A range for output parameters is * optional so this may return zero for a function that does have output parameters, this will simply return the * number that have the range specified. * * @return The number of output parameters that have a range specified. */ public int getNumberOfOutputParameters() { if (numberOfOutputValues == -1) { COSArray rangeValues = getRangeValues(); numberOfOutputValues = rangeValues.size() / 2; } return numberOfOutputValues; } /** * This will get the range for a certain output parameters. This is will never return null. If it is not present * then the range 0 to 0 will be returned. * * @param n The output parameter number to get the range for. * * @return The range for this component. */ public PDRange getRangeForOutput(int n) { COSArray rangeValues = getRangeValues(); return new PDRange(rangeValues, n); } /** * This will set the range values. * * @param rangeValues The new range values. */ public void setRangeValues(COSArray rangeValues) { range = rangeValues; getCOSObject().setItem(COSName.RANGE, rangeValues); } /** * This will get the number of input parameters that have a domain specified. * * @return The number of input parameters that have a domain specified. */ public int getNumberOfInputParameters() { if (numberOfInputValues == -1) { COSArray array = getDomainValues(); numberOfInputValues = array.size() / 2; } return numberOfInputValues; } /** * This will get the range for a certain input parameter. This is will never return null. If it is not present then * the range 0 to 0 will be returned. * * @param n The parameter number to get the domain for. * * @return The domain range for this component. */ public PDRange getDomainForInput(int n) { COSArray domainValues = getDomainValues(); return new PDRange(domainValues, n); } /** * This will set the domain values. * * @param domainValues The new domain values. */ public void setDomainValues(COSArray domainValues) { domain = domainValues; getCOSObject().setItem(COSName.DOMAIN, domainValues); } /** * @deprecated Replaced by {@link #eval(float[] input)} */ @Deprecated public COSArray eval(COSArray input) throws IOException { float[] outputValues = eval(input.toFloatArray()); COSArray array = new COSArray(); array.setFloatArray(outputValues); return array; } /** * Evaluates the function at the given input. ReturnValue = f(input) * * @param input The array of input values for the function. In many cases will be an array of a single value, but * not always. * * @return The of outputs the function returns based on those inputs. In many cases will be an array of a single * value, but not always. * * @throws IOException an IOExcpetion is thrown if something went wrong processing the function. */ public abstract float[] eval(float[] input) throws IOException; /** * Returns all ranges for the output values as COSArray . Required for type 0 and type 4 functions * * @return the ranges array. */ protected COSArray getRangeValues() { if (range == null) { range = (COSArray) getCOSObject().getDictionaryObject(COSName.RANGE); } return range; } /** * Returns all domains for the input values as COSArray. Required for all function types. * * @return the domains array. */ private COSArray getDomainValues() { if (domain == null) { domain = (COSArray) getCOSObject().getDictionaryObject(COSName.DOMAIN); } return domain; } /** * Clip the given input values to the ranges. * * @param inputValues the input values * @return the clipped values */ protected float[] clipToRange(float[] inputValues) { COSArray rangesArray = getRangeValues(); float[] result; if (rangesArray != null) { float[] rangeValues = rangesArray.toFloatArray(); int numberOfRanges = rangeValues.length / 2; result = new float[numberOfRanges]; for (int i = 0; i < numberOfRanges; i++) { int index = i << 1; result[i] = clipToRange(inputValues[i], rangeValues[index], rangeValues[index + 1]); } } else { result = inputValues; } return result; } /** * Clip the given input value to the given range. * * @param x the input value * @param rangeMin the min value of the range * @param rangeMax the max value of the range * * @return the clipped value */ protected float clipToRange(float x, float rangeMin, float rangeMax) { if (x < rangeMin) { return rangeMin; } else if (x > rangeMax) { return rangeMax; } return x; } /** * For a given value of x, interpolate calculates the y value on the line defined by the two points (xRangeMin , * xRangeMax ) and (yRangeMin , yRangeMax ). * * @param x the to be interpolated value. * @param xRangeMin the min value of the x range * @param xRangeMax the max value of the x range * @param yRangeMin the min value of the y range * @param yRangeMax the max value of the y range * @return the interpolated y value */ protected float interpolate(float x, float xRangeMin, float xRangeMax, float yRangeMin, float yRangeMax) { return yRangeMin + ((x - xRangeMin) * (yRangeMax - yRangeMin) / (xRangeMax - xRangeMin)); } /** * {@inheritDoc} */ @Override public String toString() { return "FunctionType" + getFunctionType(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/PDFunctionType0.java000066400000000000000000000421301320103431700312270ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function; import java.io.IOException; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDRange; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class represents a type 0 function in a PDF document. * * @author Ben Litchfield * @author Tilman Hausherr * */ public class PDFunctionType0 extends PDFunction { /** * Log instance. */ private static final Logger LOG = LoggerFactory.getLogger(PDFunctionType0.class); /** * An array of 2 x m numbers specifying the linear mapping of input values * into the domain of the function's sample table. Default value: [ 0 (Size0 * - 1) 0 (Size1 - 1) ...]. */ private COSArray encode = null; /** * An array of 2 x n numbers specifying the linear mapping of sample values * into the range appropriate for the function's output values. Default * value: same as the value of Range */ private COSArray decode = null; /** * An array of m positive integers specifying the number of samples in each * input dimension of the sample table. */ private COSArray size = null; /** * The samples of the function. */ private int[][] samples = null; /** * Constructor. * * @param function The function. */ public PDFunctionType0(COSBase function) { super(function); } /** * {@inheritDoc} */ @Override public int getFunctionType() { return 0; } /** * The "Size" entry, which is the number of samples in each input dimension * of the sample table. * * @return A List of java.lang.Integer objects. */ public COSArray getSize() { if (size == null) { size = (COSArray) getCOSObject().getDictionaryObject(COSName.SIZE); } return size; } /** * Get all sample values of this function. * * @return an array with all samples. */ public int[][] getSamples() { if (samples == null) { int arraySize = 1; int numberOfInputValues = getNumberOfInputParameters(); int numberOfOutputValues = getNumberOfOutputParameters(); COSArray sizes = getSize(); for (int i = 0; i < numberOfInputValues; i++) { arraySize *= sizes.getInt(i); } samples = new int[arraySize][numberOfOutputValues]; int bitsPerSample = getBitsPerSample(); int index = 0; try { // PDF spec 1.7 p.171: // Each sample value is represented as a sequence of BitsPerSample bits. // Successive values are adjacent in the bit stream; // there is no padding at byte boundaries. ImageInputStream mciis = new MemoryCacheImageInputStream(getPDStream().createInputStream()); for (int i = 0; i < arraySize; i++) { for (int k = 0; k < numberOfOutputValues; k++) { // TODO will this cast work properly for 32 bitsPerSample or should we use long[]? samples[index][k] = (int) mciis.readBits(bitsPerSample); } index++; } mciis.close(); } catch (IOException exception) { LOG.error("IOException while reading the sample values of this function.", exception); } } return samples; } /** * Get the number of bits that the output value will take up. * * Valid values are 1,2,4,8,12,16,24,32. * * @return Number of bits for each output value. */ public int getBitsPerSample() { return getCOSObject().getInt(COSName.BITS_PER_SAMPLE); } /** * Get the order of interpolation between samples. Valid values are 1 and 3, * specifying linear and cubic spline interpolation, respectively. Default * is 1. See p.170 in PDF spec 1.7. * * @return order of interpolation. */ public int getOrder() { return getCOSObject().getInt(COSName.ORDER, 1); } /** * Set the number of bits that the output value will take up. Valid values * are 1,2,4,8,12,16,24,32. * * @param bps The number of bits for each output value. */ public void setBitsPerSample(int bps) { getCOSObject().setInt(COSName.BITS_PER_SAMPLE, bps); } /** * Returns all encode values as COSArray. * * @return the encode array. */ private COSArray getEncodeValues() { if (encode == null) { encode = (COSArray) getCOSObject().getDictionaryObject(COSName.ENCODE); // the default value is [0 (size[0]-1) 0 (size[1]-1) ...] if (encode == null) { encode = new COSArray(); COSArray sizeValues = getSize(); int sizeValuesSize = sizeValues.size(); for (int i = 0; i < sizeValuesSize; i++) { encode.add(COSInteger.ZERO); encode.add(COSInteger.get(sizeValues.getInt(i) - 1)); } } } return encode; } /** * Returns all decode values as COSArray. * * @return the decode array. */ private COSArray getDecodeValues() { if (decode == null) { decode = (COSArray) getCOSObject().getDictionaryObject(COSName.DECODE); // if decode is null, the default values are the range values if (decode == null) { decode = getRangeValues(); } } return decode; } /** * Get the encode for the input parameter. * * @param paramNum The function parameter number. * * @return The encode parameter range or null if none is set. */ public PDRange getEncodeForParameter(int paramNum) { PDRange retval = null; COSArray encodeValues = getEncodeValues(); if (encodeValues != null && encodeValues.size() >= paramNum * 2 + 1) { retval = new PDRange(encodeValues, paramNum); } return retval; } /** * This will set the encode values. * * @param encodeValues The new encode values. */ public void setEncodeValues(COSArray encodeValues) { encode = encodeValues; getCOSObject().setItem(COSName.ENCODE, encodeValues); } /** * Get the decode for the input parameter. * * @param paramNum The function parameter number. * * @return The decode parameter range or null if none is set. */ public PDRange getDecodeForParameter(int paramNum) { PDRange retval = null; COSArray decodeValues = getDecodeValues(); if (decodeValues != null && decodeValues.size() >= paramNum * 2 + 1) { retval = new PDRange(decodeValues, paramNum); } return retval; } /** * This will set the decode values. * * @param decodeValues The new decode values. */ public void setDecodeValues(COSArray decodeValues) { decode = decodeValues; getCOSObject().setItem(COSName.DECODE, decodeValues); } /** * calculate array index (structure described in p.171 PDF spec 1.7) in * multiple dimensions. * * @param vector with coordinates * @return index in flat array */ private int calcSampleIndex(int[] vector) { // inspiration: http://stackoverflow.com/a/12113479/535646 // but used in reverse float[] sizeValues = getSize().toFloatArray(); int index = 0; int sizeProduct = 1; int dimension = vector.length; for (int i = dimension - 2; i >= 0; --i) { sizeProduct *= sizeValues[i]; } for (int i = dimension - 1; i >= 0; --i) { index += sizeProduct * vector[i]; if (i - 1 >= 0) { sizeProduct /= sizeValues[i - 1]; } } return index; } /** * Inner class do to an interpolation in the Nth dimension by comparing the * content size of N-1 dimensional objects. This is done with the help of * recursive calls. To understand the algorithm without recursion, here is a * bilinear * interpolation and here's a trilinear * interpolation (external links). */ class Rinterpol { // coordinate that is to be interpolated final float[] in; // coordinate of the "ceil" point final int[] inPrev; // coordinate of the "floor" point final int[] inNext; final int numberOfInputValues; final int numberOfOutputValues = getNumberOfOutputParameters(); /** * Constructor. * * @param input the input coordinates * @param inputPrev coordinate of the "ceil" point * @param inputNext coordinate of the "floor" point * */ Rinterpol(float[] input, int[] inputPrev, int[] inputNext) { in = input; inPrev = inputPrev; inNext = inputNext; numberOfInputValues = input.length; } /** * Calculate the interpolation. * * @return interpolated result sample */ public float[] rinterpolate() { return rinterpol(new int[numberOfInputValues], 0); } /** * Do a linear interpolation if the two coordinates can be known, or * call itself recursively twice. * * @param coord coord partially set coordinate (not set from step * upwards); gets fully filled in the last call ("leaf"), where it is * used to get the correct sample * @param step between 0 (first call) and dimension - 1 * @return interpolated result sample */ private float[] rinterpol(int[] coord, int step) { float[] resultSample = new float[numberOfOutputValues]; if (step == in.length - 1) { // leaf if (inPrev[step] == inNext[step]) { coord[step] = inPrev[step]; int[] tmpSample = getSamples()[calcSampleIndex(coord)]; for (int i = 0; i < numberOfOutputValues; ++i) { resultSample[i] = tmpSample[i]; } return resultSample; } coord[step] = inPrev[step]; int[] sample1 = getSamples()[calcSampleIndex(coord)]; coord[step] = inNext[step]; int[] sample2 = getSamples()[calcSampleIndex(coord)]; for (int i = 0; i < numberOfOutputValues; ++i) { resultSample[i] = interpolate(in[step], inPrev[step], inNext[step], sample1[i], sample2[i]); } return resultSample; } else { // branch if (inPrev[step] == inNext[step]) { coord[step] = inPrev[step]; return rinterpol(coord, step + 1); } coord[step] = inPrev[step]; float[] sample1 = rinterpol(coord, step + 1); coord[step] = inNext[step]; float[] sample2 = rinterpol(coord, step + 1); for (int i = 0; i < numberOfOutputValues; ++i) { resultSample[i] = interpolate(in[step], inPrev[step], inNext[step], sample1[i], sample2[i]); } return resultSample; } } } @Override public float[] eval(float[] input) { //This involves linear interpolation based on a set of sample points. //Theoretically it's not that difficult ... see section 3.9.1 of the PDF Reference. float[] sizeValues = getSize().toFloatArray(); int bitsPerSample = getBitsPerSample(); float maxSample = (float) (Math.pow(2, bitsPerSample) - 1.0); int numberOfInputValues = input.length; int numberOfOutputValues = getNumberOfOutputParameters(); int[] inputPrev = new int[numberOfInputValues]; int[] inputNext = new int[numberOfInputValues]; for (int i = 0; i < numberOfInputValues; i++) { PDRange domain = getDomainForInput(i); PDRange encodeValues = getEncodeForParameter(i); input[i] = clipToRange(input[i], domain.getMin(), domain.getMax()); input[i] = interpolate(input[i], domain.getMin(), domain.getMax(), encodeValues.getMin(), encodeValues.getMax()); input[i] = clipToRange(input[i], 0, sizeValues[i] - 1); inputPrev[i] = (int) Math.floor(input[i]); inputNext[i] = (int) Math.ceil(input[i]); } // old code for N=1 and N=2, don't delete in case one uses this for optimization // // if (numberOfInputValues == 1) // { // int[] sample1 = getSamples()[calcSampleIndex(new int[] // { // inputPrev[0] // })]; // int[] sample2 = getSamples()[calcSampleIndex(new int[] // { // inputNext[0] // })]; // for (int i = 0; i < numberOfOutputValues; ++i) // { // outputValues[i] = inputPrev[0] == inputNext[0] ? sample1[i] : interpolate(input[0], inputPrev[0], inputNext[0], sample1[i], sample2[i]); // } // //TODO optimize so that sample is collected only when needed // } // if (numberOfInputValues == 2) // { // int[] sample1 = getSamples()[calcSampleIndex(new int[] // { // inputPrev[0], inputPrev[1] // })]; // int[] sample2 = getSamples()[calcSampleIndex(new int[] // { // inputPrev[0], inputNext[1] // })]; // int[] sample3 = getSamples()[calcSampleIndex(new int[] // { // inputNext[0], inputPrev[1] // })]; // int[] sample4 = getSamples()[calcSampleIndex(new int[] // { // inputNext[0], inputNext[1] // })]; // // for (int i = 0; i < numberOfOutputValues; ++i) // { // // bilinear color interpolation, see e.g. // // http://harmoniccode.blogspot.de/2011/04/bilinear-color-interpolation.html // // interpolate the color at top and bottom edges (x-axis) // // then interpolate the color between these two results (y-axis) // double lowerVal = inputPrev[0] == inputNext[0] ? sample1[i] : interpolate(input[0], inputPrev[0], inputNext[0], sample1[i], sample3[i]); // double upperVal = inputPrev[0] == inputNext[0] ? sample2[i] : interpolate(input[0], inputPrev[0], inputNext[0], sample2[i], sample4[i]); // outputValues[i] = (float) (inputPrev[1] == inputNext[1] ? lowerVal : interpolate(input[1], inputPrev[1], inputNext[1], (float) lowerVal, (float) upperVal)); // //TODO optimize so that sample is collected only when needed // } // } // float[] outputValues = new Rinterpol(input, inputPrev, inputNext).rinterpolate(); for (int i = 0; i < numberOfOutputValues; i++) { PDRange range = getRangeForOutput(i); PDRange decodeValues = getDecodeForParameter(i); outputValues[i] = interpolate(outputValues[i], 0, maxSample, decodeValues.getMin(), decodeValues.getMax()); outputValues[i] = clipToRange(outputValues[i], range.getMin(), range.getMax()); } return outputValues; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/PDFunctionType2.java000066400000000000000000000075361320103431700312440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; /** * This class represents a Type 2 (exponential interpolation) function in a PDF * document. * * @author Ben Litchfield */ public class PDFunctionType2 extends PDFunction { /** * The C0 values of the exponential function. */ private final COSArray c0; /** * The C1 values of the exponential function. */ private final COSArray c1; /** * The exponent value of the exponential function. */ private final float exponent; /** * Constructor. * * @param function The function. */ public PDFunctionType2(COSBase function) { super(function); if (getCOSObject().getDictionaryObject(COSName.C0) instanceof COSArray) { c0 = (COSArray) getCOSObject().getDictionaryObject(COSName.C0); } else { c0 = new COSArray(); } if (c0.size() == 0) { c0.add(new COSFloat(0)); } if (getCOSObject().getDictionaryObject(COSName.C1) instanceof COSArray) { c1 = (COSArray) getCOSObject().getDictionaryObject(COSName.C1); } else { c1 = new COSArray(); } if (c1.size() == 0) { c1.add(new COSFloat(1)); } exponent = getCOSObject().getFloat(COSName.N); } /** * {@inheritDoc} */ @Override public int getFunctionType() { return 2; } /** * Performs exponential interpolation * * {@inheritDoc} */ @Override public float[] eval(float[] input) { // exponential interpolation float xToN = (float) Math.pow(input[0], exponent); // x^exponent float[] result = new float[Math.min(c0.size(), c1.size())]; for (int j = 0; j < result.length; j++) { float c0j = ((COSNumber) c0.getObject(j)).floatValue(); float c1j = ((COSNumber) c1.getObject(j)).floatValue(); result[j] = c0j + xToN * (c1j - c0j); } return clipToRange(result); } /** * Returns the C0 values of the function, 0 if empty. * * @return a COSArray with the C0 values */ public COSArray getC0() { return c0; } /** * Returns the C1 values of the function, 1 if empty. * * @return a COSArray with the C1 values */ public COSArray getC1() { return c1; } /** * Returns the exponent of the function. * * @return the float value of the exponent */ public float getN() { return exponent; } /** * {@inheritDoc} */ @Override public String toString() { return "FunctionType2{" + "C0: " + getC0() + " " + "C1: " + getC1() + " " + "N: " + getN() + "}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/PDFunctionType3.java000066400000000000000000000132421320103431700312340ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDRange; /** * This class represents a Type 3 (stitching) function in a PDF document. * * @author Ben Litchfield */ public class PDFunctionType3 extends PDFunction { private COSArray functions = null; private COSArray encode = null; private COSArray bounds = null; private PDFunction[] functionsArray = null; /** * Constructor. * * @param functionStream The function . */ public PDFunctionType3(COSBase functionStream) { super( functionStream ); } /** * {@inheritDoc} */ @Override public int getFunctionType() { return 3; } /** * {@inheritDoc} */ @Override public float[] eval(float[] input) throws IOException { //This function is known as a "stitching" function. Based on the input, it decides which child function to call. // All functions in the array are 1-value-input functions //See PDF Reference section 3.9.3. PDFunction function = null; float x = input[0]; PDRange domain = getDomainForInput(0); // clip input value to domain x = clipToRange(x, domain.getMin(), domain.getMax()); if (functionsArray == null) { COSArray ar = getFunctions(); functionsArray = new PDFunction[ar.size()]; for (int i = 0; i < ar.size(); ++i) { functionsArray[i] = PDFunction.create(ar.getObject(i)); } } if (functionsArray.length == 1) { // This doesn't make sense but it may happen ... function = functionsArray[0]; PDRange encRange = getEncodeForParameter(0); x = interpolate(x, domain.getMin(), domain.getMax(), encRange.getMin(), encRange.getMax()); } else { float[] boundsValues = getBounds().toFloatArray(); int boundsSize = boundsValues.length; // create a combined array containing the domain and the bounds values // domain.min, bounds[0], bounds[1], ...., bounds[boundsSize-1], domain.max float[] partitionValues = new float[boundsSize+2]; int partitionValuesSize = partitionValues.length; partitionValues[0] = domain.getMin(); partitionValues[partitionValuesSize-1] = domain.getMax(); System.arraycopy(boundsValues, 0, partitionValues, 1, boundsSize); // find the partition for (int i=0; i < partitionValuesSize-1; i++) { if ( x >= partitionValues[i] && (x < partitionValues[i+1] || (i == partitionValuesSize - 2 && x == partitionValues[i+1]))) { function = functionsArray[i]; PDRange encRange = getEncodeForParameter(i); x = interpolate(x, partitionValues[i], partitionValues[i+1], encRange.getMin(), encRange.getMax()); break; } } if (function == null) { throw new IOException("partition not found in type 3 function"); } } float[] functionValues = new float[]{x}; // calculate the output values using the chosen function float[] functionResult = function.eval(functionValues); // clip to range if available return clipToRange(functionResult); } /** * Returns all functions values as COSArray. * * @return the functions array. */ public COSArray getFunctions() { if (functions == null) { functions = (COSArray)(getCOSObject().getDictionaryObject( COSName.FUNCTIONS )); } return functions; } /** * Returns all bounds values as COSArray. * * @return the bounds array. */ public COSArray getBounds() { if (bounds == null) { bounds = (COSArray)(getCOSObject().getDictionaryObject( COSName.BOUNDS )); } return bounds; } /** * Returns all encode values as COSArray. * * @return the encode array. */ public COSArray getEncode() { if (encode == null) { encode = (COSArray)(getCOSObject().getDictionaryObject( COSName.ENCODE )); } return encode; } /** * Get the encode for the input parameter. * * @param n The function parameter number. * * @return The encode parameter range or null if none is set. */ private PDRange getEncodeForParameter(int n) { COSArray encodeValues = getEncode(); return new PDRange( encodeValues, n ); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/PDFunctionType4.java000066400000000000000000000067551320103431700312500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.pdmodel.common.PDRange; import org.sejda.sambox.pdmodel.common.function.type4.ExecutionContext; import org.sejda.sambox.pdmodel.common.function.type4.InstructionSequence; import org.sejda.sambox.pdmodel.common.function.type4.InstructionSequenceBuilder; import org.sejda.sambox.pdmodel.common.function.type4.Operators; /** * This class represents a Type 4 (PostScript calculator) function in a PDF document. *

* See section 3.9.4 of the PDF 1.4 Reference. * */ public class PDFunctionType4 extends PDFunction { private static final Operators OPERATORS = new Operators(); private final InstructionSequence instructions; /** * Constructor. * * @param functionStream The function stream. * @throws IOException if an I/O error occurs while reading the function */ public PDFunctionType4(COSBase functionStream) throws IOException { super(functionStream); byte[] bytes = getPDStream().toByteArray(); String string = new String(bytes, "ISO-8859-1"); this.instructions = InstructionSequenceBuilder.parse(string); } /** * {@inheritDoc} */ public int getFunctionType() { return 4; } /** * {@inheritDoc} */ public float[] eval(float[] input) { // Setup the input values ExecutionContext context = new ExecutionContext(OPERATORS); for (int i = 0; i < input.length; i++) { PDRange domain = getDomainForInput(i); float value = clipToRange(input[i], domain.getMin(), domain.getMax()); context.getStack().push(value); } // Execute the type 4 function. instructions.execute(context); // Extract the output values int numberOfOutputValues = getNumberOfOutputParameters(); int numberOfActualOutputValues = context.getStack().size(); if (numberOfActualOutputValues < numberOfOutputValues) { throw new IllegalStateException("The type 4 function returned " + numberOfActualOutputValues + " values but the Range entry indicates that " + numberOfOutputValues + " values be returned."); } float[] outputValues = new float[numberOfOutputValues]; for (int i = numberOfOutputValues - 1; i >= 0; i--) { PDRange range = getRangeForOutput(i); outputValues[i] = context.popReal(); outputValues[i] = clipToRange(outputValues[i], range.getMin(), range.getMax()); } // Return the resulting array return outputValues; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/PDFunctionTypeIdentity.java000066400000000000000000000023751320103431700326700ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.common.function; import org.sejda.sambox.cos.COSBase; /** * The identity function. * * @author Tilman Hausherr */ public class PDFunctionTypeIdentity extends PDFunction { public PDFunctionTypeIdentity(COSBase function) { super(null); } @Override public int getFunctionType() { // shouldn't be called throw new UnsupportedOperationException(); } @Override public float[] eval(float[] input) { return input; } /** * {@inheritDoc} */ @Override public String toString() { return "FunctionTypeIdentity"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/package.html000066400000000000000000000017321320103431700277150ustar00rootroot00000000000000 This package contains functions that are available in the PDF specification. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/000077500000000000000000000000001320103431700264765ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/ArithmeticOperators.java000066400000000000000000000247131320103431700333400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.Stack; /** * Provides the arithmetic operators such as "add" and "sub". * */ class ArithmeticOperators { /** Implements the "abs" operator. */ static class Abs implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); if (num instanceof Integer) { context.getStack().push(Math.abs(num.intValue())); } else { context.getStack().push(Math.abs(num.floatValue())); } } } /** Implements the "add" operator. */ static class Add implements Operator { public void execute(ExecutionContext context) { Number num2 = context.popNumber(); Number num1 = context.popNumber(); if (num1 instanceof Integer && num2 instanceof Integer) { long sum = num1.longValue() + num2.longValue(); if (sum < Integer.MIN_VALUE || sum > Integer.MAX_VALUE) { context.getStack().push((float) sum); } else { context.getStack().push((int)sum); } } else { float sum = num1.floatValue() + num2.floatValue(); context.getStack().push(sum); } } } /** Implements the "atan" operator. */ static class Atan implements Operator { public void execute(ExecutionContext context) { float den = context.popReal(); float num = context.popReal(); float atan = (float)Math.atan2(num, den); atan = (float)Math.toDegrees(atan) % 360; if (atan < 0) { atan = atan + 360; } context.getStack().push(atan); } } /** Implements the "ceiling" operator. */ static class Ceiling implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); if (num instanceof Integer) { context.getStack().push(num); } else { context.getStack().push((float)Math.ceil(num.doubleValue())); } } } /** Implements the "cos" operator. */ static class Cos implements Operator { public void execute(ExecutionContext context) { float angle = context.popReal(); float cos = (float)Math.cos(Math.toRadians(angle)); context.getStack().push(cos); } } /** Implements the "cvi" operator. */ static class Cvi implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); context.getStack().push(num.intValue()); } } /** Implements the "cvr" operator. */ static class Cvr implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); context.getStack().push(num.floatValue()); } } /** Implements the "div" operator. */ static class Div implements Operator { public void execute(ExecutionContext context) { Number num2 = context.popNumber(); Number num1 = context.popNumber(); context.getStack().push(num1.floatValue() / num2.floatValue()); } } /** Implements the "exp" operator. */ static class Exp implements Operator { public void execute(ExecutionContext context) { Number exp = context.popNumber(); Number base = context.popNumber(); double value = Math.pow(base.doubleValue(), exp.doubleValue()); context.getStack().push((float)value); } } /** Implements the "floor" operator. */ static class Floor implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); if (num instanceof Integer) { context.getStack().push(num); } else { context.getStack().push((float)Math.floor(num.doubleValue())); } } } /** Implements the "idiv" operator. */ static class IDiv implements Operator { public void execute(ExecutionContext context) { int num2 = context.popInt(); int num1 = context.popInt(); context.getStack().push(num1 / num2); } } /** Implements the "ln" operator. */ static class Ln implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); context.getStack().push((float)Math.log(num.doubleValue())); } } /** Implements the "log" operator. */ static class Log implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); context.getStack().push((float)Math.log10(num.doubleValue())); } } /** Implements the "mod" operator. */ static class Mod implements Operator { public void execute(ExecutionContext context) { int int2 = context.popInt(); int int1 = context.popInt(); context.getStack().push(int1 % int2); } } /** Implements the "mul" operator. */ static class Mul implements Operator { public void execute(ExecutionContext context) { Number num2 = context.popNumber(); Number num1 = context.popNumber(); if (num1 instanceof Integer && num2 instanceof Integer) { long result = num1.longValue() * num2.longValue(); if (result >= Integer.MIN_VALUE && result <= Integer.MAX_VALUE) { context.getStack().push((int)result); } else { context.getStack().push((float)result); } } else { double result = num1.doubleValue() * num2.doubleValue(); context.getStack().push((float)result); } } } /** Implements the "neg" operator. */ static class Neg implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); if (num instanceof Integer) { int v = num.intValue(); if (v == Integer.MIN_VALUE) { context.getStack().push(-num.floatValue()); } else { context.getStack().push(-num.intValue()); } } else { context.getStack().push(-num.floatValue()); } } } /** Implements the "round" operator. */ static class Round implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); if (num instanceof Integer) { context.getStack().push(num.intValue()); } else { context.getStack().push((float)Math.round(num.doubleValue())); } } } /** Implements the "sin" operator. */ static class Sin implements Operator { public void execute(ExecutionContext context) { float angle = context.popReal(); float sin = (float)Math.sin(Math.toRadians(angle)); context.getStack().push(sin); } } /** Implements the "sqrt" operator. */ static class Sqrt implements Operator { public void execute(ExecutionContext context) { float num = context.popReal(); if (num < 0) { throw new IllegalArgumentException("argument must be nonnegative"); } context.getStack().push((float)Math.sqrt(num)); } } /** Implements the "sub" operator. */ static class Sub implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); Number num2 = context.popNumber(); Number num1 = context.popNumber(); if (num1 instanceof Integer && num2 instanceof Integer) { long result = num1.longValue() - num2.longValue(); if (result < Integer.MIN_VALUE || result > Integer.MAX_VALUE) { stack.push((float) result); } else { stack.push((int)result); } } else { float result = num1.floatValue() - num2.floatValue(); stack.push(result); } } } /** Implements the "truncate" operator. */ static class Truncate implements Operator { public void execute(ExecutionContext context) { Number num = context.popNumber(); if (num instanceof Integer) { context.getStack().push(num.intValue()); } else { context.getStack().push((float)(int)(num.floatValue())); } } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/BitwiseOperators.java000066400000000000000000000121451320103431700326510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.Stack; /** * Provides the bitwise operators such as "and" and "xor". * */ class BitwiseOperators { /** Abstract base class for logical operators. */ private abstract static class AbstractLogicalOperator implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); Object op2 = stack.pop(); Object op1 = stack.pop(); if (op1 instanceof Boolean && op2 instanceof Boolean) { boolean bool1 = (Boolean)op1; boolean bool2 = (Boolean)op2; boolean result = applyForBoolean(bool1, bool2); stack.push(result); } else if (op1 instanceof Integer && op2 instanceof Integer) { int int1 = (Integer)op1; int int2 = (Integer)op2; int result = applyforInteger(int1, int2); stack.push(result); } else { throw new ClassCastException("Operands must be bool/bool or int/int"); } } protected abstract boolean applyForBoolean(boolean bool1, boolean bool2); protected abstract int applyforInteger(int int1, int int2); } /** Implements the "and" operator. */ static class And extends AbstractLogicalOperator { @Override protected boolean applyForBoolean(boolean bool1, boolean bool2) { return bool1 & bool2; } @Override protected int applyforInteger(int int1, int int2) { return int1 & int2; } } /** Implements the "bitshift" operator. */ static class Bitshift implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); int shift = (Integer)stack.pop(); int int1 = (Integer)stack.pop(); if (shift < 0) { int result = int1 >> Math.abs(shift); stack.push(result); } else { int result = int1 << shift; stack.push(result); } } } /** Implements the "false" operator. */ static class False implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); stack.push(Boolean.FALSE); } } /** Implements the "not" operator. */ static class Not implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); Object op1 = stack.pop(); if (op1 instanceof Boolean) { boolean bool1 = (Boolean)op1; boolean result = !bool1; stack.push(result); } else if (op1 instanceof Integer) { int int1 = (Integer)op1; int result = -int1; stack.push(result); } else { throw new ClassCastException("Operand must be bool or int"); } } } /** Implements the "or" operator. */ static class Or extends AbstractLogicalOperator { @Override protected boolean applyForBoolean(boolean bool1, boolean bool2) { return bool1 | bool2; } @Override protected int applyforInteger(int int1, int int2) { return int1 | int2; } } /** Implements the "true" operator. */ static class True implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); stack.push(Boolean.TRUE); } } /** Implements the "xor" operator. */ static class Xor extends AbstractLogicalOperator { @Override protected boolean applyForBoolean(boolean bool1, boolean bool2) { return bool1 ^ bool2; } @Override protected int applyforInteger(int int1, int int2) { return int1 ^ int2; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/ConditionalOperators.java000066400000000000000000000040041320103431700335010ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.Stack; /** * Provides the conditional operators such as "if" and "ifelse". * */ class ConditionalOperators { /** Implements the "if" operator. */ static class If implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); InstructionSequence proc = (InstructionSequence)stack.pop(); Boolean condition = (Boolean)stack.pop(); if (condition) { proc.execute(context); } } } /** Implements the "ifelse" operator. */ static class IfElse implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); InstructionSequence proc2 = (InstructionSequence)stack.pop(); InstructionSequence proc1 = (InstructionSequence)stack.pop(); Boolean condition = (Boolean)stack.pop(); if (condition) { proc1.execute(context); } else { proc2.execute(context); } } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/ExecutionContext.java000066400000000000000000000046251320103431700326600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.Stack; /** * Makes up the execution context, holding the available operators and the execution stack. * */ public class ExecutionContext { private final Operators operators; private final Stack stack = new Stack(); /** * Creates a new execution context. * @param operatorSet the operator set */ public ExecutionContext(Operators operatorSet) { this.operators = operatorSet; } /** * Returns the stack used by this execution context. * @return the stack */ public Stack getStack() { return this.stack; } /** * Returns the operator set used by this execution context. * @return the operator set */ public Operators getOperators() { return this.operators; } /** * Pops a number (int or real) from the stack. If it's neither data type, a * ClassCastException is thrown. * @return the number */ public Number popNumber() { return (Number)stack.pop(); } /** * Pops a value of type int from the stack. If the value is not of type int, a * ClassCastException is thrown. * @return the int value */ public int popInt() { return ((Integer)stack.pop()); } /** * Pops a number from the stack and returns it as a real value. If the value is not of a * numeric type, a ClassCastException is thrown. * @return the real value */ public float popReal() { return ((Number)stack.pop()).floatValue(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/InstructionSequence.java000066400000000000000000000057711320103431700333650ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.List; import java.util.Stack; /** * Represents an instruction sequence, a combination of values, operands and nested procedures. * */ public class InstructionSequence { private final List instructions = new java.util.ArrayList(); /** * Add a name (ex. an operator) * @param name the name */ public void addName(String name) { this.instructions.add(name); } /** * Adds an int value. * @param value the value */ public void addInteger(int value) { this.instructions.add(value); } /** * Adds a real value. * @param value the value */ public void addReal(float value) { this.instructions.add(value); } /** * Adds a bool value. * @param value the value */ public void addBoolean(boolean value) { this.instructions.add(value); } /** * Adds a proc (sub-sequence of instructions). * @param child the child proc */ public void addProc(InstructionSequence child) { this.instructions.add(child); } /** * Executes the instruction sequence. * @param context the execution context */ public void execute(ExecutionContext context) { Stack stack = context.getStack(); for (Object o : instructions) { if (o instanceof String) { String name = (String)o; Operator cmd = context.getOperators().getOperator(name); if (cmd != null) { cmd.execute(context); } else { throw new UnsupportedOperationException("Unknown operator or name: " + name); } } else { stack.push(o); } } //Handles top-level procs that simply need to be executed while (!stack.isEmpty() && stack.peek() instanceof InstructionSequence) { InstructionSequence nested = (InstructionSequence)stack.pop(); nested.execute(context); } } } InstructionSequenceBuilder.java000066400000000000000000000076761320103431700346230ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Basic parser for Type 4 functions which is used to build up instruction sequences. * */ public final class InstructionSequenceBuilder extends Parser.AbstractSyntaxHandler { private final InstructionSequence mainSequence = new InstructionSequence(); private final Stack seqStack = new Stack(); private InstructionSequenceBuilder() { this.seqStack.push(this.mainSequence); } /** * Returns the instruction sequence that has been build from the syntactic elements. * @return the instruction sequence */ public InstructionSequence getInstructionSequence() { return this.mainSequence; } /** * Parses the given text into an instruction sequence representing a Type 4 function * that can be executed. * @param text the Type 4 function text * @return the instruction sequence */ public static InstructionSequence parse(CharSequence text) { InstructionSequenceBuilder builder = new InstructionSequenceBuilder(); Parser.parse(text, builder); return builder.getInstructionSequence(); } private InstructionSequence getCurrentSequence() { return this.seqStack.peek(); } private static final Pattern INTEGER_PATTERN = Pattern.compile("[\\+\\-]?\\d+"); private static final Pattern REAL_PATTERN = Pattern.compile("[\\-]?\\d*\\.\\d*([Ee]\\-?\\d+)?"); /** {@inheritDoc} */ @Override public void token(CharSequence text) { String token = text.toString(); token(token); } private void token(String token) { if ("{".equals(token)) { InstructionSequence child = new InstructionSequence(); getCurrentSequence().addProc(child); this.seqStack.push(child); } else if ("}".equals(token)) { this.seqStack.pop(); } else { Matcher m = INTEGER_PATTERN.matcher(token); if (m.matches()) { getCurrentSequence().addInteger(parseInt(token)); return; } m = REAL_PATTERN.matcher(token); if (m.matches()) { getCurrentSequence().addReal(parseReal(token)); return; } //TODO Maybe implement radix numbers, such as 8#1777 or 16#FFFE getCurrentSequence().addName(token); } } /** * Parses a value of type "int". * @param token the token to be parsed * @return the parsed value */ public static int parseInt(String token) { //TODO Beginning with JDK7 Integer.parseInt accepts leading +'s return Integer.parseInt(token.startsWith("+") ? token.substring(1) : token); } /** * Parses a value of type "real". * @param token the token to be parsed * @return the parsed value */ public static float parseReal(String token) { return Float.parseFloat(token); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/Operator.java000066400000000000000000000021451320103431700311360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; /** * Interface for PostScript operators. * */ public interface Operator { /** * Executes the operator. The method can inspect and manipulate the stack. * @param context the execution context */ void execute(ExecutionContext context); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/Operators.java000066400000000000000000000137451320103431700313310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.Map; /** * This class provides all the supported operators. */ public class Operators { //Arithmetic operators private static final Operator ABS = new ArithmeticOperators.Abs(); private static final Operator ADD = new ArithmeticOperators.Add(); private static final Operator ATAN = new ArithmeticOperators.Atan(); private static final Operator CEILING = new ArithmeticOperators.Ceiling(); private static final Operator COS = new ArithmeticOperators.Cos(); private static final Operator CVI = new ArithmeticOperators.Cvi(); private static final Operator CVR = new ArithmeticOperators.Cvr(); private static final Operator DIV = new ArithmeticOperators.Div(); private static final Operator EXP = new ArithmeticOperators.Exp(); private static final Operator FLOOR = new ArithmeticOperators.Floor(); private static final Operator IDIV = new ArithmeticOperators.IDiv(); private static final Operator LN = new ArithmeticOperators.Ln(); private static final Operator LOG = new ArithmeticOperators.Log(); private static final Operator MOD = new ArithmeticOperators.Mod(); private static final Operator MUL = new ArithmeticOperators.Mul(); private static final Operator NEG = new ArithmeticOperators.Neg(); private static final Operator ROUND = new ArithmeticOperators.Round(); private static final Operator SIN = new ArithmeticOperators.Sin(); private static final Operator SQRT = new ArithmeticOperators.Sqrt(); private static final Operator SUB = new ArithmeticOperators.Sub(); private static final Operator TRUNCATE = new ArithmeticOperators.Truncate(); //Relational, boolean and bitwise operators private static final Operator AND = new BitwiseOperators.And(); private static final Operator BITSHIFT = new BitwiseOperators.Bitshift(); private static final Operator EQ = new RelationalOperators.Eq(); private static final Operator FALSE = new BitwiseOperators.False(); private static final Operator GE = new RelationalOperators.Ge(); private static final Operator GT = new RelationalOperators.Gt(); private static final Operator LE = new RelationalOperators.Le(); private static final Operator LT = new RelationalOperators.Lt(); private static final Operator NE = new RelationalOperators.Ne(); private static final Operator NOT = new BitwiseOperators.Not(); private static final Operator OR = new BitwiseOperators.Or(); private static final Operator TRUE = new BitwiseOperators.True(); private static final Operator XOR = new BitwiseOperators.Xor(); //Conditional operators private static final Operator IF = new ConditionalOperators.If(); private static final Operator IFELSE = new ConditionalOperators.IfElse(); //Stack operators private static final Operator COPY = new StackOperators.Copy(); private static final Operator DUP = new StackOperators.Dup(); private static final Operator EXCH = new StackOperators.Exch(); private static final Operator INDEX = new StackOperators.Index(); private static final Operator POP = new StackOperators.Pop(); private static final Operator ROLL = new StackOperators.Roll(); private final Map operators = new java.util.HashMap(); /** * Creates a new Operators object with the default set of operators. */ public Operators() { operators.put("add", ADD); operators.put("abs", ABS); operators.put("atan", ATAN); operators.put("ceiling", CEILING); operators.put("cos", COS); operators.put("cvi", CVI); operators.put("cvr", CVR); operators.put("div", DIV); operators.put("exp", EXP); operators.put("floor", FLOOR); operators.put("idiv", IDIV); operators.put("ln", LN); operators.put("log", LOG); operators.put("mod", MOD); operators.put("mul", MUL); operators.put("neg", NEG); operators.put("round", ROUND); operators.put("sin", SIN); operators.put("sqrt", SQRT); operators.put("sub", SUB); operators.put("truncate", TRUNCATE); operators.put("and", AND); operators.put("bitshift", BITSHIFT); operators.put("eq", EQ); operators.put("false", FALSE); operators.put("ge", GE); operators.put("gt", GT); operators.put("le", LE); operators.put("lt", LT); operators.put("ne", NE); operators.put("not", NOT); operators.put("or", OR); operators.put("true", TRUE); operators.put("xor", XOR); operators.put("if", IF); operators.put("ifelse", IFELSE); operators.put("copy", COPY); operators.put("dup", DUP); operators.put("exch", EXCH); operators.put("index", INDEX); operators.put("pop", POP); operators.put("roll", ROLL); } /** * Returns the operator for the given operator name. * @param operatorName the operator name * @return the operator (or null if there's no such operator */ public Operator getOperator(String operatorName) { return this.operators.get(operatorName); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/Parser.java000066400000000000000000000202111320103431700305710ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; /** * Parser for PDF Type 4 functions. This implements a small subset of the PostScript * language but is no full PostScript interpreter. * */ public final class Parser { /** Used to indicate the parsers current state. */ private enum State { NEWLINE, WHITESPACE, COMMENT, TOKEN } private Parser() { //nop } /** * Parses a Type 4 function and sends the syntactic elements to the given * syntax handler. * @param input the text source * @param handler the syntax handler */ public static void parse(CharSequence input, SyntaxHandler handler) { Tokenizer tokenizer = new Tokenizer(input, handler); tokenizer.tokenize(); } /** * This interface defines all possible syntactic elements of a Type 4 function. * It is called by the parser as the function is interpreted. */ public interface SyntaxHandler { /** * Indicates that a new line starts. * @param text the new line character (CR, LF, CR/LF or FF) */ void newLine(CharSequence text); /** * Called when whitespace characters are encountered. * @param text the whitespace text */ void whitespace(CharSequence text); /** * Called when a token is encountered. No distinction between operators and values * is done here. * @param text the token text */ void token(CharSequence text); /** * Called for a comment. * @param text the comment */ void comment(CharSequence text); } /** * Abstract base class for a {@link SyntaxHandler}. */ public abstract static class AbstractSyntaxHandler implements SyntaxHandler { /** {@inheritDoc} */ @Override public void comment(CharSequence text) { //nop } /** {@inheritDoc} */ @Override public void newLine(CharSequence text) { //nop } /** {@inheritDoc} */ @Override public void whitespace(CharSequence text) { //nop } } /** * Tokenizer for Type 4 functions. */ private static final class Tokenizer { private static final char NUL = '\u0000'; //NUL private static final char EOT = '\u0004'; //END OF TRANSMISSION private static final char TAB = '\u0009'; //TAB CHARACTER private static final char FF = '\u000C'; //FORM FEED private static final char CR = '\r'; //CARRIAGE RETURN private static final char LF = '\n'; //LINE FEED private static final char SPACE = '\u0020'; //SPACE private final CharSequence input; private int index; private final SyntaxHandler handler; private State state = State.WHITESPACE; private final StringBuilder buffer = new StringBuilder(); private Tokenizer(CharSequence text, SyntaxHandler syntaxHandler) { this.input = text; this.handler = syntaxHandler; } private boolean hasMore() { return index < input.length(); } private char currentChar() { return input.charAt(index); } private char nextChar() { index++; if (!hasMore()) { return EOT; } else { return currentChar(); } } private char peek() { if (index < input.length() - 1) { return input.charAt(index + 1); } else { return EOT; } } private State nextState() { char ch = currentChar(); switch (ch) { case CR: case LF: case FF: //FF state = State.NEWLINE; break; case NUL: case TAB: case SPACE: state = State.WHITESPACE; break; case '%': state = State.COMMENT; break; default: state = State.TOKEN; } return state; } private void tokenize() { while (hasMore()) { buffer.setLength(0); nextState(); switch (state) { case NEWLINE: scanNewLine(); break; case WHITESPACE: scanWhitespace(); break; case COMMENT: scanComment(); break; default: scanToken(); } } } private void scanNewLine() { assert state == State.NEWLINE; char ch = currentChar(); buffer.append(ch); if (ch == CR && peek() == LF) { //CRLF is treated as one newline buffer.append(nextChar()); } handler.newLine(buffer); nextChar(); } private void scanWhitespace() { assert state == State.WHITESPACE; buffer.append(currentChar()); loop: while (hasMore()) { char ch = nextChar(); switch (ch) { case NUL: case TAB: case SPACE: buffer.append(ch); break; default: break loop; } } handler.whitespace(buffer); } private void scanComment() { assert state == State.COMMENT; buffer.append(currentChar()); loop: while (hasMore()) { char ch = nextChar(); switch (ch) { case CR: case LF: case FF: break loop; default: buffer.append(ch); } } //EOF reached handler.comment(buffer); } private void scanToken() { assert state == State.TOKEN; char ch = currentChar(); buffer.append(ch); switch (ch) { case '{': case '}': handler.token(buffer); nextChar(); return; default: //continue } loop: while (hasMore()) { ch = nextChar(); switch (ch) { case NUL: case TAB: case SPACE: case CR: case LF: case FF: case EOT: case '{': case '}': break loop; default: buffer.append(ch); } } //EOF reached handler.token(buffer); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/RelationalOperators.java000066400000000000000000000073271320103431700333430ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.Stack; /** * Provides the relational operators such as "eq" and "le". * */ class RelationalOperators { /** Implements the "eq" operator. */ static class Eq implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); Object op2 = stack.pop(); Object op1 = stack.pop(); boolean result = isEqual(op1, op2); stack.push(result); } protected boolean isEqual(Object op1, Object op2) { boolean result = false; if (op1 instanceof Number && op2 instanceof Number) { Number num1 = (Number)op1; Number num2 = (Number)op2; result = num1.floatValue() == num2.floatValue(); } else { result = op1.equals(op2); } return result; } } /** Abstract base class for number comparison operators. */ private abstract static class AbstractNumberComparisonOperator implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); Object op2 = stack.pop(); Object op1 = stack.pop(); Number num1 = (Number)op1; Number num2 = (Number)op2; boolean result = compare(num1, num2); stack.push(result); } protected abstract boolean compare(Number num1, Number num2); } /** Implements the "ge" operator. */ static class Ge extends AbstractNumberComparisonOperator { @Override protected boolean compare(Number num1, Number num2) { return num1.floatValue() >= num2.floatValue(); } } /** Implements the "gt" operator. */ static class Gt extends AbstractNumberComparisonOperator { @Override protected boolean compare(Number num1, Number num2) { return num1.floatValue() > num2.floatValue(); } } /** Implements the "le" operator. */ static class Le extends AbstractNumberComparisonOperator { @Override protected boolean compare(Number num1, Number num2) { return num1.floatValue() <= num2.floatValue(); } } /** Implements the "lt" operator. */ static class Lt extends AbstractNumberComparisonOperator { @Override protected boolean compare(Number num1, Number num2) { return num1.floatValue() < num2.floatValue(); } } /** Implements the "ne" operator. */ static class Ne extends Eq { @Override protected boolean isEqual(Object op1, Object op2) { boolean result = super.isEqual(op1, op2); return !result; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/StackOperators.java000066400000000000000000000107731320103431700323150ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.common.function.type4; import java.util.LinkedList; import java.util.List; import java.util.Stack; /** * Provides the stack operators such as "pop" and "dup". * */ class StackOperators { /** Implements the "copy" operator. */ static class Copy implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); int n = ((Number)stack.pop()).intValue(); if (n > 0) { int size = stack.size(); //Need to copy to a new list to avoid ConcurrentModificationException List copy = new java.util.ArrayList( stack.subList(size - n, size)); stack.addAll(copy); } } } /** Implements the "dup" operator. */ static class Dup implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); stack.push(stack.peek()); } } /** Implements the "exch" operator. */ static class Exch implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); Object any2 = stack.pop(); Object any1 = stack.pop(); stack.push(any2); stack.push(any1); } } /** Implements the "index" operator. */ static class Index implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); int n = ((Number)stack.pop()).intValue(); if (n < 0) { throw new IllegalArgumentException("rangecheck: " + n); } int size = stack.size(); stack.push(stack.get(size - n - 1)); } } /** Implements the "pop" operator. */ static class Pop implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); stack.pop(); } } /** Implements the "roll" operator. */ static class Roll implements Operator { public void execute(ExecutionContext context) { Stack stack = context.getStack(); int j = ((Number)stack.pop()).intValue(); int n = ((Number)stack.pop()).intValue(); if (j == 0) { return; //Nothing to do } if (n < 0) { throw new IllegalArgumentException("rangecheck: " + n); } LinkedList rolled = new LinkedList(); LinkedList moved = new LinkedList(); if (j < 0) { //negative roll int n1 = n + j; for (int i = 0; i < n1; i++) { moved.addFirst(stack.pop()); } for (int i = j; i < 0; i++) { rolled.addFirst(stack.pop()); } stack.addAll(moved); stack.addAll(rolled); } else { //positive roll int n1 = n - j; for (int i = j; i > 0; i--) { rolled.addFirst(stack.pop()); } for (int i = 0; i < n1; i++) { moved.addFirst(stack.pop()); } stack.addAll(rolled); stack.addAll(moved); } } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/common/function/type4/package.html000066400000000000000000000016741320103431700307670ustar00rootroot00000000000000 This package contains Type 4 function support. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/000077500000000000000000000000001320103431700263425ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/000077500000000000000000000000001320103431700317355ustar00rootroot00000000000000PDAttributeObject.java000066400000000000000000000157401320103431700360460ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; import org.sejda.sambox.pdmodel.documentinterchange.taggedpdf.PDExportFormatAttributeObject; import org.sejda.sambox.pdmodel.documentinterchange.taggedpdf.PDLayoutAttributeObject; import org.sejda.sambox.pdmodel.documentinterchange.taggedpdf.PDListAttributeObject; import org.sejda.sambox.pdmodel.documentinterchange.taggedpdf.PDPrintFieldAttributeObject; import org.sejda.sambox.pdmodel.documentinterchange.taggedpdf.PDTableAttributeObject; /** * An attribute object. * * @author Johannes Koch * */ public abstract class PDAttributeObject extends PDDictionaryWrapper { /** * Creates an attribute object. * * @param dictionary the dictionary * @return the attribute object */ public static PDAttributeObject create(COSDictionary dictionary) { String owner = dictionary.getNameAsString(COSName.O); if (PDUserAttributeObject.OWNER_USER_PROPERTIES.equals(owner)) { return new PDUserAttributeObject(dictionary); } else if (PDListAttributeObject.OWNER_LIST.equals(owner)) { return new PDListAttributeObject(dictionary); } else if (PDPrintFieldAttributeObject.OWNER_PRINT_FIELD.equals(owner)) { return new PDPrintFieldAttributeObject(dictionary); } else if (PDTableAttributeObject.OWNER_TABLE.equals(owner)) { return new PDTableAttributeObject(dictionary); } else if (PDLayoutAttributeObject.OWNER_LAYOUT.equals(owner)) { return new PDLayoutAttributeObject(dictionary); } else if (PDExportFormatAttributeObject.OWNER_XML_1_00.equals(owner) || PDExportFormatAttributeObject.OWNER_HTML_3_20.equals(owner) || PDExportFormatAttributeObject.OWNER_HTML_4_01.equals(owner) || PDExportFormatAttributeObject.OWNER_OEB_1_00.equals(owner) || PDExportFormatAttributeObject.OWNER_RTF_1_05.equals(owner) || PDExportFormatAttributeObject.OWNER_CSS_1_00.equals(owner) || PDExportFormatAttributeObject.OWNER_CSS_2_00.equals(owner)) { return new PDExportFormatAttributeObject(dictionary); } return new PDDefaultAttributeObject(dictionary); } private PDStructureElement structureElement; /** * Gets the structure element. * * @return the structure element */ private PDStructureElement getStructureElement() { return this.structureElement; } /** * Sets the structure element. * * @param structureElement the structure element */ protected void setStructureElement(PDStructureElement structureElement) { this.structureElement = structureElement; } /** * Default constructor. */ public PDAttributeObject() { } /** * Creates a new attribute object with a given dictionary. * * @param dictionary the dictionary */ public PDAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Returns the owner of the attributes. * * @return the owner of the attributes */ public String getOwner() { return this.getCOSObject().getNameAsString(COSName.O); } /** * Sets the owner of the attributes. * * @param owner the owner of the attributes */ protected void setOwner(String owner) { this.getCOSObject().setName(COSName.O, owner); } /** * Detects whether there are no properties in the attribute object. * * @return true if the attribute object is empty, * false otherwise */ public boolean isEmpty() { // only entry is the owner? return (this.getCOSObject().size() == 1) && (this.getOwner() != null); } /** * Notifies the attribute object change listeners if the attribute is changed. * * @param oldBase old value * @param newBase new value */ protected void potentiallyNotifyChanged(COSBase oldBase, COSBase newBase) { if (this.isValueChanged(oldBase, newBase)) { this.notifyChanged(); } } /** * Is the value changed? * * @param oldValue old value * @param newValue new value * @return true if the value is changed, false * otherwise */ private boolean isValueChanged(COSBase oldValue, COSBase newValue) { if (oldValue == null) { return newValue != null; } return !oldValue.equals(newValue); } /** * Notifies the attribute object change listeners about a change in this * attribute object. */ protected void notifyChanged() { if (this.getStructureElement() != null) { this.getStructureElement().attributeChanged(this); } } @Override public String toString() { return "O=" + this.getOwner(); } /** * Creates a String representation of an Object array. * * @param array the Object array * @return the String representation */ protected static String arrayToString(Object[] array) { StringBuilder sb = new StringBuilder("["); for (int i = 0; i < array.length; i++) { if (i > 0) { sb.append(", "); } sb.append(array[i]); } return sb.append(']').toString(); } /** * Creates a String representation of a float array. * * @param array the float array * @return the String representation */ protected static String arrayToString(float[] array) { StringBuilder sb = new StringBuilder("["); for (int i = 0; i < array.length; i++) { if (i > 0) { sb.append(", "); } sb.append(array[i]); } return sb.append(']').toString(); } } PDDefaultAttributeObject.java000066400000000000000000000073051320103431700373510ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * A default attribute object. * * @author Johannes Koch */ public class PDDefaultAttributeObject extends PDAttributeObject { /** * Default constructor. */ public PDDefaultAttributeObject() { } /** * Creates a default attribute object with a given dictionary. * * @param dictionary the dictionary */ public PDDefaultAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Gets the attribute names. * * @return the attribute names */ public List getAttributeNames() { List attrNames = new ArrayList(); for (Entry entry : this.getCOSObject().entrySet()) { COSName key = entry.getKey(); if (!COSName.O.equals(key)) { attrNames.add(key.getName()); } } return attrNames; } /** * Gets the attribute value for a given name. * * @param attrName the given attribute name * @return the attribute value for a given name */ public COSBase getAttributeValue(String attrName) { return this.getCOSObject().getDictionaryObject(attrName); } /** * Gets the attribute value for a given name. * * @param attrName the given attribute name * @param defaultValue the default value * @return the attribute value for a given name */ protected COSBase getAttributeValue(String attrName, COSBase defaultValue) { COSBase value = this.getCOSObject().getDictionaryObject(attrName); if (value == null) { return defaultValue; } return value; } /** * Sets an attribute. * * @param attrName the attribute name * @param attrValue the attribute value */ public void setAttribute(String attrName, COSBase attrValue) { COSBase old = this.getAttributeValue(attrName); this.getCOSObject().setItem(COSName.getPDFName(attrName), attrValue); this.potentiallyNotifyChanged(old, attrValue); } @Override public String toString() { StringBuilder sb = new StringBuilder().append(super.toString()) .append(", attributes={"); Iterator it = this.getAttributeNames().iterator(); while (it.hasNext()) { String name = it.next(); sb.append(name).append('=').append(this.getAttributeValue(name)); if (it.hasNext()) { sb.append(", "); } } return sb.append('}').toString(); } } PDMarkInfo.java000066400000000000000000000065151320103431700344620ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSObjectable; /** * The MarkInfo provides additional information relevant to specialized * uses of structured documents. * * @author Ben Litchfield */ public class PDMarkInfo implements COSObjectable { private final COSDictionary dictionary; /** * Default Constructor. * */ public PDMarkInfo() { dictionary = new COSDictionary(); } /** * Constructor for an existing MarkInfo element. * * @param dic The existing dictionary. */ public PDMarkInfo( COSDictionary dic ) { dictionary = dic; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return dictionary; } /** * Tells if this is a tagged PDF. * * @return true If this is a tagged PDF. */ public boolean isMarked() { return dictionary.getBoolean( "Marked", false ); } /** * Set if this is a tagged PDF. * * @param value The new marked value. */ public void setMarked( boolean value ) { dictionary.setBoolean( "Marked", value ); } /** * Tells if structure elements use user properties. * * @return A boolean telling if the structure elements use user properties. */ public boolean usesUserProperties() { return dictionary.getBoolean( "UserProperties", false ); } /** * Set if the structure elements contain user properties. * * @param userProps The new value for this property. */ public void setUserProperties( boolean userProps ) { dictionary.setBoolean( "UserProperties", userProps ); } /** * Tells if this PDF contain 'suspect' tags. See PDF Reference 1.6 * section 10.6 "Logical Structure" for more information about this property. * * @return true if the suspect flag has been set. */ public boolean isSuspect() { return dictionary.getBoolean( "Suspects", false ); } /** * Set the value of the suspects property. See PDF Reference 1.6 * section 10.6 "Logical Structure" for more information about this * property. * * @param suspect The new "Suspects" value. */ public void setSuspect( boolean suspect ) { dictionary.setBoolean( "Suspects", false ); } } PDMarkedContentReference.java000066400000000000000000000056271320103431700373340ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.PDPage; /** * A marked-content reference. * * @author Johannes Koch */ public class PDMarkedContentReference implements COSObjectable { public static final String TYPE = "MCR"; private final COSDictionary dictionary; /** * {@inheritDoc} */ @Override public COSDictionary getCOSObject() { return this.dictionary; } /** * Default constructor */ public PDMarkedContentReference() { this.dictionary = new COSDictionary(); this.dictionary.setName(COSName.TYPE, TYPE); } /** * Constructor for an existing marked content reference. * * @param dictionary the page dictionary */ public PDMarkedContentReference(COSDictionary dictionary) { this.dictionary = dictionary; } /** * Gets the page. * * @return the page */ public PDPage getPage() { COSDictionary pg = (COSDictionary) this.getCOSObject().getDictionaryObject(COSName.PG); if (pg != null) { return new PDPage(pg); } return null; } /** * Sets the page. * * @param page the page */ public void setPage(PDPage page) { this.getCOSObject().setItem(COSName.PG, page); } /** * Gets the marked content identifier. * * @return the marked content identifier */ public int getMCID() { return this.getCOSObject().getInt(COSName.MCID); } /** * Sets the marked content identifier. * * @param mcid the marked content identifier */ public void setMCID(int mcid) { this.getCOSObject().setInt(COSName.MCID, mcid); } @Override public String toString() { return "mcid=" + this.getMCID(); } } PDObjectReference.java000066400000000000000000000077351320103431700360060ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotation; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationUnknown; /** * An object reference. * * @author Johannes Koch */ public class PDObjectReference extends PDDictionaryWrapper { /** * TYPE of this object. */ public static final String TYPE = "OBJR"; /** * Default Constructor. * */ public PDObjectReference() { this.getCOSObject().setName(COSName.TYPE, TYPE); } public PDObjectReference(COSDictionary dictionary) { super(dictionary); } /** * Gets a higher-level object for the referenced object. * Currently this method may return a {@link PDAnnotation}, * a {@link PDXObject} or null. * * @return a higher-level object for the referenced object */ public COSObjectable getReferencedObject() { COSBase obj = this.getCOSObject().getDictionaryObject(COSName.OBJ); if (!(obj instanceof COSDictionary)) { return null; } try { if (obj instanceof COSStream) { PDXObject xobject = PDXObject.createXObject(obj, null); // <-- TODO: valid? if (xobject != null) { return xobject; } } COSDictionary objDictionary = (COSDictionary)obj; PDAnnotation annotation = PDAnnotation.createAnnotation(obj); /* * COSName.TYPE is optional, so if annotation is of type unknown and * COSName.TYPE is not COSName.ANNOT it still may be an annotation. * TODO shall we return the annotation object instead of null? * what else can be the target of the object reference? */ if (!(annotation instanceof PDAnnotationUnknown) || COSName.ANNOT.equals(objDictionary.getDictionaryObject(COSName.TYPE))) { return annotation; } } catch (IOException exception) { // this can only happen if the target is an XObject. } return null; } /** * Sets the referenced annotation. * * @param annotation the referenced annotation */ public void setReferencedObject(PDAnnotation annotation) { this.getCOSObject().setItem(COSName.OBJ, annotation); } /** * Sets the referenced XObject. * * @param xobject the referenced XObject */ public void setReferencedObject(PDXObject xobject) { this.getCOSObject().setItem(COSName.OBJ, xobject); } } PDStructureElement.java000066400000000000000000000503161320103431700362640ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import java.util.Iterator; import java.util.Map; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; /** * A structure element. * * @author Ben Litchfield * @author Johannes Koch */ public class PDStructureElement extends PDStructureNode { public static final String TYPE = "StructElem"; /** * Constructor with required values. * * @param structureType the structure type * @param parent the parent structure node */ public PDStructureElement(String structureType, PDStructureNode parent) { super(TYPE); this.setStructureType(structureType); this.setParent(parent); } /** * Constructor for an existing structure element. * * @param dic The existing dictionary. */ public PDStructureElement( COSDictionary dic ) { super(dic); } /** * Returns the structure type (S). * * @return the structure type */ public String getStructureType() { return this.getCOSObject().getNameAsString(COSName.S); } /** * Sets the structure type (S). * * @param structureType the structure type */ public final void setStructureType(String structureType) { this.getCOSObject().setName(COSName.S, structureType); } /** * Returns the parent in the structure hierarchy (P). * * @return the parent in the structure hierarchy */ public PDStructureNode getParent() { COSDictionary p = (COSDictionary) this.getCOSObject().getDictionaryObject(COSName.P); if (p == null) { return null; } return PDStructureNode.create(p); } /** * Sets the parent in the structure hierarchy (P). * * @param structureNode the parent in the structure hierarchy */ public final void setParent(PDStructureNode structureNode) { this.getCOSObject().setItem(COSName.P, structureNode); } /** * Returns the element identifier (ID). * * @return the element identifier */ public String getElementIdentifier() { return this.getCOSObject().getString(COSName.ID); } /** * Sets the element identifier (ID). * * @param id the element identifier */ public void setElementIdentifier(String id) { this.getCOSObject().setString(COSName.ID, id); } /** * Returns the page on which some or all of the content items designated by * the K entry shall be rendered (Pg). * * @return the page on which some or all of the content items designated by * the K entry shall be rendered */ public PDPage getPage() { COSDictionary pageDic = (COSDictionary) this.getCOSObject().getDictionaryObject(COSName.PG); if (pageDic == null) { return null; } return new PDPage(pageDic); } /** * Sets the page on which some or all of the content items designated by * the K entry shall be rendered (Pg). * @param page the page on which some or all of the content items designated * by the K entry shall be rendered. */ public void setPage(PDPage page) { this.getCOSObject().setItem(COSName.PG, page); } /** * Returns the attributes together with their revision numbers (A). * * @return the attributes */ public Revisions getAttributes() { Revisions attributes = new Revisions(); COSBase a = this.getCOSObject().getDictionaryObject(COSName.A); if (a instanceof COSArray) { COSArray aa = (COSArray) a; Iterator it = aa.iterator(); PDAttributeObject ao = null; while (it.hasNext()) { COSBase item = it.next(); if (item instanceof COSDictionary) { ao = PDAttributeObject.create((COSDictionary) item); ao.setStructureElement(this); attributes.addObject(ao, 0); } else if (item instanceof COSInteger) { attributes.setRevisionNumber(ao, ((COSInteger) item).intValue()); } } } if (a instanceof COSDictionary) { PDAttributeObject ao = PDAttributeObject.create((COSDictionary) a); ao.setStructureElement(this); attributes.addObject(ao, 0); } return attributes; } /** * Sets the attributes together with their revision numbers (A). * * @param attributes the attributes */ public void setAttributes(Revisions attributes) { COSName key = COSName.A; if ((attributes.size() == 1) && (attributes.getRevisionNumber(0) == 0)) { PDAttributeObject attributeObject = attributes.getObject(0); attributeObject.setStructureElement(this); this.getCOSObject().setItem(key, attributeObject); return; } COSArray array = new COSArray(); for (int i = 0; i < attributes.size(); i++) { PDAttributeObject attributeObject = attributes.getObject(i); attributeObject.setStructureElement(this); int revisionNumber = attributes.getRevisionNumber(i); if (revisionNumber < 0) { throw new IllegalArgumentException("The revision number shall be > -1"); } array.add(attributeObject); array.add(COSInteger.get(revisionNumber)); } this.getCOSObject().setItem(key, array); } /** * Adds an attribute object. * * @param attributeObject the attribute object */ public void addAttribute(PDAttributeObject attributeObject) { COSName key = COSName.A; attributeObject.setStructureElement(this); COSBase a = this.getCOSObject().getDictionaryObject(key); COSArray array; if (a instanceof COSArray) { array = (COSArray) a; } else { array = new COSArray(); if (a != null) { array.add(a); array.add(COSInteger.get(0)); } } this.getCOSObject().setItem(key, array); array.add(attributeObject); array.add(COSInteger.get(this.getRevisionNumber())); } /** * Removes an attribute object. * * @param attributeObject the attribute object */ public void removeAttribute(PDAttributeObject attributeObject) { COSName key = COSName.A; COSBase a = this.getCOSObject().getDictionaryObject(key); if (a instanceof COSArray) { COSArray array = (COSArray) a; array.remove(attributeObject.getCOSObject()); if ((array.size() == 2) && (array.getInt(1) == 0)) { this.getCOSObject().setItem(key, array.getObject(0)); } } else { if (attributeObject.getCOSObject().equals(a.getCOSObject())) { this.getCOSObject().removeItem(key); } } attributeObject.setStructureElement(null); } /** * Updates the revision number for the given attribute object. * * @param attributeObject the attribute object */ public void attributeChanged(PDAttributeObject attributeObject) { COSName key = COSName.A; COSBase a = this.getCOSObject().getDictionaryObject(key); if (a instanceof COSArray) { COSArray array = (COSArray) a; for (int i = 0; i < array.size(); i++) { COSBase entry = array.getObject(i); if (entry.equals(attributeObject.getCOSObject())) { COSBase next = array.get(i + 1); if (next instanceof COSInteger) { array.set(i + 1, COSInteger.get(this.getRevisionNumber())); } } } } else { COSArray array = new COSArray(); array.add(a); array.add(COSInteger.get(this.getRevisionNumber())); this.getCOSObject().setItem(key, array); } } /** * Returns the class names together with their revision numbers (C). * * @return the class names */ public Revisions getClassNames() { COSName key = COSName.C; Revisions classNames = new Revisions(); COSBase c = this.getCOSObject().getDictionaryObject(key); if (c instanceof COSName) { classNames.addObject(((COSName) c).getName(), 0); } if (c instanceof COSArray) { COSArray array = (COSArray) c; Iterator it = array.iterator(); String className = null; while (it.hasNext()) { COSBase item = it.next(); if (item instanceof COSName) { className = ((COSName) item).getName(); classNames.addObject(className, 0); } else if (item instanceof COSInteger) { classNames.setRevisionNumber(className, ((COSInteger) item).intValue()); } } } return classNames; } /** * Sets the class names together with their revision numbers (C). * * @param classNames the class names */ public void setClassNames(Revisions classNames) { if (classNames == null) { return; } COSName key = COSName.C; if ((classNames.size() == 1) && (classNames.getRevisionNumber(0) == 0)) { String className = classNames.getObject(0); this.getCOSObject().setName(key, className); return; } COSArray array = new COSArray(); for (int i = 0; i < classNames.size(); i++) { String className = classNames.getObject(i); int revisionNumber = classNames.getRevisionNumber(i); if (revisionNumber < 0) { throw new IllegalArgumentException("The revision number shall be > -1"); } array.add(COSName.getPDFName(className)); array.add(COSInteger.get(revisionNumber)); } this.getCOSObject().setItem(key, array); } /** * Adds a class name. * * @param className the class name */ public void addClassName(String className) { if (className == null) { return; } COSName key = COSName.C; COSBase c = this.getCOSObject().getDictionaryObject(key); COSArray array; if (c instanceof COSArray) { array = (COSArray) c; } else { array = new COSArray(); if (c != null) { array.add(c); array.add(COSInteger.get(0)); } } this.getCOSObject().setItem(key, array); array.add(COSName.getPDFName(className)); array.add(COSInteger.get(this.getRevisionNumber())); } /** * Removes a class name. * * @param className the class name */ public void removeClassName(String className) { if (className == null) { return; } COSName key = COSName.C; COSBase c = this.getCOSObject().getDictionaryObject(key); COSName name = COSName.getPDFName(className); if (c instanceof COSArray) { COSArray array = (COSArray) c; array.remove(name); if ((array.size() == 2) && (array.getInt(1) == 0)) { this.getCOSObject().setItem(key, array.getObject(0)); } } else { if (name.equals(c.getCOSObject())) { this.getCOSObject().removeItem(key); } } } /** * Returns the revision number (R). * * @return the revision number */ public int getRevisionNumber() { return this.getCOSObject().getInt(COSName.R, 0); } /** * Sets the revision number (R). * * @param revisionNumber the revision number */ public void setRevisionNumber(int revisionNumber) { if (revisionNumber < 0) { throw new IllegalArgumentException("The revision number shall be > -1"); } this.getCOSObject().setInt(COSName.R, revisionNumber); } /** * Increments th revision number. */ public void incrementRevisionNumber() { this.setRevisionNumber(this.getRevisionNumber() + 1); } /** * Returns the title (T). * * @return the title */ public String getTitle() { return this.getCOSObject().getString(COSName.T); } /** * Sets the title (T). * * @param title the title */ public void setTitle(String title) { this.getCOSObject().setString(COSName.T, title); } /** * Returns the language (Lang). * * @return the language */ public String getLanguage() { return this.getCOSObject().getString(COSName.LANG); } /** * Sets the language (Lang). * * @param language the language */ public void setLanguage(String language) { this.getCOSObject().setString(COSName.LANG, language); } /** * Returns the alternate description (Alt). * * @return the alternate description */ public String getAlternateDescription() { return this.getCOSObject().getString(COSName.ALT); } /** * Sets the alternate description (Alt). * * @param alternateDescription the alternate description */ public void setAlternateDescription(String alternateDescription) { this.getCOSObject().setString(COSName.ALT, alternateDescription); } /** * Returns the expanded form (E). * * @return the expanded form */ public String getExpandedForm() { return this.getCOSObject().getString(COSName.E); } /** * Sets the expanded form (E). * * @param expandedForm the expanded form */ public void setExpandedForm(String expandedForm) { this.getCOSObject().setString(COSName.E, expandedForm); } /** * Returns the actual text (ActualText). * * @return the actual text */ public String getActualText() { return this.getCOSObject().getString(COSName.ACTUAL_TEXT); } /** * Sets the actual text (ActualText). * * @param actualText the actual text */ public void setActualText(String actualText) { this.getCOSObject().setString(COSName.ACTUAL_TEXT, actualText); } /** * Returns the standard structure type, the actual structure type is mapped * to in the role map. * * @return the standard structure type */ public String getStandardStructureType() { String type = this.getStructureType(); Map roleMap = getRoleMap(); if (roleMap.containsKey(type)) { Object mappedValue = getRoleMap().get(type); if (mappedValue instanceof String) { type = (String)mappedValue; } } return type; } /** * Appends a marked-content sequence kid. * * @param markedContent the marked-content sequence */ public void appendKid(PDMarkedContent markedContent) { if (markedContent == null) { return; } this.appendKid(COSInteger.get(markedContent.getMCID())); } /** * Appends a marked-content reference kid. * * @param markedContentReference the marked-content reference */ public void appendKid(PDMarkedContentReference markedContentReference) { this.appendObjectableKid(markedContentReference); } /** * Appends an object reference kid. * * @param objectReference the object reference */ public void appendKid(PDObjectReference objectReference) { this.appendObjectableKid(objectReference); } /** * Inserts a marked-content identifier kid before a reference kid. * * @param markedContentIdentifier the marked-content identifier * @param refKid the reference kid */ public void insertBefore(COSInteger markedContentIdentifier, Object refKid) { this.insertBefore((COSBase) markedContentIdentifier, refKid); } /** * Inserts a marked-content reference kid before a reference kid. * * @param markedContentReference the marked-content reference * @param refKid the reference kid */ public void insertBefore(PDMarkedContentReference markedContentReference, Object refKid) { this.insertObjectableBefore(markedContentReference, refKid); } /** * Inserts an object reference kid before a reference kid. * * @param objectReference the object reference * @param refKid the reference kid */ public void insertBefore(PDObjectReference objectReference, Object refKid) { this.insertObjectableBefore(objectReference, refKid); } /** * Removes a marked-content identifier kid. * * @param markedContentIdentifier the marked-content identifier */ public void removeKid(COSInteger markedContentIdentifier) { this.removeKid((COSBase) markedContentIdentifier); } /** * Removes a marked-content reference kid. * * @param markedContentReference the marked-content reference */ public void removeKid(PDMarkedContentReference markedContentReference) { this.removeObjectableKid(markedContentReference); } /** * Removes an object reference kid. * * @param objectReference the object reference */ public void removeKid(PDObjectReference objectReference) { this.removeObjectableKid(objectReference); } /** * Returns the structure tree root. * * @return the structure tree root */ private PDStructureTreeRoot getStructureTreeRoot() { PDStructureNode parent = this.getParent(); while (parent instanceof PDStructureElement) { parent = ((PDStructureElement) parent).getParent(); } if (parent instanceof PDStructureTreeRoot) { return (PDStructureTreeRoot) parent; } return null; } /** * Returns the role map. * * @return the role map */ private Map getRoleMap() { PDStructureTreeRoot root = this.getStructureTreeRoot(); if (root != null) { return root.getRoleMap(); } return null; } } PDStructureNode.java000066400000000000000000000270131320103431700355560ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import static java.util.Objects.nonNull; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * A node in the structure tree. * * @author Johannes Koch */ public abstract class PDStructureNode implements COSObjectable { /** * Creates a node in the structure tree. Can be either a structure tree root, or a structure element. * * @param node the node dictionary * @return the structure node */ public static PDStructureNode create(COSDictionary node) { String type = node.getNameAsString(COSName.TYPE); if ("StructTreeRoot".equals(type)) { return new PDStructureTreeRoot(node); } if ((type == null) || "StructElem".equals(type)) { return new PDStructureElement(node); } throw new IllegalArgumentException( "Dictionary must not include a Type entry with a value that is neither StructTreeRoot nor StructElem."); } private final COSDictionary dictionary; @Override public COSDictionary getCOSObject() { return dictionary; } /** * Constructor. * * @param type the type */ protected PDStructureNode(String type) { this.dictionary = new COSDictionary(); this.dictionary.setName(COSName.TYPE, type); } /** * Constructor for an existing structure node. * * @param dictionary The existing dictionary. */ protected PDStructureNode(COSDictionary dictionary) { this.dictionary = dictionary; } /** * Returns the type. * * @return the type */ public String getType() { return this.getCOSObject().getNameAsString(COSName.TYPE); } /** * Returns a list of objects for the kids (K). * * @return a list of objects for the kids */ public List getKids() { List kidObjects = new ArrayList<>(); COSArray k = this.getCOSObject().getDictionaryObject(COSName.K, COSArray.class); if (nonNull(k)) { for (COSBase kid : k) { Object kidObject = this.createObject(kid); if (kidObject != null) { kidObjects.add(kidObject); } } } else { Object kidObject = this.createObject(k); if (kidObject != null) { kidObjects.add(kidObject); } } return kidObjects; } /** * Sets the kids (K). * * @param kids the kids */ public void setKids(List kids) { this.getCOSObject().setItem(COSName.K, COSArrayList.converterToCOSArray(kids)); } /** * Appends a structure element kid. * * @param structureElement the structure element */ public void appendKid(PDStructureElement structureElement) { this.appendObjectableKid(structureElement); structureElement.setParent(this); } /** * Appends an objectable kid. * * @param objectable the objectable */ protected void appendObjectableKid(COSObjectable objectable) { if (objectable == null) { return; } this.appendKid(objectable.getCOSObject()); } /** * Appends a COS base kid. * * @param object the COS base */ protected void appendKid(COSBase object) { if (object == null) { return; } COSBase k = this.getCOSObject().getDictionaryObject(COSName.K); if (k == null) { // currently no kid: set new kid as kids this.getCOSObject().setItem(COSName.K, object); } else if (k instanceof COSArray) { // currently more than one kid: add new kid to existing array COSArray array = (COSArray) k; array.add(object); } else { // currently one kid: put current and new kid into array and set array as kids COSArray array = new COSArray(); array.add(k); array.add(object); this.getCOSObject().setItem(COSName.K, array); } } /** * Inserts a structure element kid before a reference kid. * * @param newKid the structure element * @param refKid the reference kid */ public void insertBefore(PDStructureElement newKid, Object refKid) { this.insertObjectableBefore(newKid, refKid); } /** * Inserts an objectable kid before a reference kid. * * @param newKid the objectable * @param refKid the reference kid */ protected void insertObjectableBefore(COSObjectable newKid, Object refKid) { if (newKid == null) { return; } this.insertBefore(newKid.getCOSObject(), refKid); } /** * Inserts an COS base kid before a reference kid. * * @param newKid the COS base * @param refKid the reference kid */ protected void insertBefore(COSBase newKid, Object refKid) { if (newKid == null || refKid == null) { return; } COSBase k = this.getCOSObject().getDictionaryObject(COSName.K); if (k == null) { return; } COSBase refKidBase = null; if (refKid instanceof COSObjectable) { refKidBase = ((COSObjectable) refKid).getCOSObject(); } else if (refKid instanceof COSInteger) { refKidBase = (COSBase) refKid; } if (k instanceof COSArray) { COSArray array = (COSArray) k; int refIndex = array.indexOfObject(refKidBase); array.add(refIndex, newKid.getCOSObject()); } else { boolean onlyKid = k.equals(refKidBase); if (onlyKid) { COSArray array = new COSArray(); array.add(newKid); array.add(refKidBase); this.getCOSObject().setItem(COSName.K, array); } } } /** * Removes a structure element kid. * * @param structureElement the structure element * @return true if the kid was removed, false otherwise */ public boolean removeKid(PDStructureElement structureElement) { boolean removed = this.removeObjectableKid(structureElement); if (removed) { structureElement.setParent(null); } return removed; } /** * Removes an objectable kid. * * @param objectable the objectable * @return true if the kid was removed, false otherwise */ protected boolean removeObjectableKid(COSObjectable objectable) { if (objectable == null) { return false; } return this.removeKid(objectable.getCOSObject()); } /** * Removes a COS base kid. * * @param object the COS base * @return true if the kid was removed, false otherwise */ protected boolean removeKid(COSBase object) { if (object == null) { return false; } COSBase k = this.getCOSObject().getDictionaryObject(COSName.K); if (k == null) { // no kids: objectable is not a kid return false; } else if (k instanceof COSArray) { // currently more than one kid: remove kid from existing array COSArray array = (COSArray) k; boolean removed = array.removeObject(object); // if now only one kid: set remaining kid as kids if (array.size() == 1) { this.getCOSObject().setItem(COSName.K, array.getObject(0)); } return removed; } else { // currently one kid: if current kid equals given object, remove kids entry boolean onlyKid = k.equals(object.getCOSObject()); if (onlyKid) { this.getCOSObject().removeItem(COSName.K); return true; } return false; } } /** * Creates an object for a kid of this structure node. The type of object depends on the type of the kid. It can be *
    *
  • a {@link PDStructureElement},
  • *
  • a {@link PDObjectReference},
  • *
  • a {@link PDMarkedContentReference},
  • *
  • an {@link Integer}
  • *
* * @param kid the kid * @return the object */ protected Object createObject(COSBase kid) { COSBase direct = kid.getCOSObject(); COSDictionary kidDic = null; if (direct instanceof COSDictionary) { kidDic = (COSDictionary) direct; } if (kidDic != null) { return createObjectFromDic(kidDic); } else if (kid instanceof COSInteger) { // An integer marked-content identifier denoting a marked-content sequence COSInteger mcid = (COSInteger) kid; return mcid.intValue(); } return null; } private COSObjectable createObjectFromDic(COSDictionary kidDic) { String type = kidDic.getNameAsString(COSName.TYPE); if ((type == null) || PDStructureElement.TYPE.equals(type)) { // A structure element dictionary denoting another structure element return new PDStructureElement(kidDic); } else if (PDObjectReference.TYPE.equals(type)) { // An object reference dictionary denoting a PDF object return new PDObjectReference(kidDic); } else if (PDMarkedContentReference.TYPE.equals(type)) { // A marked-content reference dictionary denoting a marked-content sequence return new PDMarkedContentReference(kidDic); } return null; } } PDStructureTreeRoot.java000066400000000000000000000133341320103431700364350ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import java.io.IOException; import java.util.Hashtable; import java.util.Map; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDStructureElementNameTreeNode; import org.sejda.sambox.pdmodel.common.COSDictionaryMap; import org.sejda.sambox.pdmodel.common.PDNameTreeNode; import org.sejda.sambox.pdmodel.common.PDNumberTreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A root of a structure tree. * * @author Ben Litchfield * @author Johannes Koch * */ public class PDStructureTreeRoot extends PDStructureNode { /** * Log instance. */ private static final Logger LOG = LoggerFactory.getLogger(PDStructureTreeRoot.class); private static final String TYPE = "StructTreeRoot"; /** * Default Constructor. * */ public PDStructureTreeRoot() { super(TYPE); } /** * Constructor for an existing structure element. * * @param dic The existing dictionary. */ public PDStructureTreeRoot(COSDictionary dic) { super(dic); } /** * Returns the K array entry. * * @return the K array entry */ public COSArray getKArray() { COSBase k = this.getCOSObject().getDictionaryObject(COSName.K); if (k != null) { if (k instanceof COSDictionary) { COSDictionary kdict = (COSDictionary) k; k = kdict.getDictionaryObject(COSName.K); if (k instanceof COSArray) { return (COSArray) k; } } else { return (COSArray) k; } } return null; } /** * Returns the K entry. * * @return the K entry */ public COSBase getK() { return this.getCOSObject().getDictionaryObject(COSName.K); } /** * Sets the K entry. * * @param k the K value */ public void setK(COSBase k) { this.getCOSObject().setItem(COSName.K, k); } /** * Returns the ID tree. * * @return the ID tree */ public PDNameTreeNode getIDTree() { COSDictionary idTreeDic = (COSDictionary) this.getCOSObject().getDictionaryObject(COSName.ID_TREE); if (idTreeDic != null) { return new PDStructureElementNameTreeNode(idTreeDic); } return null; } /** * Sets the ID tree. * * @param idTree the ID tree */ public void setIDTree(PDNameTreeNode idTree) { this.getCOSObject().setItem(COSName.ID_TREE, idTree); } /** * Returns the parent tree. * * @return the parent tree */ public PDNumberTreeNode getParentTree() { COSDictionary parentTreeDic = (COSDictionary) this.getCOSObject().getDictionaryObject(COSName.PARENT_TREE); if (parentTreeDic != null) { return new PDNumberTreeNode(parentTreeDic, COSBase.class); } return null; } /** * Sets the parent tree. * * @param parentTree the parent tree */ public void setParentTree(PDNumberTreeNode parentTree) { this.getCOSObject().setItem(COSName.PARENT_TREE, parentTree); } /** * Returns the next key in the parent tree. * * @return the next key in the parent tree */ public int getParentTreeNextKey() { return this.getCOSObject().getInt(COSName.PARENT_TREE_NEXT_KEY); } /** * Sets the next key in the parent tree. * * @param parentTreeNextkey the next key in the parent tree. */ public void setParentTreeNextKey(int parentTreeNextkey) { this.getCOSObject().setInt(COSName.PARENT_TREE_NEXT_KEY, parentTreeNextkey); } /** * Returns the role map. * * @return the role map */ public Map getRoleMap() { COSBase rm = this.getCOSObject().getDictionaryObject(COSName.ROLE_MAP); if (rm instanceof COSDictionary) { try { return COSDictionaryMap.convertBasicTypesToMap((COSDictionary) rm); } catch (IOException e) { LOG.error(e.getMessage(), e); } } return new Hashtable<>(); } /** * Sets the role map. * * @param roleMap the role map */ public void setRoleMap(Map roleMap) { COSDictionary rmDic = new COSDictionary(); for (Map.Entry entry : roleMap.entrySet()) { rmDic.setName(entry.getKey(), entry.getValue()); } this.getCOSObject().setItem(COSName.ROLE_MAP, rmDic); } } PDUserAttributeObject.java000066400000000000000000000070211320103431700366760ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * A User attribute object. * * @author Johannes Koch */ public class PDUserAttributeObject extends PDAttributeObject { /** * Attribute owner for user properties */ public static final String OWNER_USER_PROPERTIES = "UserProperties"; /** * Default constructor */ public PDUserAttributeObject() { this.setOwner(OWNER_USER_PROPERTIES); } /** * * @param dictionary the dictionary */ public PDUserAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Returns the user properties. * * @return the user properties */ public List getOwnerUserProperties() { COSArray p = (COSArray) this.getCOSObject() .getDictionaryObject(COSName.P); List properties = new ArrayList(p.size()); for (int i = 0; i < p.size(); i++) { properties.add( new PDUserProperty((COSDictionary) p.getObject(i), this)); } return properties; } /** * Sets the user properties. * * @param userProperties the user properties */ public void setUserProperties(List userProperties) { COSArray p = new COSArray(); for (PDUserProperty userProperty : userProperties) { p.add(userProperty); } this.getCOSObject().setItem(COSName.P, p); } /** * Adds a user property. * * @param userProperty the user property */ public void addUserProperty(PDUserProperty userProperty) { COSArray p = (COSArray) this.getCOSObject() .getDictionaryObject(COSName.P); p.add(userProperty); this.notifyChanged(); } /** * Removes a user property. * * @param userProperty the user property */ public void removeUserProperty(PDUserProperty userProperty) { if (userProperty == null) { return; } COSArray p = (COSArray) this.getCOSObject() .getDictionaryObject(COSName.P); p.remove(userProperty.getCOSObject()); this.notifyChanged(); } /** * @param userProperty */ public void userPropertyChanged(PDUserProperty userProperty) { } @Override public String toString() { return super.toString() + ", userProperties=" + this.getOwnerUserProperties(); } } PDUserProperty.java000066400000000000000000000135361320103431700354400ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; /** * A user property. * * @author Johannes Koch */ public class PDUserProperty extends PDDictionaryWrapper { private final PDUserAttributeObject userAttributeObject; /** * Creates a new user property. * * @param userAttributeObject the user attribute object */ public PDUserProperty(PDUserAttributeObject userAttributeObject) { this.userAttributeObject = userAttributeObject; } /** * Creates a user property with a given dictionary. * * @param dictionary the dictionary * @param userAttributeObject the user attribute object */ public PDUserProperty(COSDictionary dictionary, PDUserAttributeObject userAttributeObject) { super(dictionary); this.userAttributeObject = userAttributeObject; } /** * Returns the property name. * * @return the property name */ public String getName() { return this.getCOSObject().getNameAsString(COSName.N); } /** * Sets the property name. * * @param name the property name */ public void setName(String name) { this.potentiallyNotifyChanged(this.getName(), name); this.getCOSObject().setName(COSName.N, name); } /** * Returns the property value. * * @return the property value */ public COSBase getValue() { return this.getCOSObject().getDictionaryObject(COSName.V); } /** * Sets the property value. * * @param value the property value */ public void setValue(COSBase value) { this.potentiallyNotifyChanged(this.getValue(), value); this.getCOSObject().setItem(COSName.V, value); } /** * Returns the string for the property value. * * @return the string for the property value */ public String getFormattedValue() { return this.getCOSObject().getString(COSName.F); } /** * Sets the string for the property value. * * @param formattedValue the string for the property value */ public void setFormattedValue(String formattedValue) { this.potentiallyNotifyChanged(this.getFormattedValue(), formattedValue); this.getCOSObject().setString(COSName.F, formattedValue); } /** * Shall the property be hidden? * * @return true if the property shall be hidden, * false otherwise */ public boolean isHidden() { return this.getCOSObject().getBoolean(COSName.H, false); } /** * Specifies whether the property shall be hidden. * * @param hidden true if the property shall be hidden, * false otherwise */ public void setHidden(boolean hidden) { this.potentiallyNotifyChanged(this.isHidden(), hidden); this.getCOSObject().setBoolean(COSName.H, hidden); } @Override public String toString() { return "Name=" + this.getName() + ", Value=" + this.getValue() + ", FormattedValue=" + this .getFormattedValue() + ", Hidden=" + this.isHidden(); } /** * Notifies the user attribute object if the user property is changed. * * @param oldEntry old entry * @param newEntry new entry */ private void potentiallyNotifyChanged(Object oldEntry, Object newEntry) { if (this.isEntryChanged(oldEntry, newEntry)) { this.userAttributeObject.userPropertyChanged(this); } } /** * Is the value changed? * * @param oldEntry old entry * @param newEntry new entry * @return true if the entry is changed, false * otherwise */ private boolean isEntryChanged(Object oldEntry, Object newEntry) { if (oldEntry == null) { return newEntry != null; } return !oldEntry.equals(newEntry); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((userAttributeObject == null) ? 0 : userAttributeObject.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } PDUserProperty other = (PDUserProperty) obj; if (userAttributeObject == null) { if (other.userAttributeObject != null) { return false; } } else if (!userAttributeObject.equals(other.userAttributeObject)) { return false; } return true; } }Revisions.java000066400000000000000000000067761320103431700345220ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.logicalstructure; import java.util.ArrayList; import java.util.List; /** * * @author Johannes Koch * @param the type of object to store the revision numbers with */ public class Revisions { private List objects; private List revisionNumbers; private List getObjects() { if (this.objects == null) { this.objects = new ArrayList(); } return this.objects; } private List getRevisionNumbers() { if (this.revisionNumbers == null) { this.revisionNumbers = new ArrayList(); } return this.revisionNumbers; } /** * */ public Revisions() { } /** * Returns the object at the specified position. * * @param index the position * @return the object * @throws IndexOutOfBoundsException if the index is out of range */ public T getObject(int index) { return this.getObjects().get(index); } /** * Returns the revision number at the specified position. * * @param index the position * @return the revision number * @throws IndexOutOfBoundsException if the index is out of range */ public int getRevisionNumber(int index) { return this.getRevisionNumbers().get(index); } /** * Adds an object with a specified revision number. * * @param object the object * @param revisionNumber the revision number */ public void addObject(T object, int revisionNumber) { this.getObjects().add(object); this.getRevisionNumbers().add(revisionNumber); } /** * Sets the revision number of a specified object. * * @param object the object * @param revisionNumber the revision number */ protected void setRevisionNumber(T object, int revisionNumber) { int index = this.getObjects().indexOf(object); if (index > -1) { this.getRevisionNumbers().set(index, revisionNumber); } } /** * Returns the size. * * @return the size */ public int size() { return this.getObjects().size(); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < this.getObjects().size(); i++) { if (i > 0) { sb.append("; "); } sb.append("object=").append(this.getObjects().get(i)) .append(", revisionNumber=").append(this.getRevisionNumber(i)); } return sb.toString(); } } package.html000066400000000000000000000020251320103431700341360ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/logicalstructure The logical structure package provides a mechanism for incorporating structural information about a document's content into a PDF file. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/markedcontent/000077500000000000000000000000001320103431700312005ustar00rootroot00000000000000PDMarkedContent.java000066400000000000000000000116161320103431700347530ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/markedcontent/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.markedcontent; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.documentinterchange.taggedpdf.PDArtifactMarkedContent; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.text.TextPosition; /** * A marked content. * * @author Johannes Koch */ public class PDMarkedContent { /** * Creates a marked-content sequence. * * @param tag the tag * @param properties the properties * @return the marked-content sequence */ public static PDMarkedContent create(COSName tag, COSDictionary properties) { if (COSName.ARTIFACT.equals(tag)) { return new PDArtifactMarkedContent(properties); } return new PDMarkedContent(tag, properties); } private final String tag; private final COSDictionary properties; private final List contents; /** * Creates a new marked content object. * * @param tag the tag * @param properties the properties */ public PDMarkedContent(COSName tag, COSDictionary properties) { this.tag = tag == null ? null : tag.getName(); this.properties = properties; this.contents = new ArrayList(); } /** * Gets the tag. * * @return the tag */ public String getTag() { return this.tag; } /** * Gets the properties. * * @return the properties */ public COSDictionary getProperties() { return this.properties; } /** * Gets the marked-content identifier. * * @return the marked-content identifier, or -1 if it doesn't exist. */ public int getMCID() { return this.getProperties() == null ? -1 : this.getProperties().getInt(COSName.MCID); } /** * Gets the language (Lang). * * @return the language */ public String getLanguage() { return this.getProperties() == null ? null : this.getProperties().getNameAsString(COSName.LANG); } /** * Gets the actual text (ActualText). * * @return the actual text */ public String getActualText() { return this.getProperties() == null ? null : this.getProperties().getString(COSName.ACTUAL_TEXT); } /** * Gets the alternate description (Alt). * * @return the alternate description */ public String getAlternateDescription() { return this.getProperties() == null ? null : this.getProperties().getString(COSName.ALT); } /** * Gets the expanded form (E). * * @return the expanded form */ public String getExpandedForm() { return this.getProperties() == null ? null : this.getProperties().getString(COSName.E); } /** * Gets the contents of the marked content sequence. Can be *
    *
  • {@link TextPosition},
  • *
  • {@link PDMarkedContent}, or
  • *
  • {@link PDXObject}.
  • *
* * @return the contents of the marked content sequence */ public List getContents() { return this.contents; } /** * Adds a text position to the contents. * * @param text the text position */ public void addText(TextPosition text) { this.getContents().add(text); } /** * Adds a marked content to the contents. * * @param markedContent the marked content */ public void addMarkedContent(PDMarkedContent markedContent) { this.getContents().add(markedContent); } /** * Adds an XObject to the contents. * * @param xobject the XObject */ public void addXObject(PDXObject xobject) { this.getContents().add(xobject); } @Override public String toString() { return "tag=" + this.tag + ", properties=" + this.properties + ", contents=" + this.contents; } } PDPropertyList.java000066400000000000000000000040671320103431700346770ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/markedcontent/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.markedcontent; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup; /** * A property list is a dictionary containing private information meaningful to the conforming * writer creating the marked content. */ public class PDPropertyList implements COSObjectable { protected final COSDictionary dict; /** * Creates a property list from the given dictionary. * @param dict COS dictionary */ public static PDPropertyList create(COSDictionary dict) { if (COSName.OCG.equals(dict.getItem(COSName.TYPE))) { return new PDOptionalContentGroup(dict); } else { // todo: more types return new PDPropertyList(dict); } } /** * Constructor for subclasses. */ protected PDPropertyList() { this.dict = new COSDictionary(); } /** * Constructor for subclasses. */ protected PDPropertyList(COSDictionary dict) { this.dict = dict; } @Override public COSDictionary getCOSObject() { return dict; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/markedcontent/package.html000066400000000000000000000017441320103431700334670ustar00rootroot00000000000000 The marked content package provides a mechanism for modeling marked-content sequences. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/prepress/000077500000000000000000000000001320103431700302055ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/prepress/PDBoxStyle.java000066400000000000000000000126701320103431700330530ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.prepress; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.graphics.PDLineDashPattern; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; /** * The Box Style specifies visual characteristics for displaying box areas. * * @author Ben Litchfield */ public class PDBoxStyle implements COSObjectable { /** * Style for guideline. */ public static final String GUIDELINE_STYLE_SOLID = "S"; /** * Style for guideline. */ public static final String GUIDELINE_STYLE_DASHED = "D"; private final COSDictionary dictionary; /** * Default Constructor. * */ public PDBoxStyle() { dictionary = new COSDictionary(); } /** * Constructor for an existing BoxStyle element. * * @param dic The existing dictionary. */ public PDBoxStyle( COSDictionary dic ) { dictionary = dic; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return dictionary; } /** * Get the RGB color to be used for the guidelines. This is guaranteed to * not return null. The default color is [0,0,0]. * *@return The guideline color. */ public PDColor getGuidelineColor() { COSArray colorValues = (COSArray) dictionary.getDictionaryObject(COSName.C); if( colorValues == null ) { colorValues = new COSArray(); colorValues.add( COSInteger.ZERO ); colorValues.add( COSInteger.ZERO ); colorValues.add( COSInteger.ZERO ); dictionary.setItem(COSName.C, colorValues); } PDColor color = new PDColor(colorValues.toFloatArray(), PDDeviceRGB.INSTANCE); return color; } /** * Set the color space instance for this box style. This must be a * PDDeviceRGB! * * @param color The new colorspace value. */ public void setGuideLineColor( PDColor color ) { if( color != null ) { dictionary.setItem(COSName.C, color.toComponentsCOSArray()); } } /** * Get the width of the of the guideline in default user space units. * The default is 1. * * @return The width of the guideline. */ public float getGuidelineWidth() { return dictionary.getFloat(COSName.W, 1); } /** * Set the guideline width. * * @param width The width in default user space units. */ public void setGuidelineWidth( float width ) { dictionary.setFloat(COSName.W, width); } /** * Get the style for the guideline. The default is "S" for solid. * * @return The guideline style. * @see PDBoxStyle#GUIDELINE_STYLE_DASHED * @see PDBoxStyle#GUIDELINE_STYLE_SOLID */ public String getGuidelineStyle() { return dictionary.getNameAsString(COSName.S, GUIDELINE_STYLE_SOLID); } /** * Set the style for the box. * * @param style The style for the box line. * @see PDBoxStyle#GUIDELINE_STYLE_DASHED * @see PDBoxStyle#GUIDELINE_STYLE_SOLID */ public void setGuidelineStyle( String style ) { dictionary.setName(COSName.S, style); } /** * Get the line dash pattern for this box style. This is guaranteed to not * return null. The default is [3],0. * * @return The line dash pattern. */ public PDLineDashPattern getLineDashPattern() { PDLineDashPattern pattern; COSArray d = (COSArray) dictionary.getDictionaryObject(COSName.D); if( d == null ) { d = new COSArray(); d.add( COSInteger.THREE ); dictionary.setItem(COSName.D, d); } COSArray lineArray = new COSArray(); lineArray.add( d ); //dash phase is not specified and assumed to be zero. pattern = new PDLineDashPattern( lineArray, 0 ); return pattern; } /** * Set the line dash pattern associated with this box style. * * @param dashArray The patter for this box style. */ public void setLineDashPattern( COSArray dashArray ) { COSArray array = null; if( dashArray != null ) { array = dashArray; } dictionary.setItem(COSName.D, array); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/prepress/package.html000066400000000000000000000017131320103431700324700ustar00rootroot00000000000000 This package contains classes for prepress support in PDFBox. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/000077500000000000000000000000001320103431700302675ustar00rootroot00000000000000PDArtifactMarkedContent.java000066400000000000000000000075251320103431700355240ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; /** * An artifact marked content. * * @author Johannes Koch * */ public class PDArtifactMarkedContent extends PDMarkedContent { public PDArtifactMarkedContent(COSDictionary properties) { super(COSName.ARTIFACT, properties); } /** * Gets the type (Type). * * @return the type */ public String getType() { return this.getProperties().getNameAsString(COSName.TYPE); } /** * Gets the artifact's bounding box (BBox). * * @return the artifact's bounding box */ public PDRectangle getBBox() { PDRectangle retval = null; COSArray a = (COSArray) this.getProperties().getDictionaryObject( COSName.BBOX); if (a != null) { retval = new PDRectangle(a); } return retval; } /** * Is the artifact attached to the top edge? * * @return true if the artifact is attached to the top edge, * false otherwise */ public boolean isTopAttached() { return this.isAttached("Top"); } /** * Is the artifact attached to the bottom edge? * * @return true if the artifact is attached to the bottom edge, * false otherwise */ public boolean isBottomAttached() { return this.isAttached("Bottom"); } /** * Is the artifact attached to the left edge? * * @return true if the artifact is attached to the left edge, * false otherwise */ public boolean isLeftAttached() { return this.isAttached("Left"); } /** * Is the artifact attached to the right edge? * * @return true if the artifact is attached to the right edge, * false otherwise */ public boolean isRightAttached() { return this.isAttached("Right"); } /** * Gets the subtype (Subtype). * * @return the subtype */ public String getSubtype() { return this.getProperties().getNameAsString(COSName.SUBTYPE); } /** * Is the artifact attached to the given edge? * * @param edge the edge * @return true if the artifact is attached to the given edge, * false otherwise */ private boolean isAttached(String edge) { COSArray a = (COSArray) this.getProperties().getDictionaryObject( COSName.ATTACHED); if (a != null) { for (int i = 0; i < a.size(); i++) { if (edge.equals(a.getName(i))) { return true; } } } return false; } } PDExportFormatAttributeObject.java000066400000000000000000000203511320103431700367450ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import org.sejda.sambox.cos.COSDictionary; /** * An Export Format attribute object. * * @author Johannes Koch */ public class PDExportFormatAttributeObject extends PDLayoutAttributeObject { /** * standard attribute owner: XML-1.00 */ public static final String OWNER_XML_1_00 = "XML-1.00"; /** * standard attribute owner: HTML-3.2 */ public static final String OWNER_HTML_3_20 = "HTML-3.2"; /** * standard attribute owner: HTML-4.01 */ public static final String OWNER_HTML_4_01 = "HTML-4.01"; /** * standard attribute owner: OEB-1.00 */ public static final String OWNER_OEB_1_00 = "OEB-1.00"; /** * standard attribute owner: RTF-1.05 */ public static final String OWNER_RTF_1_05 = "RTF-1.05"; /** * standard attribute owner: CSS-1.00 */ public static final String OWNER_CSS_1_00 = "CSS-1.00"; /** * standard attribute owner: CSS-2.00 */ public static final String OWNER_CSS_2_00 = "CSS-2.00"; /** * Default constructor. */ public PDExportFormatAttributeObject(String owner) { this.setOwner(owner); } /** * Creates a new ExportFormat attribute object with a given dictionary. * * @param dictionary the dictionary */ public PDExportFormatAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Gets the list numbering (ListNumbering). The default value is * {@link PDListAttributeObject#LIST_NUMBERING_NONE}. * * @return the list numbering */ public String getListNumbering() { return this.getName(PDListAttributeObject.LIST_NUMBERING, PDListAttributeObject.LIST_NUMBERING_NONE); } /** * Sets the list numbering (ListNumbering). The value shall be one of the * following: *
    *
  • {@link PDListAttributeObject#LIST_NUMBERING_NONE},
  • *
  • {@link PDListAttributeObject#LIST_NUMBERING_DISC},
  • *
  • {@link PDListAttributeObject#LIST_NUMBERING_CIRCLE},
  • *
  • {@link PDListAttributeObject#LIST_NUMBERING_SQUARE},
  • *
  • {@link PDListAttributeObject#LIST_NUMBERING_DECIMAL},
  • *
  • {@link PDListAttributeObject#LIST_NUMBERING_UPPER_ROMAN},
  • *
  • {@link PDListAttributeObject#LIST_NUMBERING_LOWER_ROMAN},
  • *
  • {@link PDListAttributeObject#LIST_NUMBERING_UPPER_ALPHA},
  • *
  • {@link PDListAttributeObject#LIST_NUMBERING_LOWER_ALPHA}.
  • *
* * @param listNumbering the list numbering */ public void setListNumbering(String listNumbering) { this.setName(PDListAttributeObject.LIST_NUMBERING, listNumbering); } /** * Gets the number of rows in the enclosing table that shall be spanned by * the cell (RowSpan). The default value is 1. * * @return the row span */ public int getRowSpan() { return this.getInteger(PDTableAttributeObject.ROW_SPAN, 1); } /** * Sets the number of rows in the enclosing table that shall be spanned by * the cell (RowSpan). * * @param rowSpan the row span */ public void setRowSpan(int rowSpan) { this.setInteger(PDTableAttributeObject.ROW_SPAN, rowSpan); } /** * Gets the number of columns in the enclosing table that shall be spanned * by the cell (ColSpan). The default value is 1. * * @return the column span */ public int getColSpan() { return this.getInteger(PDTableAttributeObject.COL_SPAN, 1); } /** * Sets the number of columns in the enclosing table that shall be spanned * by the cell (ColSpan). * * @param colSpan the column span */ public void setColSpan(int colSpan) { this.setInteger(PDTableAttributeObject.COL_SPAN, colSpan); } /** * Gets the headers (Headers). An array of byte strings, where each string * shall be the element identifier (see the * {@link org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement#getElementIdentifier()}) for a TH structure * element that shall be used as a header associated with this cell. * * @return the headers. */ public String[] getHeaders() { return this.getArrayOfString(PDTableAttributeObject.HEADERS); } /** * Sets the headers (Headers). An array of byte strings, where each string * shall be the element identifier (see the * {@link org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement#getElementIdentifier()}) for a TH structure * element that shall be used as a header associated with this cell. * * @param headers the headers */ public void setHeaders(String[] headers) { this.setArrayOfString(PDTableAttributeObject.HEADERS, headers); } /** * Gets the scope (Scope). It shall reflect whether the header cell applies * to the rest of the cells in the row that contains it, the column that * contains it, or both the row and the column that contain it. * * @return the scope */ public String getScope() { return this.getName(PDTableAttributeObject.SCOPE); } /** * Sets the scope (Scope). It shall reflect whether the header cell applies * to the rest of the cells in the row that contains it, the column that * contains it, or both the row and the column that contain it. The value * shall be one of the following: *
    *
  • {@link PDTableAttributeObject#SCOPE_ROW},
  • *
  • {@link PDTableAttributeObject#SCOPE_COLUMN}, or
  • *
  • {@link PDTableAttributeObject#SCOPE_BOTH}.
  • *
* * @param scope the scope */ public void setScope(String scope) { this.setName(PDTableAttributeObject.SCOPE, scope); } /** * Gets the summary of the table’s purpose and structure. * * @return the summary */ public String getSummary() { return this.getString(PDTableAttributeObject.SUMMARY); } /** * Sets the summary of the table’s purpose and structure. * * @param summary the summary */ public void setSummary(String summary) { this.setString(PDTableAttributeObject.SUMMARY, summary); } @Override public String toString() { StringBuilder sb = new StringBuilder().append(super.toString()); if (this.isSpecified(PDListAttributeObject.LIST_NUMBERING)) { sb.append(", ListNumbering=").append(this.getListNumbering()); } if (this.isSpecified(PDTableAttributeObject.ROW_SPAN)) { sb.append(", RowSpan=").append(String.valueOf(this.getRowSpan())); } if (this.isSpecified(PDTableAttributeObject.COL_SPAN)) { sb.append(", ColSpan=").append(String.valueOf(this.getColSpan())); } if (this.isSpecified(PDTableAttributeObject.HEADERS)) { sb.append(", Headers=").append(arrayToString(this.getHeaders())); } if (this.isSpecified(PDTableAttributeObject.SCOPE)) { sb.append(", Scope=").append(this.getScope()); } if (this.isSpecified(PDTableAttributeObject.SUMMARY)) { sb.append(", Summary=").append(this.getSummary()); } return sb.toString(); } } PDFourColours.java000066400000000000000000000104641320103431700335660ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.graphics.color.PDGamma; /** * An object for four colours. * * @author Johannes Koch */ public class PDFourColours implements COSObjectable { private final COSArray array; public PDFourColours() { this.array = new COSArray(); this.array.add(COSNull.NULL); this.array.add(COSNull.NULL); this.array.add(COSNull.NULL); this.array.add(COSNull.NULL); } public PDFourColours(COSArray array) { this.array = array; // ensure that array has 4 items if (this.array.size() < 4) { for (int i = (this.array.size() - 1); i < 4; i++) { this.array.add(COSNull.NULL); } } } /** * Gets the colour for the before edge. * * @return the colour for the before edge */ public PDGamma getBeforeColour() { return this.getColourByIndex(0); } /** * Sets the colour for the before edge. * * @param colour the colour for the before edge */ public void setBeforeColour(PDGamma colour) { this.setColourByIndex(0, colour); } /** * Gets the colour for the after edge. * * @return the colour for the after edge */ public PDGamma getAfterColour() { return this.getColourByIndex(1); } /** * Sets the colour for the after edge. * * @param colour the colour for the after edge */ public void setAfterColour(PDGamma colour) { this.setColourByIndex(1, colour); } /** * Gets the colour for the start edge. * * @return the colour for the start edge */ public PDGamma getStartColour() { return this.getColourByIndex(2); } /** * Sets the colour for the start edge. * * @param colour the colour for the start edge */ public void setStartColour(PDGamma colour) { this.setColourByIndex(2, colour); } /** * Gets the colour for the end edge. * * @return the colour for the end edge */ public PDGamma getEndColour() { return this.getColourByIndex(3); } /** * Sets the colour for the end edge. * * @param colour the colour for the end edge */ public void setEndColour(PDGamma colour) { this.setColourByIndex(3, colour); } /** * {@inheritDoc} */ @Override public COSBase getCOSObject() { return this.array; } /** * Gets the colour by edge index. * * @param index edge index * @return the colour */ private PDGamma getColourByIndex(int index) { PDGamma retval = null; COSBase item = this.array.getObject(index); if (item instanceof COSArray) { retval = new PDGamma((COSArray) item); } return retval; } /** * Sets the colour by edge index. * * @param index the edge index * @param colour the colour */ private void setColourByIndex(int index, PDGamma colour) { COSBase base; if (colour == null) { base = COSNull.NULL; } else { base = colour.getCOSArray(); } this.array.set(index, base); } } PDLayoutAttributeObject.java000066400000000000000000001551311320103431700355750ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.graphics.color.PDGamma; /** * A Layout attribute object. * * @author Johannes Koch */ public class PDLayoutAttributeObject extends PDStandardAttributeObject { /** * standard attribute owner: Layout */ public static final String OWNER_LAYOUT = "Layout"; private static final String PLACEMENT = "Placement"; private static final String WRITING_MODE = "WritingMode"; private static final String BACKGROUND_COLOR = "BackgroundColor"; private static final String BORDER_COLOR = "BorderColor"; private static final String BORDER_STYLE = "BorderStyle"; private static final String BORDER_THICKNESS = "BorderThickness"; private static final String PADDING = "Padding"; private static final String COLOR = "Color"; private static final String SPACE_BEFORE = "SpaceBefore"; private static final String SPACE_AFTER = "SpaceAfter"; private static final String START_INDENT = "StartIndent"; private static final String END_INDENT = "EndIndent"; private static final String TEXT_INDENT = "TextIndent"; private static final String TEXT_ALIGN = "TextAlign"; private static final String BBOX = "BBox"; private static final String WIDTH = "Width"; private static final String HEIGHT = "Height"; private static final String BLOCK_ALIGN = "BlockAlign"; private static final String INLINE_ALIGN = "InlineAlign"; private static final String T_BORDER_STYLE = "TBorderStyle"; private static final String T_PADDING = "TPadding"; private static final String BASELINE_SHIFT = "BaselineShift"; private static final String LINE_HEIGHT = "LineHeight"; private static final String TEXT_DECORATION_COLOR = "TextDecorationColor"; private static final String TEXT_DECORATION_THICKNESS = "TextDecorationThickness"; private static final String TEXT_DECORATION_TYPE = "TextDecorationType"; private static final String RUBY_ALIGN = "RubyAlign"; private static final String RUBY_POSITION = "RubyPosition"; private static final String GLYPH_ORIENTATION_VERTICAL = "GlyphOrientationVertical"; private static final String COLUMN_COUNT = "ColumnCount"; private static final String COLUMN_GAP = "ColumnGap"; private static final String COLUMN_WIDTHS = "ColumnWidths"; /** * Placement: Block: Stacked in the block-progression direction within an * enclosing reference area or parent BLSE. */ public static final String PLACEMENT_BLOCK = "Block"; /** * Placement: Inline: Packed in the inline-progression direction within an * enclosing BLSE. */ public static final String PLACEMENT_INLINE = "Inline"; /** * Placement: Before: Placed so that the before edge of the element’s * allocation rectangle coincides with that of the nearest enclosing * reference area. The element may float, if necessary, to achieve the * specified placement. The element shall be treated as a block occupying * the full extent of the enclosing reference area in the inline direction. * Other content shall be stacked so as to begin at the after edge of the * element’s allocation rectangle. */ public static final String PLACEMENT_BEFORE = "Before"; /** * Placement: Start: Placed so that the start edge of the element’s * allocation rectangle coincides with that of the nearest enclosing * reference area. The element may float, if necessary, to achieve the * specified placement. Other content that would intrude into the element’s * allocation rectangle shall be laid out as a runaround. */ public static final String PLACEMENT_START = "Start"; /** * Placement: End: Placed so that the end edge of the element’s allocation * rectangle coincides with that of the nearest enclosing reference area. * The element may float, if necessary, to achieve the specified placement. * Other content that would intrude into the element’s allocation rectangle * shall be laid out as a runaround. */ public static final String PLACEMENT_END = "End"; /** * WritingMode: LrTb: Inline progression from left to right; block * progression from top to bottom. This is the typical writing mode for * Western writing systems. */ public static final String WRITING_MODE_LRTB = "LrTb"; /** * WritingMode: RlTb: Inline progression from right to left; block * progression from top to bottom. This is the typical writing mode for * Arabic and Hebrew writing systems. */ public static final String WRITING_MODE_RLTB = "RlTb"; /** * WritingMode: TbRl: Inline progression from top to bottom; block * progression from right to left. This is the typical writing mode for * Chinese and Japanese writing systems. */ public static final String WRITING_MODE_TBRL = "TbRl"; /** * BorderStyle: None: No border. Forces the computed value of * BorderThickness to be 0. */ public static final String BORDER_STYLE_NONE = "None"; /** * BorderStyle: Hidden: Same as {@link #BORDER_STYLE_NONE}, except in terms * of border conflict resolution for table elements. */ public static final String BORDER_STYLE_HIDDEN = "Hidden"; /** * BorderStyle: Dotted: The border is a series of dots. */ public static final String BORDER_STYLE_DOTTED = "Dotted"; /** * BorderStyle: Dashed: The border is a series of short line segments. */ public static final String BORDER_STYLE_DASHED = "Dashed"; /** * BorderStyle: Solid: The border is a single line segment. */ public static final String BORDER_STYLE_SOLID = "Solid"; /** * BorderStyle: Double: The border is two solid lines. The sum of the two * lines and the space between them equals the value of BorderThickness. */ public static final String BORDER_STYLE_DOUBLE = "Double"; /** * BorderStyle: Groove: The border looks as though it were carved into the * canvas. */ public static final String BORDER_STYLE_GROOVE = "Groove"; /** * BorderStyle: Ridge: The border looks as though it were coming out of the * canvas (the opposite of {@link #BORDER_STYLE_GROOVE}). */ public static final String BORDER_STYLE_RIDGE = "Ridge"; /** * BorderStyle: Inset: The border makes the entire box look as though it * were embedded in the canvas. */ public static final String BORDER_STYLE_INSET = "Inset"; /** * BorderStyle: Outset: The border makes the entire box look as though it * were coming out of the canvas (the opposite of {@link #BORDER_STYLE_INSET}. */ public static final String BORDER_STYLE_OUTSET = "Outset"; /** * TextAlign: Start: Aligned with the start edge. */ public static final String TEXT_ALIGN_START = "Start"; /** * TextAlign: Center: Centered between the start and end edges. */ public static final String TEXT_ALIGN_CENTER = "Center"; /** * TextAlign: End: Aligned with the end edge. */ public static final String TEXT_ALIGN_END = "End"; /** * TextAlign: Justify: Aligned with both the start and end edges, with * internal spacing within each line expanded, if necessary, to achieve such * alignment. The last (or only) line shall be aligned with the start edge * only. */ public static final String TEXT_ALIGN_JUSTIFY = "Justify"; /** * Width: Auto */ public static final String WIDTH_AUTO = "Auto"; /** * Height: Auto */ public static final String HEIGHT_AUTO = "Auto"; /** * BlockAlign: Before: Before edge of the first child’s allocation rectangle * aligned with that of the table cell’s content rectangle. */ public static final String BLOCK_ALIGN_BEFORE = "Before"; /** * BlockAlign: Middle: Children centered within the table cell. The distance * between the before edge of the first child’s allocation rectangle and * that of the table cell’s content rectangle shall be the same as the * distance between the after edge of the last child’s allocation rectangle * and that of the table cell’s content rectangle. */ public static final String BLOCK_ALIGN_MIDDLE = "Middle"; /** * BlockAlign: After: After edge of the last child’s allocation rectangle * aligned with that of the table cell’s content rectangle. */ public static final String BLOCK_ALIGN_AFTER = "After"; /** * BlockAlign: Justify: Children aligned with both the before and after * edges of the table cell’s content rectangle. The first child shall be * placed as described for {@link #BLOCK_ALIGN_BEFORE} and the last child as * described for {@link #BLOCK_ALIGN_AFTER}, with equal spacing between the * children. If there is only one child, it shall be aligned with the before * edge only, as for {@link #BLOCK_ALIGN_BEFORE}. */ public static final String BLOCK_ALIGN_JUSTIFY = "Justify"; /** * InlineAlign: Start: Start edge of each child’s allocation rectangle * aligned with that of the table cell’s content rectangle. */ public static final String INLINE_ALIGN_START = "Start"; /** * InlineAlign: Center: Each child centered within the table cell. The * distance between the start edges of the child’s allocation rectangle and * the table cell’s content rectangle shall be the same as the distance * between their end edges. */ public static final String INLINE_ALIGN_CENTER = "Center"; /** * InlineAlign: End: End edge of each child’s allocation rectangle aligned * with that of the table cell’s content rectangle. */ public static final String INLINE_ALIGN_END = "End"; /** * LineHeight: NormalAdjust the line height to include any nonzero value * specified for BaselineShift. */ public static final String LINE_HEIGHT_NORMAL = "Normal"; /** * LineHeight: Auto: Adjustment for the value of BaselineShift shall not be * made. */ public static final String LINE_HEIGHT_AUTO = "Auto"; /** * TextDecorationType: None: No text decoration */ public static final String TEXT_DECORATION_TYPE_NONE = "None"; /** * TextDecorationType: Underline: A line below the text */ public static final String TEXT_DECORATION_TYPE_UNDERLINE = "Underline"; /** * TextDecorationType: Overline: A line above the text */ public static final String TEXT_DECORATION_TYPE_OVERLINE = "Overline"; /** * TextDecorationType: LineThrough: A line through the middle of the text */ public static final String TEXT_DECORATION_TYPE_LINE_THROUGH = "LineThrough"; /** * RubyAlign: Start: The content shall be aligned on the start edge in the * inline-progression direction. */ public static final String RUBY_ALIGN_START = "Start"; /** * RubyAlign: Center: The content shall be centered in the * inline-progression direction. */ public static final String RUBY_ALIGN_CENTER = "Center"; /** * RubyAlign: End: The content shall be aligned on the end edge in the * inline-progression direction. */ public static final String RUBY_ALIGN_END = "End"; /** * RubyAlign: Justify: The content shall be expanded to fill the available * width in the inline-progression direction. */ public static final String RUBY_ALIGN_JUSTIFY = "Justify"; /** * RubyAlign: Distribute: The content shall be expanded to fill the * available width in the inline-progression direction. However, space shall * also be inserted at the start edge and end edge of the text. The spacing * shall be distributed using a 1:2:1 (start:infix:end) ratio. It shall be * changed to a 0:1:1 ratio if the ruby appears at the start of a text line * or to a 1:1:0 ratio if the ruby appears at the end of the text line. */ public static final String RUBY_ALIGN_DISTRIBUTE = "Distribute"; /** * RubyPosition: Before: The RT content shall be aligned along the before * edge of the element. */ public static final String RUBY_POSITION_BEFORE = "Before"; /** * RubyPosition: After: The RT content shall be aligned along the after edge * of the element. */ public static final String RUBY_POSITION_AFTER = "After"; /** * RubyPosition: Warichu: The RT and associated RP elements shall be * formatted as a warichu, following the RB element. */ public static final String RUBY_POSITION_WARICHU = "Warichu"; /** * RubyPosition: Inline: The RT and associated RP elements shall be * formatted as a parenthesis comment, following the RB element. */ public static final String RUBY_POSITION_INLINE = "Inline"; /** * GlyphOrientationVertical: Auto */ public static final String GLYPH_ORIENTATION_VERTICAL_AUTO = "Auto"; /** * GlyphOrientationVertical: -180° */ public static final String GLYPH_ORIENTATION_VERTICAL_MINUS_180_DEGREES = "-180"; /** * GlyphOrientationVertical: -90° */ public static final String GLYPH_ORIENTATION_VERTICAL_MINUS_90_DEGREES = "-90"; /** * GlyphOrientationVertical: 0° */ public static final String GLYPH_ORIENTATION_VERTICAL_ZERO_DEGREES = "0"; /** * GlyphOrientationVertical: 90° */ public static final String GLYPH_ORIENTATION_VERTICAL_90_DEGREES = "90"; /** * GlyphOrientationVertical: 180° */ public static final String GLYPH_ORIENTATION_VERTICAL_180_DEGREES = "180"; /** * GlyphOrientationVertical: 270° */ public static final String GLYPH_ORIENTATION_VERTICAL_270_DEGREES = "270"; /** * GlyphOrientationVertical: 360° */ public static final String GLYPH_ORIENTATION_VERTICAL_360_DEGREES = "360"; /** * Default constructor. */ public PDLayoutAttributeObject() { this.setOwner(OWNER_LAYOUT); } /** * Creates a new Layout attribute object with a given dictionary. * * @param dictionary the dictionary */ public PDLayoutAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Gets the positioning of the element with respect to the enclosing * reference area and other content (Placement). The default value is * {@link #PLACEMENT_INLINE}. * * @return the placement */ public String getPlacement() { return this.getName(PLACEMENT, PLACEMENT_INLINE); } /** * Sets the positioning of the element with respect to the enclosing * reference area and other content (Placement). The value should be one of: *
    *
  • {@link #PLACEMENT_BLOCK},
  • *
  • {@link #PLACEMENT_INLINE},
  • *
  • {@link #PLACEMENT_BEFORE},
  • *
  • {@link #PLACEMENT_START},
  • *
  • {@link #PLACEMENT_END}.
  • *
* * @param placement the placement */ public void setPlacement(String placement) { this.setName(PLACEMENT, placement); } /** * Gets the writing mode (WritingMode). The default value is * {@link #WRITING_MODE_LRTB}. * * @return the writing mode */ public String getWritingMode() { return this.getName(WRITING_MODE, WRITING_MODE_LRTB); } /** * Sets the writing mode (WritingMode). The value should be one of: *
    *
  • {@link #WRITING_MODE_LRTB},
  • *
  • {@link #WRITING_MODE_RLTB},
  • *
  • {@link #WRITING_MODE_TBRL}.
  • *
* * @param writingMode the writing mode */ public void setWritingMode(String writingMode) { this.setName(WRITING_MODE, writingMode); } /** * Gets the background colour (BackgroundColor). * * @return the background colour */ public PDGamma getBackgroundColor() { return this.getColor(BACKGROUND_COLOR); } /** * Sets the background colour (BackgroundColor). * * @param backgroundColor the background colour */ public void setBackgroundColor(PDGamma backgroundColor) { this.setColor(BACKGROUND_COLOR, backgroundColor); } /** * Gets the border colour (BorderColor). * * @return a single border colour ({@link PDGamma}) or four border colours * ({@link PDFourColours}) */ public Object getBorderColors() { return this.getColorOrFourColors(BORDER_COLOR); } /** * Sets the same border colour for all four sides (BorderColor). * * @param borderColor the border colour */ public void setAllBorderColors(PDGamma borderColor) { this.setColor(BORDER_COLOR, borderColor); } /** * Sets the border colours for four sides separately (BorderColor). * * @param borderColors the border colours */ public void setBorderColors(PDFourColours borderColors) { this.setFourColors(BORDER_COLOR, borderColors); } /** * Gets the border style (BorderStyle). The default value is * {@link #BORDER_STYLE_NONE}. * * @return the border styles (a String or an array of four Strings) */ public Object getBorderStyle() { return this.getNameOrArrayOfName(BORDER_STYLE, BORDER_STYLE_NONE); } /** * Sets the same border style for all four sides (BorderStyle). The value * should be one of: *
    *
  • {@link #BORDER_STYLE_NONE},
  • *
  • {@link #BORDER_STYLE_HIDDEN},
  • *
  • {@link #BORDER_STYLE_DOTTED},
  • *
  • {@link #BORDER_STYLE_DASHED},
  • *
  • {@link #BORDER_STYLE_SOLID},
  • *
  • {@link #BORDER_STYLE_DOUBLE},
  • *
  • {@link #BORDER_STYLE_GROOVE},
  • *
  • {@link #BORDER_STYLE_RIDGE},
  • *
  • {@link #BORDER_STYLE_INSET},
  • *
  • {@link #BORDER_STYLE_OUTSET}.
  • *
* * @param borderStyle the border style */ public void setAllBorderStyles(String borderStyle) { this.setName(BORDER_STYLE, borderStyle); } /** * Sets the border styles for four sides separately (BorderStyle). The * values should be of: *
    *
  • {@link #BORDER_STYLE_NONE},
  • *
  • {@link #BORDER_STYLE_HIDDEN},
  • *
  • {@link #BORDER_STYLE_DOTTED},
  • *
  • {@link #BORDER_STYLE_DASHED},
  • *
  • {@link #BORDER_STYLE_SOLID},
  • *
  • {@link #BORDER_STYLE_DOUBLE},
  • *
  • {@link #BORDER_STYLE_GROOVE},
  • *
  • {@link #BORDER_STYLE_RIDGE},
  • *
  • {@link #BORDER_STYLE_INSET},
  • *
  • {@link #BORDER_STYLE_OUTSET}.
  • *
* * @param borderStyles the border styles (an array of four Strings) */ public void setBorderStyles(String[] borderStyles) { this.setArrayOfName(BORDER_STYLE, borderStyles); } /** * Gets the border thickness (BorderThickness). * * @return the border thickness (a Float or an array of four floats) */ public Object getBorderThickness() { return this.getNumberOrArrayOfNumber(BORDER_THICKNESS, UNSPECIFIED); } /** * Sets the same border thickness for all four sides (BorderThickness). * * @param borderThickness the border thickness */ public void setAllBorderThicknesses(float borderThickness) { this.setNumber(BORDER_THICKNESS, borderThickness); } /** * Sets the same border thickness for all four sides (BorderThickness). * * @param borderThickness the border thickness */ public void setAllBorderThicknesses(int borderThickness) { this.setNumber(BORDER_THICKNESS, borderThickness); } /** * Sets the border thicknesses for four sides separately (BorderThickness). * * @param borderThicknesses the border thickness (an array of four floats) */ public void setBorderThicknesses(float[] borderThicknesses) { this.setArrayOfNumber(BORDER_THICKNESS, borderThicknesses); } /** * Gets the padding (Padding). The default value is 0. * * @return the padding (a Float or an array of float) */ public Object getPadding() { return this.getNumberOrArrayOfNumber(PADDING, 0.f); } /** * Sets the same padding for all four sides (Padding). * * @param padding the padding */ public void setAllPaddings(float padding) { this.setNumber(PADDING, padding); } /** * Sets the same padding for all four sides (Padding). * * @param padding the padding */ public void setAllPaddings(int padding) { this.setNumber(PADDING, padding); } /** * Sets the paddings for four sides separately (Padding). * * @param paddings the paddings (an array of four floats) */ public void setPaddings(float[] paddings) { this.setArrayOfNumber(PADDING, paddings); } /** * Gets the color to be used for drawing text and the default value for the * colour of table borders and text decorations (Color). * * @return the colour */ public PDGamma getColor() { return this.getColor(COLOR); } /** * Sets the color to be used for drawing text and the default value for the * colour of table borders and text decorations (Color). * * @param color the colour */ public void setColor(PDGamma color) { this.setColor(COLOR, color); } /** * Gets the amount of extra space preceding the before edge of the BLSE in * the block-progression direction (SpaceBefore). The default value is 0. * * @return the space before */ public float getSpaceBefore() { return this.getNumber(SPACE_BEFORE, 0.f); } /** * Sets the amount of extra space preceding the before edge of the BLSE in * the block-progression direction (SpaceBefore). * * @param spaceBefore the space before */ public void setSpaceBefore(float spaceBefore) { this.setNumber(SPACE_BEFORE, spaceBefore); } /** * Sets the amount of extra space preceding the before edge of the BLSE in * the block-progression direction (SpaceBefore). * * @param spaceBefore the space before */ public void setSpaceBefore(int spaceBefore) { this.setNumber(SPACE_BEFORE, spaceBefore); } /** * Gets the amount of extra space following the after edge of the BLSE in * the block-progression direction (SpaceAfter). The default value is 0. * * @return the space after */ public float getSpaceAfter() { return this.getNumber(SPACE_AFTER, 0.f); } /** * Sets the amount of extra space following the after edge of the BLSE in * the block-progression direction (SpaceAfter). * * @param spaceAfter the space after */ public void setSpaceAfter(float spaceAfter) { this.setNumber(SPACE_AFTER, spaceAfter); } /** * Sets the amount of extra space following the after edge of the BLSE in * the block-progression direction (SpaceAfter). * * @param spaceAfter the space after */ public void setSpaceAfter(int spaceAfter) { this.setNumber(SPACE_AFTER, spaceAfter); } /** * Gets the distance from the start edge of the reference area to that of * the BLSE in the inline-progression direction (StartIndent). The default value is 0. * * @return the start indent */ public float getStartIndent() { return this.getNumber(START_INDENT, 0.f); } /** * Sets the distance from the start edge of the reference area to that of * the BLSE in the inline-progression direction (StartIndent). * * @param startIndent the start indent */ public void setStartIndent(float startIndent) { this.setNumber(START_INDENT, startIndent); } /** * Sets the distance from the start edge of the reference area to that of * the BLSE in the inline-progression direction (StartIndent). * * @param startIndent the start indent */ public void setStartIndent(int startIndent) { this.setNumber(START_INDENT, startIndent); } /** * Gets the distance from the end edge of the BLSE to that of the reference * area in the inline-progression direction (EndIndent). The default value * is 0. * * @return the end indent */ public float getEndIndent() { return this.getNumber(END_INDENT, 0.f); } /** * Sets the distance from the end edge of the BLSE to that of the reference * area in the inline-progression direction (EndIndent). * * @param endIndent the end indent */ public void setEndIndent(float endIndent) { this.setNumber(END_INDENT, endIndent); } /** * Sets the distance from the end edge of the BLSE to that of the reference * area in the inline-progression direction (EndIndent). * * @param endIndent the end indent */ public void setEndIndent(int endIndent) { this.setNumber(END_INDENT, endIndent); } /** * Gets the additional distance in the inline-progression direction from the * start edge of the BLSE, as specified by StartIndent, to that of the first * line of text (TextIndent). The default value is 0. * * @return the text indent */ public float getTextIndent() { return this.getNumber(TEXT_INDENT, 0.f); } /** * Sets the additional distance in the inline-progression direction from the * start edge of the BLSE, as specified by StartIndent, to that of the first * line of text (TextIndent). * * @param textIndent the text indent */ public void setTextIndent(float textIndent) { this.setNumber(TEXT_INDENT, textIndent); } /** * Sets the additional distance in the inline-progression direction from the * start edge of the BLSE, as specified by StartIndent, to that of the first * line of text (TextIndent). * * @param textIndent the text indent */ public void setTextIndent(int textIndent) { this.setNumber(TEXT_INDENT, textIndent); } /** * Gets the alignment, in the inline-progression direction, of text and * other content within lines of the BLSE (TextAlign). The default value is * {@link #TEXT_ALIGN_START}. * * @return the text alignment */ public String getTextAlign() { return this.getName(TEXT_ALIGN, TEXT_ALIGN_START); } /** * Sets the alignment, in the inline-progression direction, of text and * other content within lines of the BLSE (TextAlign). The value should be * one of: *
    *
  • {@link #TEXT_ALIGN_START},
  • *
  • {@link #TEXT_ALIGN_CENTER},
  • *
  • {@link #TEXT_ALIGN_END},
  • *
  • {@link #TEXT_ALIGN_JUSTIFY}.
  • *
* * @param textIndent the text alignment */ public void setTextAlign(String textIndent) { this.setName(TEXT_ALIGN, textIndent); } /** * Gets the bounding box. * * @return the bounding box. */ public PDRectangle getBBox() { COSArray array = (COSArray) this.getCOSObject().getDictionaryObject(BBOX); if (array != null) { return new PDRectangle(array); } return null; } /** * Sets the bounding box. * * @param bbox the bounding box */ public void setBBox(PDRectangle bbox) { String name = BBOX; COSBase oldValue = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setItem(name, bbox); COSBase newValue = bbox == null ? null : bbox.getCOSObject(); this.potentiallyNotifyChanged(oldValue, newValue); } /** * Gets the width of the element’s content rectangle in the * inline-progression direction (Width). The default value is * {@link #WIDTH_AUTO}. * * @return the width (a Float or a String) */ public Object getWidth() { return this.getNumberOrName(WIDTH, WIDTH_AUTO); } /** * Sets the width of the element’s content rectangle in the * inline-progression direction (Width) to {@link #WIDTH_AUTO}. */ public void setWidthAuto() { this.setName(WIDTH, WIDTH_AUTO); } /** * Sets the width of the element’s content rectangle in the * inline-progression direction (Width). * * @param width the width */ public void setWidth(float width) { this.setNumber(WIDTH, width); } /** * Sets the width of the element’s content rectangle in the * inline-progression direction (Width). * * @param width the width */ public void setWidth(int width) { this.setNumber(WIDTH, width); } /** * Gets the height of the element’s content rectangle in the * block-progression direction (Height). The default value is * {@link #HEIGHT_AUTO}. * * @return the height (a Float or a String) */ public Object getHeight() { return this.getNumberOrName(HEIGHT, HEIGHT_AUTO); } /** * Sets the height of the element’s content rectangle in the * block-progression direction (Height) to {@link #HEIGHT_AUTO}. */ public void setHeightAuto() { this.setName(HEIGHT, HEIGHT_AUTO); } /** * Sets the height of the element’s content rectangle in the * block-progression direction (Height). * * @param height the height */ public void setHeight(float height) { this.setNumber(HEIGHT, height); } /** * Sets the height of the element’s content rectangle in the * block-progression direction (Height). * * @param height the height */ public void setHeight(int height) { this.setNumber(HEIGHT, height); } /** * Gets the alignment, in the block-progression direction, of content within * the table cell (BlockAlign). The default value is * {@link #BLOCK_ALIGN_BEFORE}. * * @return the block alignment */ public String getBlockAlign() { return this.getName(BLOCK_ALIGN, BLOCK_ALIGN_BEFORE); } /** * Sets the alignment, in the block-progression direction, of content within * the table cell (BlockAlign). The value should be one of: *
    *
  • {@link #BLOCK_ALIGN_BEFORE},
  • *
  • {@link #BLOCK_ALIGN_MIDDLE},
  • *
  • {@link #BLOCK_ALIGN_AFTER},
  • *
  • {@link #BLOCK_ALIGN_JUSTIFY}.
  • *
* * @param blockAlign the block alignment */ public void setBlockAlign(String blockAlign) { this.setName(BLOCK_ALIGN, blockAlign); } /** * Gets the alignment, in the inline-progression direction, of content * within the table cell (InlineAlign). The default value is * {@link #INLINE_ALIGN_START}. * * @return the inline alignment */ public String getInlineAlign() { return this.getName(INLINE_ALIGN, INLINE_ALIGN_START); } /** * Sets the alignment, in the inline-progression direction, of content * within the table cell (InlineAlign). The value should be one of *
    *
  • {@link #INLINE_ALIGN_START},
  • *
  • {@link #INLINE_ALIGN_CENTER},
  • *
  • {@link #INLINE_ALIGN_END}.
  • *
* * @param inlineAlign the inline alignment */ public void setInlineAlign(String inlineAlign) { this.setName(INLINE_ALIGN, inlineAlign); } /** * Gets the style of the border drawn on each edge of a table cell * (TBorderStyle). * * @return the border style. */ public Object getTBorderStyle() { return this.getNameOrArrayOfName(T_BORDER_STYLE, BORDER_STYLE_NONE); } /** * Sets the same table border style for all four sides (TBorderStyle). The * value should be one of: *
    *
  • {@link #BORDER_STYLE_NONE},
  • *
  • {@link #BORDER_STYLE_HIDDEN},
  • *
  • {@link #BORDER_STYLE_DOTTED},
  • *
  • {@link #BORDER_STYLE_DASHED},
  • *
  • {@link #BORDER_STYLE_SOLID},
  • *
  • {@link #BORDER_STYLE_DOUBLE},
  • *
  • {@link #BORDER_STYLE_GROOVE},
  • *
  • {@link #BORDER_STYLE_RIDGE},
  • *
  • {@link #BORDER_STYLE_INSET},
  • *
  • {@link #BORDER_STYLE_OUTSET}.
  • *
* * @param tBorderStyle the table border style */ public void setAllTBorderStyles(String tBorderStyle) { this.setName(T_BORDER_STYLE, tBorderStyle); } /** * Sets the style of the border drawn on each edge of a table cell * (TBorderStyle). The values should be of: *
    *
  • {@link #BORDER_STYLE_NONE},
  • *
  • {@link #BORDER_STYLE_HIDDEN},
  • *
  • {@link #BORDER_STYLE_DOTTED},
  • *
  • {@link #BORDER_STYLE_DASHED},
  • *
  • {@link #BORDER_STYLE_SOLID},
  • *
  • {@link #BORDER_STYLE_DOUBLE},
  • *
  • {@link #BORDER_STYLE_GROOVE},
  • *
  • {@link #BORDER_STYLE_RIDGE},
  • *
  • {@link #BORDER_STYLE_INSET},
  • *
  • {@link #BORDER_STYLE_OUTSET}.
  • *
* * @param tBorderStyles */ public void setTBorderStyles(String[] tBorderStyles) { this.setArrayOfName(T_BORDER_STYLE, tBorderStyles); } /** * Gets the offset to account for the separation between the table cell’s * content rectangle and the surrounding border (TPadding). The default * value is 0. * * @return the table padding (a Float or an array of float) */ public Object getTPadding() { return this.getNumberOrArrayOfNumber(T_PADDING, 0.f); } /** * Sets the same table padding for all four sides (TPadding). * * @param tPadding the table padding */ public void setAllTPaddings(float tPadding) { this.setNumber(T_PADDING, tPadding); } /** * Sets the same table padding for all four sides (TPadding). * * @param tPadding the table padding */ public void setAllTPaddings(int tPadding) { this.setNumber(T_PADDING, tPadding); } /** * Sets the table paddings for four sides separately (TPadding). * * @param tPaddings the table paddings (an array of four floats) */ public void setTPaddings(float[] tPaddings) { this.setArrayOfNumber(T_PADDING, tPaddings); } /** * Gets the distance by which the element’s baseline shall be shifted * relative to that of its parent element (BaselineShift). The default value * is 0. * * @return the baseline shift */ public float getBaselineShift() { return this.getNumber(BASELINE_SHIFT, 0.f); } /** * Sets the distance by which the element’s baseline shall be shifted * relative to that of its parent element (BaselineShift). * * @param baselineShift the baseline shift */ public void setBaselineShift(float baselineShift) { this.setNumber(BASELINE_SHIFT, baselineShift); } /** * Sets the distance by which the element’s baseline shall be shifted * relative to that of its parent element (BaselineShift). * * @param baselineShift the baseline shift */ public void setBaselineShift(int baselineShift) { this.setNumber(BASELINE_SHIFT, baselineShift); } /** * Gets the element’s preferred height in the block-progression direction * (LineHeight). The default value is {@link #LINE_HEIGHT_NORMAL}. * * @return the line height (a Float or a String) */ public Object getLineHeight() { return this.getNumberOrName(LINE_HEIGHT, LINE_HEIGHT_NORMAL); } /** * Sets the element’s preferred height in the block-progression direction * (LineHeight) to {@link #LINE_HEIGHT_NORMAL}. */ public void setLineHeightNormal() { this.setName(LINE_HEIGHT, LINE_HEIGHT_NORMAL); } /** * Sets the element’s preferred height in the block-progression direction * (LineHeight) to {@link #LINE_HEIGHT_AUTO}. */ public void setLineHeightAuto() { this.setName(LINE_HEIGHT, LINE_HEIGHT_AUTO); } /** * Sets the element’s preferred height in the block-progression direction * (LineHeight). * * @param lineHeight the line height */ public void setLineHeight(float lineHeight) { this.setNumber(LINE_HEIGHT, lineHeight); } /** * Sets the element’s preferred height in the block-progression direction * (LineHeight). * * @param lineHeight the line height */ public void setLineHeight(int lineHeight) { this.setNumber(LINE_HEIGHT, lineHeight); } /** * Gets the colour to be used for drawing text decorations * (TextDecorationColor). * * @return the text decoration colour */ public PDGamma getTextDecorationColor() { return this.getColor(TEXT_DECORATION_COLOR); } /** * Sets the colour to be used for drawing text decorations * (TextDecorationColor). * * @param textDecorationColor the text decoration colour */ public void setTextDecorationColor(PDGamma textDecorationColor) { this.setColor(TEXT_DECORATION_COLOR, textDecorationColor); } /** * Gets the thickness of each line drawn as part of the text decoration * (TextDecorationThickness). * * @return the text decoration thickness */ public float getTextDecorationThickness() { return this.getNumber(TEXT_DECORATION_THICKNESS); } /** * Sets the thickness of each line drawn as part of the text decoration * (TextDecorationThickness). * * @param textDecorationThickness the text decoration thickness */ public void setTextDecorationThickness(float textDecorationThickness) { this.setNumber(TEXT_DECORATION_THICKNESS, textDecorationThickness); } /** * Sets the thickness of each line drawn as part of the text decoration * (TextDecorationThickness). * * @param textDecorationThickness the text decoration thickness */ public void setTextDecorationThickness(int textDecorationThickness) { this.setNumber(TEXT_DECORATION_THICKNESS, textDecorationThickness); } /** * Gets the type of text decoration (TextDecorationType). The default value * is {@link #TEXT_DECORATION_TYPE_NONE}. * * @return the type of text decoration */ public String getTextDecorationType() { return this.getName(TEXT_DECORATION_TYPE, TEXT_DECORATION_TYPE_NONE); } /** * Sets the type of text decoration (TextDecorationType). The value should * be one of: *
    *
  • {@link #TEXT_DECORATION_TYPE_NONE},
  • *
  • {@link #TEXT_DECORATION_TYPE_UNDERLINE},
  • *
  • {@link #TEXT_DECORATION_TYPE_OVERLINE},
  • *
  • {@link #TEXT_DECORATION_TYPE_LINE_THROUGH}.
  • *
* * @param textDecorationType the type of text decoration */ public void setTextDecorationType(String textDecorationType) { this.setName(TEXT_DECORATION_TYPE, textDecorationType); } /** * Gets the justification of the lines within a ruby assembly (RubyAlign). * The default value is {@link #RUBY_ALIGN_DISTRIBUTE}. * * @return the ruby alignment */ public String getRubyAlign() { return this.getName(RUBY_ALIGN, RUBY_ALIGN_DISTRIBUTE); } /** * Sets the justification of the lines within a ruby assembly (RubyAlign). * The value should be one of: *
    *
  • {@link #RUBY_ALIGN_START},
  • *
  • {@link #RUBY_ALIGN_CENTER},
  • *
  • {@link #RUBY_ALIGN_END},
  • *
  • {@link #RUBY_ALIGN_JUSTIFY},
  • *
  • {@link #RUBY_ALIGN_DISTRIBUTE},
  • *
* * @param rubyAlign the ruby alignment */ public void setRubyAlign(String rubyAlign) { this.setName(RUBY_ALIGN, rubyAlign); } /** * Gets the placement of the RT structure element relative to the RB element * in a ruby assembly (RubyPosition). The default value is * {@link #RUBY_POSITION_BEFORE}. * * @return the ruby position */ public String getRubyPosition() { return this.getName(RUBY_POSITION, RUBY_POSITION_BEFORE); } /** * Sets the placement of the RT structure element relative to the RB element * in a ruby assembly (RubyPosition). The value should be one of: *
    *
  • {@link #RUBY_POSITION_BEFORE},
  • *
  • {@link #RUBY_POSITION_AFTER},
  • *
  • {@link #RUBY_POSITION_WARICHU},
  • *
  • {@link #RUBY_POSITION_INLINE}.
  • *
* * @param rubyPosition the ruby position */ public void setRubyPosition(String rubyPosition) { this.setName(RUBY_POSITION, rubyPosition); } /** * Gets the orientation of glyphs when the inline-progression direction is * top to bottom or bottom to top (GlyphOrientationVertical). The default * value is {@link #GLYPH_ORIENTATION_VERTICAL_AUTO}. * * @return the vertical glyph orientation */ public String getGlyphOrientationVertical() { return this.getName(GLYPH_ORIENTATION_VERTICAL, GLYPH_ORIENTATION_VERTICAL_AUTO); } /** * Sets the orientation of glyphs when the inline-progression direction is * top to bottom or bottom to top (GlyphOrientationVertical). The value * should be one of: *
    *
  • {@link #GLYPH_ORIENTATION_VERTICAL_AUTO},
  • *
  • {@link #GLYPH_ORIENTATION_VERTICAL_MINUS_180_DEGREES},
  • *
  • {@link #GLYPH_ORIENTATION_VERTICAL_MINUS_90_DEGREES},
  • *
  • {@link #GLYPH_ORIENTATION_VERTICAL_ZERO_DEGREES},
  • *
  • {@link #GLYPH_ORIENTATION_VERTICAL_90_DEGREES},
  • *
  • {@link #GLYPH_ORIENTATION_VERTICAL_180_DEGREES},
  • *
  • {@link #GLYPH_ORIENTATION_VERTICAL_270_DEGREES},
  • *
  • {@link #GLYPH_ORIENTATION_VERTICAL_360_DEGREES}.
  • *
* * @param glyphOrientationVertical the vertical glyph orientation */ public void setGlyphOrientationVertical(String glyphOrientationVertical) { this.setName(GLYPH_ORIENTATION_VERTICAL, glyphOrientationVertical); } /** * Gets the number of columns in the content of the grouping element * (ColumnCount). The default value is 1. * * @return the column count */ public int getColumnCount() { return this.getInteger(COLUMN_COUNT, 1); } /** * Sets the number of columns in the content of the grouping element * (ColumnCount). * * @param columnCount the column count */ public void setColumnCount(int columnCount) { this.setInteger(COLUMN_COUNT, columnCount); } /** * Gets the desired space between adjacent columns in the inline-progression * direction (ColumnGap). * * @return the column gap (FLoat or array of floats) */ public Object getColumnGap() { return this.getNumberOrArrayOfNumber(COLUMN_GAP, UNSPECIFIED); } /** * Sets the desired space between all columns in the inline-progression * direction (ColumnGap). * * @param columnGap the column gap */ public void setColumnGap(float columnGap) { this.setNumber(COLUMN_GAP, columnGap); } /** * Sets the desired space between all columns in the inline-progression * direction (ColumnGap). * * @param columnGap the column gap */ public void setColumnGap(int columnGap) { this.setNumber(COLUMN_GAP, columnGap); } /** * Sets the desired space between adjacent columns in the inline-progression * direction (ColumnGap), the first element specifying the space between the * first and second columns, the second specifying the space between the * second and third columns, and so on. * * @param columnGaps the column gaps */ public void setColumnGaps(float[] columnGaps) { this.setArrayOfNumber(COLUMN_GAP, columnGaps); } /** * Gets the desired width of the columns, measured in default user space * units in the inline-progression direction (ColumnWidths). * * @return the column widths (Float or array of floats) */ public Object getColumnWidths() { return this.getNumberOrArrayOfNumber(COLUMN_WIDTHS, UNSPECIFIED); } /** * Sets the same column width for all columns (ColumnWidths). * * @param columnWidth the column width */ public void setAllColumnWidths(float columnWidth) { this.setNumber(COLUMN_WIDTHS, columnWidth); } /** * Sets the same column width for all columns (ColumnWidths). * * @param columnWidth the column width */ public void setAllColumnWidths(int columnWidth) { this.setNumber(COLUMN_WIDTHS, columnWidth); } /** * Sets the column widths for the columns separately (ColumnWidths). * * @param columnWidths the column widths */ public void setColumnWidths(float[] columnWidths) { this.setArrayOfNumber(COLUMN_WIDTHS, columnWidths); } @Override public String toString() { StringBuilder sb = new StringBuilder().append(super.toString()); if (this.isSpecified(PLACEMENT)) { sb.append(", Placement=").append(this.getPlacement()); } if (this.isSpecified(WRITING_MODE)) { sb.append(", WritingMode=").append(this.getWritingMode()); } if (this.isSpecified(BACKGROUND_COLOR)) { sb.append(", BackgroundColor=").append(this.getBackgroundColor()); } if (this.isSpecified(BORDER_COLOR)) { sb.append(", BorderColor=").append(this.getBorderColors()); } if (this.isSpecified(BORDER_STYLE)) { Object borderStyle = this.getBorderStyle(); sb.append(", BorderStyle="); if (borderStyle instanceof String[]) { sb.append(arrayToString((String[]) borderStyle)); } else { sb.append(borderStyle); } } if (this.isSpecified(BORDER_THICKNESS)) { Object borderThickness = this.getBorderThickness(); sb.append(", BorderThickness="); if (borderThickness instanceof float[]) { sb.append(arrayToString((float[]) borderThickness)); } else { sb.append(String.valueOf(borderThickness)); } } if (this.isSpecified(PADDING)) { Object padding = this.getPadding(); sb.append(", Padding="); if (padding instanceof float[]) { sb.append(arrayToString((float[]) padding)); } else { sb.append(String.valueOf(padding)); } } if (this.isSpecified(COLOR)) { sb.append(", Color=").append(this.getColor()); } if (this.isSpecified(SPACE_BEFORE)) { sb.append(", SpaceBefore=") .append(String.valueOf(this.getSpaceBefore())); } if (this.isSpecified(SPACE_AFTER)) { sb.append(", SpaceAfter=") .append(String.valueOf(this.getSpaceAfter())); } if (this.isSpecified(START_INDENT)) { sb.append(", StartIndent=") .append(String.valueOf(this.getStartIndent())); } if (this.isSpecified(END_INDENT)) { sb.append(", EndIndent=") .append(String.valueOf(this.getEndIndent())); } if (this.isSpecified(TEXT_INDENT)) { sb.append(", TextIndent=") .append(String.valueOf(this.getTextIndent())); } if (this.isSpecified(TEXT_ALIGN)) { sb.append(", TextAlign=").append(this.getTextAlign()); } if (this.isSpecified(BBOX)) { sb.append(", BBox=").append(this.getBBox()); } if (this.isSpecified(WIDTH)) { Object width = this.getWidth(); sb.append(", Width="); if (width instanceof Float) { sb.append(String.valueOf(width)); } else { sb.append(width); } } if (this.isSpecified(HEIGHT)) { Object height = this.getHeight(); sb.append(", Height="); if (height instanceof Float) { sb.append(String.valueOf(height)); } else { sb.append(height); } } if (this.isSpecified(BLOCK_ALIGN)) { sb.append(", BlockAlign=").append(this.getBlockAlign()); } if (this.isSpecified(INLINE_ALIGN)) { sb.append(", InlineAlign=").append(this.getInlineAlign()); } if (this.isSpecified(T_BORDER_STYLE)) { Object tBorderStyle = this.getTBorderStyle(); sb.append(", TBorderStyle="); if (tBorderStyle instanceof String[]) { sb.append(arrayToString((String[]) tBorderStyle)); } else { sb.append(tBorderStyle); } } if (this.isSpecified(T_PADDING)) { Object tPadding = this.getTPadding(); sb.append(", TPadding="); if (tPadding instanceof float[]) { sb.append(arrayToString((float[]) tPadding)); } else { sb.append(String.valueOf(tPadding)); } } if (this.isSpecified(BASELINE_SHIFT)) { sb.append(", BaselineShift=") .append(String.valueOf(this.getBaselineShift())); } if (this.isSpecified(LINE_HEIGHT)) { Object lineHeight = this.getLineHeight(); sb.append(", LineHeight="); if (lineHeight instanceof Float) { sb.append(String.valueOf(lineHeight)); } else { sb.append(lineHeight); } } if (this.isSpecified(TEXT_DECORATION_COLOR)) { sb.append(", TextDecorationColor=") .append(this.getTextDecorationColor()); } if (this.isSpecified(TEXT_DECORATION_THICKNESS)) { sb.append(", TextDecorationThickness=") .append(String.valueOf(this.getTextDecorationThickness())); } if (this.isSpecified(TEXT_DECORATION_TYPE)) { sb.append(", TextDecorationType=") .append(this.getTextDecorationType()); } if (this.isSpecified(RUBY_ALIGN)) { sb.append(", RubyAlign=").append(this.getRubyAlign()); } if (this.isSpecified(RUBY_POSITION)) { sb.append(", RubyPosition=").append(this.getRubyPosition()); } if (this.isSpecified(GLYPH_ORIENTATION_VERTICAL)) { sb.append(", GlyphOrientationVertical=") .append(this.getGlyphOrientationVertical()); } if (this.isSpecified(COLUMN_COUNT)) { sb.append(", ColumnCount=") .append(String.valueOf(this.getColumnCount())); } if (this.isSpecified(COLUMN_GAP)) { Object columnGap = this.getColumnGap(); sb.append(", ColumnGap="); if (columnGap instanceof float[]) { sb.append(arrayToString((float[]) columnGap)); } else { sb.append(String.valueOf(columnGap)); } } if (this.isSpecified(COLUMN_WIDTHS)) { Object columnWidth = this.getColumnWidths(); sb.append(", ColumnWidths="); if (columnWidth instanceof float[]) { sb.append(arrayToString((float[]) columnWidth)); } else { sb.append(String.valueOf(columnWidth)); } } return sb.toString(); } } PDListAttributeObject.java000066400000000000000000000104221320103431700352240ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import org.sejda.sambox.cos.COSDictionary; /** * A List attribute object. * * @author Johannes Koch */ public class PDListAttributeObject extends PDStandardAttributeObject { /** * standard attribute owner: List */ public static final String OWNER_LIST = "List"; protected static final String LIST_NUMBERING = "ListNumbering"; /** * ListNumbering: Circle: Open circular bullet */ public static final String LIST_NUMBERING_CIRCLE = "Circle"; /** * ListNumbering: Decimal: Decimal arabic numerals (1–9, 10–99, …) */ public static final String LIST_NUMBERING_DECIMAL = "Decimal"; /** * ListNumbering: Disc: Solid circular bullet */ public static final String LIST_NUMBERING_DISC = "Disc"; /** * ListNumbering: LowerAlpha: Lowercase letters (a, b, c, …) */ public static final String LIST_NUMBERING_LOWER_ALPHA = "LowerAlpha"; /** * ListNumbering: LowerRoman: Lowercase roman numerals (i, ii, iii, iv, …) */ public static final String LIST_NUMBERING_LOWER_ROMAN = "LowerRoman"; /** * ListNumbering: None: No autonumbering; Lbl elements (if present) contain arbitrary text * not subject to any numbering scheme */ public static final String LIST_NUMBERING_NONE = "None"; /** * ListNumbering: Square: Solid square bullet */ public static final String LIST_NUMBERING_SQUARE = "Square"; /** * ListNumbering: UpperAlpha: Uppercase letters (A, B, C, …) */ public static final String LIST_NUMBERING_UPPER_ALPHA = "UpperAlpha"; /** * ListNumbering: UpperRoman: Uppercase roman numerals (I, II, III, IV, …) */ public static final String LIST_NUMBERING_UPPER_ROMAN = "UpperRoman"; /** * Default constructor. */ public PDListAttributeObject() { this.setOwner(OWNER_LIST); } /** * Creates a new List attribute object with a given dictionary. * * @param dictionary the dictionary */ public PDListAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Gets the list numbering (ListNumbering). The default value is * {@link #LIST_NUMBERING_NONE}. * * @return the list numbering */ public String getListNumbering() { return this.getName(LIST_NUMBERING, LIST_NUMBERING_NONE); } /** * Sets the list numbering (ListNumbering). The value shall be one of the * following: *
    *
  • {@link #LIST_NUMBERING_NONE},
  • *
  • {@link #LIST_NUMBERING_DISC},
  • *
  • {@link #LIST_NUMBERING_CIRCLE},
  • *
  • {@link #LIST_NUMBERING_SQUARE},
  • *
  • {@link #LIST_NUMBERING_DECIMAL},
  • *
  • {@link #LIST_NUMBERING_UPPER_ROMAN},
  • *
  • {@link #LIST_NUMBERING_LOWER_ROMAN},
  • *
  • {@link #LIST_NUMBERING_UPPER_ALPHA},
  • *
  • {@link #LIST_NUMBERING_LOWER_ALPHA}.
  • *
* * @param listNumbering the list numbering */ public void setListNumbering(String listNumbering) { this.setName(LIST_NUMBERING, listNumbering); } @Override public String toString() { StringBuilder sb = new StringBuilder().append(super.toString()); if (this.isSpecified(LIST_NUMBERING)) { sb.append(", ListNumbering=").append(this.getListNumbering()); } return sb.toString(); } } PDPrintFieldAttributeObject.java000066400000000000000000000107261320103431700363600ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import org.sejda.sambox.cos.COSDictionary; /** * A PrintField attribute object. * * @author Johannes Koch */ public class PDPrintFieldAttributeObject extends PDStandardAttributeObject { /** * standard attribute owner: PrintField */ public static final String OWNER_PRINT_FIELD = "PrintField"; private static final String ROLE = "Role"; private static final String CHECKED = "checked"; private static final String DESC = "Desc"; /** * role: rb: Radio button */ public static final String ROLE_RB = "rb"; /** * role: cb: Check box */ public static final String ROLE_CB = "cb"; /** * role: pb: Push button */ public static final String ROLE_PB = "pb"; /** * role: tv: Text-value field */ public static final String ROLE_TV = "tv"; /** * checked state: on */ public static final String CHECKED_STATE_ON = "on"; /** * checked state: off */ public static final String CHECKED_STATE_OFF = "off"; /** * checked state: neutral */ public static final String CHECKED_STATE_NEUTRAL = "neutral"; /** * Default constructor. */ public PDPrintFieldAttributeObject() { this.setOwner(OWNER_PRINT_FIELD); } /** * Creates a new PrintField attribute object with a given dictionary. * * @param dictionary the dictionary */ public PDPrintFieldAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Gets the role. * * @return the role */ public String getRole() { return this.getName(ROLE); } /** * Sets the role. The value of Role shall be one of the following: *
    *
  • {@link #ROLE_RB},
  • *
  • {@link #ROLE_CB},
  • *
  • {@link #ROLE_PB},
  • *
  • {@link #ROLE_TV}.
  • *
* * @param role the role */ public void setRole(String role) { this.setName(ROLE, role); } /** * Gets the checked state. The default value is {@link #CHECKED_STATE_OFF}. * * @return the checked state */ public String getCheckedState() { return this.getName(CHECKED, CHECKED_STATE_OFF); } /** * Sets the checked state. The value shall be one of: *
    *
  • {@link #CHECKED_STATE_ON},
  • *
  • {@link #CHECKED_STATE_OFF} (default), or
  • *
  • {@link #CHECKED_STATE_NEUTRAL}.
  • *
* * @param checkedState the checked state */ public void setCheckedState(String checkedState) { this.setName(CHECKED, checkedState); } /** * Gets the alternate name of the field (Desc). * * @return the alternate name of the field */ public String getAlternateName() { return this.getString(DESC); } /** * Sets the alternate name of the field (Desc). * * @param alternateName the alternate name of the field */ public void setAlternateName(String alternateName) { this.setString(DESC, alternateName); } @Override public String toString() { StringBuilder sb = new StringBuilder().append(super.toString()); if (this.isSpecified(ROLE)) { sb.append(", Role=").append(this.getRole()); } if (this.isSpecified(CHECKED)) { sb.append(", Checked=").append(this.getCheckedState()); } if (this.isSpecified(DESC)) { sb.append(", Desc=").append(this.getAlternateName()); } return sb.toString(); } } PDStandardAttributeObject.java000066400000000000000000000313721320103431700360600ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import static java.util.Objects.nonNull; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.documentinterchange.logicalstructure.PDAttributeObject; import org.sejda.sambox.pdmodel.graphics.color.PDGamma; /** * A standard attribute object. * * @author Johannes Koch */ public abstract class PDStandardAttributeObject extends PDAttributeObject { /** * Default constructor. */ public PDStandardAttributeObject() { } /** * Creates a new standard attribute object with a given dictionary. * * @param dictionary the dictionary */ public PDStandardAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Is the attribute with the given name specified in this attribute object? * * @param name the attribute name * @return true if the attribute is specified, * false otherwise */ public boolean isSpecified(String name) { return this.getCOSObject().getDictionaryObject(name) != null; } /** * Gets a string attribute value. * * @param name the attribute name * @return the string attribute value */ protected String getString(String name) { return this.getCOSObject().getString(name); } /** * Sets a string attribute value. * * @param name the attribute name * @param value the string attribute value */ protected void setString(String name, String value) { COSBase oldBase = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setString(name, value); COSBase newBase = this.getCOSObject().getDictionaryObject(name); this.potentiallyNotifyChanged(oldBase, newBase); } /** * Gets an array of strings. * * @param name the attribute name * @return the array of strings */ protected String[] getArrayOfString(String name) { COSArray array = this.getCOSObject().getDictionaryObject(name, COSArray.class); if (nonNull(array)) { String[] strings = new String[array.size()]; for (int i = 0; i < array.size(); i++) { strings[i] = ((COSName) array.getObject(i)).getName(); } return strings; } return null; } /** * Sets an array of strings. * * @param name the attribute name * @param values the array of strings */ protected void setArrayOfString(String name, String[] values) { COSBase oldBase = this.getCOSObject().getDictionaryObject(name); COSArray array = new COSArray(); for (String value : values) { array.add(COSString.parseLiteral(value)); } this.getCOSObject().setItem(name, array); COSBase newBase = this.getCOSObject().getDictionaryObject(name); this.potentiallyNotifyChanged(oldBase, newBase); } /** * Gets a name value. * * @param name the attribute name * @return the name value */ protected String getName(String name) { return this.getCOSObject().getNameAsString(name); } /** * Gets a name value. * * @param name the attribute name * @param defaultValue the default value * @return the name value */ protected String getName(String name, String defaultValue) { return this.getCOSObject().getNameAsString(name, defaultValue); } /** * Gets a name value or array of name values. * * @param name the attribute name * @param defaultValue the default value * @return a String or array of Strings */ protected Object getNameOrArrayOfName(String name, String defaultValue) { COSBase v = this.getCOSObject().getDictionaryObject(name); if (v instanceof COSArray) { COSArray array = (COSArray) v; String[] names = new String[array.size()]; for (int i = 0; i < array.size(); i++) { COSBase item = array.getObject(i); if (item instanceof COSName) { names[i] = ((COSName) item).getName(); } } return names; } if (v instanceof COSName) { return ((COSName) v).getName(); } return defaultValue; } /** * Sets a name value. * * @param name the attribute name * @param value the name value */ protected void setName(String name, String value) { COSBase oldBase = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setName(name, value); COSBase newBase = this.getCOSObject().getDictionaryObject(name); this.potentiallyNotifyChanged(oldBase, newBase); } /** * Sets an array of name values. * * @param name the attribute name * @param values the array of name values */ protected void setArrayOfName(String name, String[] values) { COSBase oldBase = this.getCOSObject().getDictionaryObject(name); COSArray array = new COSArray(); for (String value : values) { array.add(COSName.getPDFName(value)); } this.getCOSObject().setItem(name, array); COSBase newBase = this.getCOSObject().getDictionaryObject(name); this.potentiallyNotifyChanged(oldBase, newBase); } /** * Gets a number or a name value. * * @param name the attribute name * @param defaultValue the default name * @return a Float or a String */ protected Object getNumberOrName(String name, String defaultValue) { COSBase value = this.getCOSObject().getDictionaryObject(name); if (value instanceof COSNumber) { return ((COSNumber) value).floatValue(); } if (value instanceof COSName) { return ((COSName) value).getName(); } return defaultValue; } /** * Gets an integer. * * @param name the attribute name * @param defaultValue the default value * @return the integer */ protected int getInteger(String name, int defaultValue) { return this.getCOSObject().getInt(name, defaultValue); } /** * Sets an integer. * * @param name the attribute name * @param value the integer */ protected void setInteger(String name, int value) { COSBase oldBase = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setInt(name, value); COSBase newBase = this.getCOSObject().getDictionaryObject(name); this.potentiallyNotifyChanged(oldBase, newBase); } /** * Gets a number value. * * @param name the attribute name * @param defaultValue the default value * @return the number value */ protected float getNumber(String name, float defaultValue) { return this.getCOSObject().getFloat(name, defaultValue); } /** * Gets a number value. * * @param name the attribute name * @return the number value */ protected float getNumber(String name) { return this.getCOSObject().getFloat(name); } /** * An "unspecified" default float value. */ protected static final float UNSPECIFIED = -1.f; /** * Gets a number or an array of numbers. * * @param name the attribute name * @param defaultValue the default value * @return a Float or an array of floats */ protected Object getNumberOrArrayOfNumber(String name, float defaultValue) { COSBase v = this.getCOSObject().getDictionaryObject(name); if (v instanceof COSArray) { COSArray array = (COSArray) v; float[] values = new float[array.size()]; for (int i = 0; i < array.size(); i++) { COSBase item = array.getObject(i); if (item instanceof COSNumber) { values[i] = ((COSNumber) item).floatValue(); } } return values; } if (v instanceof COSNumber) { return ((COSNumber) v).floatValue(); } if (defaultValue == UNSPECIFIED) { return null; } return defaultValue; } /** * Sets a float number. * * @param name the attribute name * @param value the float number */ protected void setNumber(String name, float value) { COSBase oldBase = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setFloat(name, value); COSBase newBase = this.getCOSObject().getDictionaryObject(name); this.potentiallyNotifyChanged(oldBase, newBase); } /** * Sets an integer number. * * @param name the attribute name * @param value the integer number */ protected void setNumber(String name, int value) { COSBase oldBase = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setInt(name, value); COSBase newBase = this.getCOSObject().getDictionaryObject(name); this.potentiallyNotifyChanged(oldBase, newBase); } /** * Sets an array of float numbers. * * @param name the attribute name * @param values the float numbers */ protected void setArrayOfNumber(String name, float[] values) { COSArray array = new COSArray(); for (float value : values) { array.add(new COSFloat(value)); } COSBase oldBase = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setItem(name, array); COSBase newBase = this.getCOSObject().getDictionaryObject(name); this.potentiallyNotifyChanged(oldBase, newBase); } /** * Gets a colour. * * @param name the attribute name * @return the colour */ protected PDGamma getColor(String name) { COSArray c = (COSArray) this.getCOSObject().getDictionaryObject(name); if (c != null) { return new PDGamma(c); } return null; } /** * Gets a single colour or four colours. * * @param name the attribute name * @return the single ({@link PDGamma}) or a ({@link PDFourColours}) */ protected Object getColorOrFourColors(String name) { COSArray array = (COSArray) this.getCOSObject().getDictionaryObject(name); if (array == null) { return null; } if (array.size() == 3) { // only one colour return new PDGamma(array); } else if (array.size() == 4) { return new PDFourColours(array); } return null; } /** * Sets a colour. * * @param name the attribute name * @param value the colour */ protected void setColor(String name, PDGamma value) { COSBase oldValue = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setItem(name, value); COSBase newValue = value == null ? null : value.getCOSObject(); this.potentiallyNotifyChanged(oldValue, newValue); } /** * Sets four colours. * * @param name the attribute name * @param value the four colours */ protected void setFourColors(String name, PDFourColours value) { COSBase oldValue = this.getCOSObject().getDictionaryObject(name); this.getCOSObject().setItem(name, value); COSBase newValue = value == null ? null : value.getCOSObject(); this.potentiallyNotifyChanged(oldValue, newValue); } } PDTableAttributeObject.java000066400000000000000000000142131320103431700353420ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import org.sejda.sambox.cos.COSDictionary; /** * A Table attribute object. * * @author Johannes Koch */ public class PDTableAttributeObject extends PDStandardAttributeObject { /** * standard attribute owner: Table */ public static final String OWNER_TABLE = "Table"; protected static final String ROW_SPAN = "RowSpan"; protected static final String COL_SPAN = "ColSpan"; protected static final String HEADERS = "Headers"; protected static final String SCOPE = "Scope"; protected static final String SUMMARY = "Summary"; /** * Scope: Both */ public static final String SCOPE_BOTH = "Both"; /** * Scope: Column */ public static final String SCOPE_COLUMN = "Column"; /** * Scope: Row */ public static final String SCOPE_ROW = "Row"; /** * Default constructor. */ public PDTableAttributeObject() { this.setOwner(OWNER_TABLE); } /** * Creates a new Table attribute object with a given dictionary. * * @param dictionary the dictionary */ public PDTableAttributeObject(COSDictionary dictionary) { super(dictionary); } /** * Gets the number of rows in the enclosing table that shall be spanned by * the cell (RowSpan). The default value is 1. * * @return the row span */ public int getRowSpan() { return this.getInteger(ROW_SPAN, 1); } /** * Sets the number of rows in the enclosing table that shall be spanned by * the cell (RowSpan). * * @param rowSpan the row span */ public void setRowSpan(int rowSpan) { this.setInteger(ROW_SPAN, rowSpan); } /** * Gets the number of columns in the enclosing table that shall be spanned * by the cell (ColSpan). The default value is 1. * * @return the column span */ public int getColSpan() { return this.getInteger(COL_SPAN, 1); } /** * Sets the number of columns in the enclosing table that shall be spanned * by the cell (ColSpan). * * @param colSpan the column span */ public void setColSpan(int colSpan) { this.setInteger(COL_SPAN, colSpan); } /** * Gets the headers (Headers). An array of byte strings, where each string * shall be the element identifier (see the * {@link org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement#getElementIdentifier()}) for a TH structure * element that shall be used as a header associated with this cell. * * @return the headers. */ public String[] getHeaders() { return this.getArrayOfString(HEADERS); } /** * Sets the headers (Headers). An array of byte strings, where each string * shall be the element identifier (see the * {@link org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement#getElementIdentifier()}) for a TH structure * element that shall be used as a header associated with this cell. * * @param headers the headers */ public void setHeaders(String[] headers) { this.setArrayOfString(HEADERS, headers); } /** * Gets the scope (Scope). It shall reflect whether the header cell applies * to the rest of the cells in the row that contains it, the column that * contains it, or both the row and the column that contain it. * * @return the scope */ public String getScope() { return this.getName(SCOPE); } /** * Sets the scope (Scope). It shall reflect whether the header cell applies * to the rest of the cells in the row that contains it, the column that * contains it, or both the row and the column that contain it. The value * shall be one of the following: *
    *
  • {@link #SCOPE_ROW},
  • *
  • {@link #SCOPE_COLUMN}, or
  • *
  • {@link #SCOPE_BOTH}.
  • *
* * @param scope the scope */ public void setScope(String scope) { this.setName(SCOPE, scope); } /** * Gets the summary of the table’s purpose and structure. * * @return the summary */ public String getSummary() { return this.getString(SUMMARY); } /** * Sets the summary of the table’s purpose and structure. * * @param summary the summary */ public void setSummary(String summary) { this.setString(SUMMARY, summary); } @Override public String toString() { StringBuilder sb = new StringBuilder().append(super.toString()); if (this.isSpecified(ROW_SPAN)) { sb.append(", RowSpan=").append(String.valueOf(this.getRowSpan())); } if (this.isSpecified(COL_SPAN)) { sb.append(", ColSpan=").append(String.valueOf(this.getColSpan())); } if (this.isSpecified(HEADERS)) { sb.append(", Headers=").append(arrayToString(this.getHeaders())); } if (this.isSpecified(SCOPE)) { sb.append(", Scope=").append(this.getScope()); } if (this.isSpecified(SUMMARY)) { sb.append(", Summary=").append(this.getSummary()); } return sb.toString(); } } StandardStructureTypes.java000066400000000000000000000133271320103431700355670ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/* * 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. */ package org.sejda.sambox.pdmodel.documentinterchange.taggedpdf; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The standard structure types. * * @author Johannes Koch */ public class StandardStructureTypes { /** * Log instance. */ private static final Logger LOG = LoggerFactory.getLogger(StandardStructureTypes.class); private StandardStructureTypes() { } // Grouping Elements /** * Document */ public static final String DOCUMENT = "Document"; /** * Part */ public static final String PART = "Part"; /** * Art */ public static final String ART = "Art"; /** * Sect */ public static final String SECT = "Sect"; /** * Div */ public static final String DIV = "Div"; /** * BlockQuote */ public static final String BLOCK_QUOTE = "BlockQuote"; /** * Caption */ public static final String CAPTION = "Caption"; /** * TOC */ public static final String TOC = "TOC"; /** * TOCI */ public static final String TOCI = "TOCI"; /** * Index */ public static final String INDEX = "Index"; /** * NonStruct */ public static final String NON_STRUCT = "NonStruct"; /** * Private */ public static final String PRIVATE = "Private"; // Block-Level Structure Elements /** * P */ public static final String P = "P"; /** * H */ public static final String H = "H"; /** * H1 */ public static final String H1 = "H1"; /** * H2 */ public static final String H2 = "H2"; /** * H3 */ public static final String H3 = "H3"; /** * H4 */ public static final String H4 = "H4"; /** * H5 */ public static final String H5 = "H5"; /** * H6 */ public static final String H6 = "H6"; /** * L */ public static final String L = "L"; /** * LI */ public static final String LI = "LI"; /** * Lbl */ public static final String LBL = "Lbl"; /** * LBody */ public static final String L_BODY = "LBody"; /** * Table */ public static final String TABLE = "Table"; /** * TR */ public static final String TR = "TR"; /** * TH */ public static final String TH = "TH"; /** * TD */ public static final String TD = "TD"; /** * THead */ public static final String T_HEAD = "THead"; /** * TBody */ public static final String T_BODY = "TBody"; /** * TFoot */ public static final String T_FOOT = "TFoot"; // Inline-Level Structure Elements /** * Span */ public static final String SPAN = "Span"; /** * Quote */ public static final String QUOTE = "Quote"; /** * Note */ public static final String NOTE = "Note"; /** * Reference */ public static final String REFERENCE = "Reference"; /** * BibEntry */ public static final String BIB_ENTRY = "BibEntry"; /** * Code */ public static final String CODE = "Code"; /** * Link */ public static final String LINK = "Link"; /** * Annot */ public static final String ANNOT = "Annot"; /** * Ruby */ public static final String RUBY = "Ruby"; /** * RB */ public static final String RB = "RB"; /** * RT */ public static final String RT = "RT"; /** * RP */ public static final String RP = "RP"; /** * Warichu */ public static final String WARICHU = "Warichu"; /** * WT */ public static final String WT = "WT"; /** * WP */ public static final String WP = "WP"; // Illustration Elements /** * Figure */ public static final String Figure = "Figure"; /** * Formula */ public static final String FORMULA = "Formula"; /** * Form */ public static final String FORM = "Form"; /** * All standard structure types. */ public static List types = new ArrayList(); static { Field[] fields = StandardStructureTypes.class.getFields(); for (Field field : fields) { if (Modifier.isFinal(field.getModifiers())) { try { types.add(field.get(null).toString()); } catch (IllegalArgumentException | IllegalAccessException e) { LOG.error(e.getMessage(), e); } } } Collections.sort(types); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/documentinterchange/taggedpdf/package.html000066400000000000000000000020151320103431700325460ustar00rootroot00000000000000 The tagged PDF package provides a mechanism for incorporating "tags" (standard structure types and attributes) into a PDF file. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/000077500000000000000000000000001320103431700245065ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/AccessPermission.java000066400000000000000000000341411320103431700306260ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; /** * This class represents the access permissions to a document. * These permissions are specified in the PDF format specifications, they include: *
    *
  • print the document
  • *
  • modify the content of the document
  • *
  • copy or extract content of the document
  • *
  • add or modify annotations
  • *
  • fill in interactive form fields
  • *
  • extract text and graphics for accessibility to visually impaired people
  • *
  • assemble the document
  • *
  • print in degraded quality
  • *
* * This class can be used to protect a document by assigning access permissions to recipients. * In this case, it must be used with a specific ProtectionPolicy. * * * When a document is decrypted, it has a currentAccessPermission property which is the access permissions * granted to the user who decrypted the document. * * @see ProtectionPolicy * @see org.apache.pdfbox.pdmodel.PDDocument#getCurrentAccessPermission() * * @author Ben Litchfield * @author Benoit Guillon * */ // TODO make this immutable public class AccessPermission { private static final int DEFAULT_PERMISSIONS = ~3;//bits 0 & 1 need to be zero private static final int PRINT_BIT = 3; private static final int MODIFICATION_BIT = 4; private static final int EXTRACT_BIT = 5; private static final int MODIFY_ANNOTATIONS_BIT = 6; private static final int FILL_IN_FORM_BIT = 9; private static final int EXTRACT_FOR_ACCESSIBILITY_BIT = 10; private static final int ASSEMBLE_DOCUMENT_BIT = 11; private static final int DEGRADED_PRINT_BIT = 12; private int bytes = DEFAULT_PERMISSIONS; private boolean readOnly = false; /** * Create a new access permission object. * By default, all permissions are granted. */ public AccessPermission() { bytes = DEFAULT_PERMISSIONS; } /** * Create a new access permission object from a byte array. * Bytes are ordered most significant byte first. * * @param b the bytes as defined in PDF specs */ public AccessPermission(byte[] b) { bytes = 0; bytes |= b[0] & 0xFF; bytes <<= 8; bytes |= b[1] & 0xFF; bytes <<= 8; bytes |= b[2] & 0xFF; bytes <<= 8; bytes |= b[3] & 0xFF; } /** * Creates a new access permission object from a single integer. * * @param permissions The permission bits. */ public AccessPermission( int permissions ) { bytes = permissions; } private boolean isPermissionBitOn( int bit ) { return (bytes & (1 << (bit-1))) != 0; } private boolean setPermissionBit( int bit, boolean value ) { int permissions = bytes; if( value ) { permissions = permissions | (1 << (bit-1)); } else { permissions = permissions & (~(1 << (bit - 1))); } bytes = permissions; return (bytes & (1 << (bit-1))) != 0; } /** * This will tell if the access permission corresponds to owner * access permission (no restriction). * * @return true if the access permission does not restrict the use of the document */ public boolean isOwnerPermission() { return (this.canAssembleDocument() && this.canExtractContent() && this.canExtractForAccessibility() && this.canFillInForm() && this.canModify() && this.canModifyAnnotations() && this.canPrint() && this.canPrintDegraded() ); } /** * returns an access permission object for a document owner. * * @return A standard owner access permission set. */ public static AccessPermission getOwnerAccessPermission() { AccessPermission ret = new AccessPermission(); ret.setCanAssembleDocument(true); ret.setCanExtractContent(true); ret.setCanExtractForAccessibility(true); ret.setCanFillInForm(true); ret.setCanModify(true); ret.setCanModifyAnnotations(true); ret.setCanPrint(true); ret.setCanPrintDegraded(true); return ret; } /** * This returns an integer representing the access permissions. * This integer can be used for public key encryption. This format * is not documented in the PDF specifications but is necessary for compatibility * with Adobe Acrobat and Adobe Reader. * * @return the integer representing access permissions */ public int getPermissionBytesForPublicKey() { setPermissionBit(1, true); setPermissionBit(7, false); setPermissionBit(8, false); for(int i=13; i<=32; i++) { setPermissionBit(i, false); } return bytes; } /** * The returns an integer representing the access permissions. * This integer can be used for standard PDF encryption as specified * in the PDF specifications. * * @return the integer representing the access permissions */ public int getPermissionBytes() { return bytes; } /** * This will tell if the user can print. * * @return true If supplied with the user password they are allowed to print. */ public boolean canPrint() { return isPermissionBitOn( PRINT_BIT ); } /** * Set if the user can print. *

* This method will have no effect if the object is in read only mode * * @param allowPrinting A boolean determining if the user can print. */ public void setCanPrint( boolean allowPrinting ) { if(!readOnly) { setPermissionBit( PRINT_BIT, allowPrinting ); } } /** * This will tell if the user can modify contents of the document. * * @return true If supplied with the user password they are allowed to modify the document */ public boolean canModify() { return isPermissionBitOn( MODIFICATION_BIT ); } /** * Set if the user can modify the document. *

* This method will have no effect if the object is in read only mode * * @param allowModifications A boolean determining if the user can modify the document. */ public void setCanModify( boolean allowModifications ) { if(!readOnly) { setPermissionBit( MODIFICATION_BIT, allowModifications ); } } /** * This will tell if the user can extract text and images from the PDF document. * * @return true If supplied with the user password they are allowed to extract content * from the PDF document */ public boolean canExtractContent() { return isPermissionBitOn( EXTRACT_BIT ); } /** * Set if the user can extract content from the document. *

* This method will have no effect if the object is in read only mode * * @param allowExtraction A boolean determining if the user can extract content * from the document. */ public void setCanExtractContent( boolean allowExtraction ) { if(!readOnly) { setPermissionBit( EXTRACT_BIT, allowExtraction ); } } /** * This will tell if the user can add or modify text annotations and fill in interactive forms * fields and, if {@link #canModify() canModify()} returns true, create or modify interactive * form fields (including signature fields). Note that if * {@link #canFillInForm() canFillInForm()} returns true, it is still possible to fill in * interactive forms (including signature fields) even if this method here returns false. * * @return true If supplied with the user password they are allowed to modify annotations. */ public boolean canModifyAnnotations() { return isPermissionBitOn( MODIFY_ANNOTATIONS_BIT ); } /** * Set if the user can add or modify text annotations and fill in interactive forms fields and, * (including signature fields). Note that if {@link #canFillInForm() canFillInForm()} returns * true, it is still possible to fill in interactive forms (including signature fields) even the * parameter here is false. *

* This method will have no effect if the object is in read only mode. * * @param allowAnnotationModification A boolean determining the new setting. */ public void setCanModifyAnnotations( boolean allowAnnotationModification ) { if(!readOnly) { setPermissionBit( MODIFY_ANNOTATIONS_BIT, allowAnnotationModification ); } } /** * This will tell if the user can fill in interactive form fields (including signature fields) * even if {@link #canModifyAnnotations() canModifyAnnotations()} returns false. * * @return true If supplied with the user password they are allowed to fill in form fields. */ public boolean canFillInForm() { return isPermissionBitOn( FILL_IN_FORM_BIT ); } /** * Set if the user can fill in interactive form fields (including signature fields) even if * {@link #canModifyAnnotations() canModifyAnnotations()} returns false. Therefore, if you want * to prevent a user from filling in interactive form fields, you need to call * {@link #setCanModifyAnnotations(boolean) setCanModifyAnnotations(false)} as well. *

* This method will have no effect if the object is in read only mode. * * @param allowFillingInForm A boolean determining if the user can fill in interactive forms. */ public void setCanFillInForm( boolean allowFillingInForm ) { if(!readOnly) { setPermissionBit( FILL_IN_FORM_BIT, allowFillingInForm ); } } /** * This will tell if the user can extract text and images from the PDF document * for accessibility purposes. * * @return true If supplied with the user password they are allowed to extract content * from the PDF document */ public boolean canExtractForAccessibility() { return isPermissionBitOn( EXTRACT_FOR_ACCESSIBILITY_BIT ); } /** * Set if the user can extract content from the document for accessibility purposes. *

* This method will have no effect if the object is in read only mode. * * @param allowExtraction A boolean determining if the user can extract content * from the document. */ public void setCanExtractForAccessibility( boolean allowExtraction ) { if(!readOnly) { setPermissionBit( EXTRACT_FOR_ACCESSIBILITY_BIT, allowExtraction ); } } /** * This will tell if the user can insert/rotate/delete pages. * * @return true If supplied with the user password they are allowed to extract content * from the PDF document */ public boolean canAssembleDocument() { return isPermissionBitOn( ASSEMBLE_DOCUMENT_BIT ); } /** * Set if the user can insert/rotate/delete pages. *

* This method will have no effect if the object is in read only mode * * @param allowAssembly A boolean determining if the user can assemble the document. */ public void setCanAssembleDocument( boolean allowAssembly ) { if(!readOnly) { setPermissionBit( ASSEMBLE_DOCUMENT_BIT, allowAssembly ); } } /** * This will tell if the user can print the document in a degraded format. * * @return true If supplied with the user password they are allowed to print the * document in a degraded format. */ public boolean canPrintDegraded() { return isPermissionBitOn( DEGRADED_PRINT_BIT ); } /** * Set if the user can print the document in a degraded format. *

* This method will have no effect if the object is in read only mode * * @param canPrintDegraded A boolean determining if the user can print the document in a degraded format. */ public void setCanPrintDegraded(boolean canPrintDegraded) { if(!readOnly) { setPermissionBit(DEGRADED_PRINT_BIT, canPrintDegraded); } } /** * Locks the access permission read only (ie, the setters will have no effects). * After that, the object cannot be unlocked. * This method is used for the currentAccessPermssion of a document to avoid * users to change access permission. */ public void setReadOnly() { readOnly = true; } /** * This will tell if the object has been set as read only. * * @return true if the object is in read only mode. */ public boolean isReadOnly() { return readOnly; } /** * Indicates if any revision 3 access permission is set or not. * * @return true if any revision 3 access permission is set */ protected boolean hasAnyRevision3PermissionSet() { if (canFillInForm()) { return true; } if (canExtractForAccessibility()) { return true; } if (canAssembleDocument()) { return true; } return canPrintDegraded(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/DecryptionMaterial.java000066400000000000000000000021321320103431700311460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; /** * This class represents data required to decrypt PDF documents. This can * be a password for standard security or a X509 certificate with a private * key for public key security. * * @author Benoit Guillon */ public abstract class DecryptionMaterial { } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/InvalidPasswordException.java000066400000000000000000000023151320103431700323420ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.io.IOException; /** * Indicates that an invalid password was supplied. * @author Ben Litchfield */ public class InvalidPasswordException extends IOException { /** * Creates a new InvalidPasswordException. * @param message A msg to go with this exception. */ InvalidPasswordException( String message ) { super( message ); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/MessageDigests.java000066400000000000000000000033221320103431700302600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Utility class for creating MessageDigest instances. * @author John Hewson */ final class MessageDigests { private MessageDigests() { } /** * @return MD5 message digest */ static MessageDigest getMD5() { try { return MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { // should never happen throw new RuntimeException(e); } } /** * @return SHA-1 message digest */ static MessageDigest getSHA1() { try { return MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { // should never happen throw new RuntimeException(e); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/PDCryptFilterDictionary.java000066400000000000000000000065351320103431700321030ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.io.IOException; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This class is a specialized view of the crypt filter dictionary of a PDF document. * It contains a low level dictionary (COSDictionary) and provides the methods to * manage its fields. * */ public class PDCryptFilterDictionary { /** * COS crypt filter dictionary. */ protected COSDictionary cryptFilterDictionary = null; /** * creates a new empty crypt filter dictionary. */ public PDCryptFilterDictionary() { cryptFilterDictionary = new COSDictionary(); } /** * creates a new crypt filter dictionary from the low level dictionary provided. * @param d the low level dictionary that will be managed by the newly created object */ public PDCryptFilterDictionary(COSDictionary d) { cryptFilterDictionary = d; } /** * This will get the dictionary associated with this crypt filter dictionary. * * @return The COS dictionary that this object wraps. */ public COSDictionary getCOSDictionary() { return cryptFilterDictionary; } /** * This will set the number of bits to use for the crypt filter algorithm. * * @param length The new key length. */ public void setLength(int length) { cryptFilterDictionary.setInt(COSName.LENGTH, length); } /** * This will return the Length entry of the crypt filter dictionary.

* The length in bits for the crypt filter algorithm. This will return a multiple of 8. * * @return The length in bits for the encryption algorithm */ public int getLength() { return cryptFilterDictionary.getInt( COSName.LENGTH, 40 ); } /** * This will set the crypt filter method. * Allowed values are: NONE, V2, AESV2, AESV3 * * @param cfm name of the crypt filter method. * * @throws IOException If there is an error setting the data. */ public void setCryptFilterMethod(COSName cfm) { cryptFilterDictionary.setItem( COSName.CFM, cfm ); } /** * This will return the crypt filter method. * Allowed values are: NONE, V2, AESV2, AESV3 * * @return the name of the crypt filter method. * * @throws IOException If there is an error accessing the data. */ public COSName getCryptFilterMethod() { return (COSName)cryptFilterDictionary.getDictionaryObject( COSName.CFM ); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/PDEncryption.java000066400000000000000000000427721320103431700277430ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import static java.util.Objects.nonNull; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; /** * This class is a specialized view of the encryption dictionary of a PDF document. It contains a low level dictionary * (COSDictionary) and provides the methods to manage its fields. * * The available fields are the ones who are involved by standard security handler and public key security handler. * * @author Ben Litchfield * @author Benoit Guillon */ public class PDEncryption { /** * See PDF Reference 1.4 Table 3.13. */ public static final int VERSION0_UNDOCUMENTED_UNSUPPORTED = 0; /** * See PDF Reference 1.4 Table 3.13. */ public static final int VERSION1_40_BIT_ALGORITHM = 1; /** * See PDF Reference 1.4 Table 3.13. */ public static final int VERSION2_VARIABLE_LENGTH_ALGORITHM = 2; /** * See PDF Reference 1.4 Table 3.13. */ public static final int VERSION3_UNPUBLISHED_ALGORITHM = 3; /** * See PDF Reference 1.4 Table 3.13. */ public static final int VERSION4_SECURITY_HANDLER = 4; /** * The default length for the encryption key. */ public static final int DEFAULT_LENGTH = 40; /** * The default version, according to the PDF Reference. */ public static final int DEFAULT_VERSION = VERSION0_UNDOCUMENTED_UNSUPPORTED; private final COSDictionary dictionary; private SecurityHandler securityHandler; /** * creates a new empty encryption dictionary. */ public PDEncryption() { dictionary = new COSDictionary(); } /** * creates a new encryption dictionary from the low level dictionary provided. * * @param dictionary a COS encryption dictionary */ public PDEncryption(COSDictionary dictionary) { this.dictionary = dictionary; securityHandler = SecurityHandlerFactory.INSTANCE.newSecurityHandlerForFilter(getFilter()); } /** * Returns the security handler specified in the dictionary's Filter entry. * * @return a security handler instance * @throws IOException if there is no security handler available which matches the Filter */ public SecurityHandler getSecurityHandler() throws IOException { if (securityHandler == null) { throw new IOException("No security handler for filter " + getFilter()); } return securityHandler; } /** * Sets the security handler used in this encryption dictionary * * @param securityHandler new security handler */ public void setSecurityHandler(SecurityHandler securityHandler) { this.securityHandler = securityHandler; // TODO set Filter (currently this is done by the security handlers) } /** * Returns true if the security handler specified in the dictionary's Filter is available. * * @return true if the security handler is available */ public boolean hasSecurityHandler() { return securityHandler == null; } /** * This will get the dictionary associated with this encryption dictionary. * * @return The COS dictionary that this object wraps. */ public COSDictionary getCOSDictionary() { return dictionary; } /** * Sets the filter entry of the encryption dictionary. * * @param filter The filter name. */ public void setFilter(String filter) { dictionary.setItem(COSName.FILTER, COSName.getPDFName(filter)); } /** * Get the name of the filter. * * @return The filter name contained in this encryption dictionary. */ public final String getFilter() { return dictionary.getNameAsString(COSName.FILTER); } /** * Get the name of the subfilter. * * @return The subfilter name contained in this encryption dictionary. */ public String getSubFilter() { return dictionary.getNameAsString(COSName.SUB_FILTER); } /** * Set the subfilter entry of the encryption dictionary. * * @param subfilter The value of the subfilter field. */ public void setSubFilter(String subfilter) { dictionary.setName(COSName.SUB_FILTER, subfilter); } /** * This will set the V entry of the encryption dictionary.
*
* See PDF Reference 1.4 Table 3.13.
*
* Note: This value is used to decrypt the pdf document. If you change this when the document is encrypted then * decryption will fail!. * * @param version The new encryption version. */ public void setVersion(int version) { dictionary.setInt(COSName.V, version); } /** * This will return the V entry of the encryption dictionary.
*
* See PDF Reference 1.4 Table 3.13. * * @return The encryption version to use. */ public int getVersion() { return dictionary.getInt(COSName.V, 0); } /** * This will set the number of bits to use for the encryption algorithm. * * @param length The new key length. */ public void setLength(int length) { dictionary.setInt(COSName.LENGTH, length); } /** * This will return the Length entry of the encryption dictionary.
*
* The length in bits for the encryption algorithm. This will return a multiple of 8. * * @return The length in bits for the encryption algorithm */ public int getLength() { return dictionary.getInt(COSName.LENGTH, 40); } /** * This will set the R entry of the encryption dictionary.
*
* See PDF Reference 1.4 Table 3.14.
*
* * Note: This value is used to decrypt the pdf document. If you change this when the document is encrypted then * decryption will fail!. * * @param revision The new encryption version. */ public void setRevision(int revision) { dictionary.setInt(COSName.R, revision); } /** * This will return the R entry of the encryption dictionary.
*
* See PDF Reference 1.4 Table 3.14. * * @return The encryption revision to use. */ public int getRevision() { return dictionary.getInt(COSName.R, DEFAULT_VERSION); } /** * This will set the O entry in the standard encryption dictionary. * * @param o A 32 byte array or null if there is no owner key. * * @throws IOException If there is an error setting the data. */ public void setOwnerKey(byte[] o) { dictionary.setItem(COSName.O, COSString.newInstance(o)); } /** * This will get the O entry in the standard encryption dictionary. * * @return A 32 byte array or null if there is no owner key. * * @throws IOException If there is an error accessing the data. */ public byte[] getOwnerKey() { byte[] o = null; COSString owner = (COSString) dictionary.getDictionaryObject(COSName.O); if (owner != null) { o = owner.getBytes(); } return o; } /** * This will set the U entry in the standard encryption dictionary. * * @param u A 32 byte array. * * @throws IOException If there is an error setting the data. */ public void setUserKey(byte[] u) { dictionary.setItem(COSName.U, COSString.newInstance(u)); } /** * This will get the U entry in the standard encryption dictionary. * * @return A 32 byte array or null if there is no user key. * * @throws IOException If there is an error accessing the data. */ public byte[] getUserKey() { byte[] u = null; COSString user = (COSString) dictionary.getDictionaryObject(COSName.U); if (user != null) { u = user.getBytes(); } return u; } /** * This will set the OE entry in the standard encryption dictionary. * * @param oe A 32 byte array or null if there is no owner encryption key. * * @throws IOException If there is an error setting the data. */ public void setOwnerEncryptionKey(byte[] oe) { dictionary.setItem(COSName.OE, COSString.newInstance(oe)); } /** * This will get the OE entry in the standard encryption dictionary. * * @return A 32 byte array or null if there is no owner encryption key. * * @throws IOException If there is an error accessing the data. */ public byte[] getOwnerEncryptionKey() { byte[] oe = null; COSString ownerEncryptionKey = (COSString) dictionary.getDictionaryObject(COSName.OE); if (ownerEncryptionKey != null) { oe = ownerEncryptionKey.getBytes(); } return oe; } /** * This will set the UE entry in the standard encryption dictionary. * * @param ue A 32 byte array or null if there is no user encryption key. * * @throws IOException If there is an error setting the data. */ public void setUserEncryptionKey(byte[] ue) { dictionary.setItem(COSName.UE, COSString.newInstance(ue)); } /** * This will get the UE entry in the standard encryption dictionary. * * @return A 32 byte array or null if there is no user encryption key. * * @throws IOException If there is an error accessing the data. */ public byte[] getUserEncryptionKey() { byte[] ue = null; COSString userEncryptionKey = (COSString) dictionary.getDictionaryObject(COSName.UE); if (userEncryptionKey != null) { ue = userEncryptionKey.getBytes(); } return ue; } /** * This will set the permissions bit mask. * * @param permissions The new permissions bit mask */ public void setPermissions(int permissions) { dictionary.setInt(COSName.P, permissions); } /** * This will get the permissions bit mask. * * @return The permissions bit mask. */ public int getPermissions() { return dictionary.getInt(COSName.P, 0); } /** * Will get the EncryptMetaData dictionary info. * * @return true if EncryptMetaData is explicitly set to false (the default is true) */ public boolean isEncryptMetaData() { // default is true (see 7.6.3.2 Standard Encryption Dictionary PDF 32000-1:2008) boolean encryptMetaData = true; COSBase value = dictionary.getDictionaryObject(COSName.ENCRYPT_META_DATA); if (value instanceof COSBoolean) { encryptMetaData = ((COSBoolean) value).getValue(); } return encryptMetaData; } /** * This will set the Recipients field of the dictionary. This field contains an array of string. * * @param recipients the array of bytes arrays to put in the Recipients field. * @throws IOException If there is an error setting the data. */ public void setRecipients(byte[][] recipients) { COSArray array = new COSArray(); for (byte[] recipient : recipients) { array.add(COSString.newInstance(recipient)); } dictionary.setItem(COSName.RECIPIENTS, array); } /** * Returns the number of recipients contained in the Recipients field of the dictionary. * * @return the number of recipients contained in the Recipients field. */ public int getRecipientsLength() { COSArray array = (COSArray) dictionary.getItem(COSName.RECIPIENTS); return array.size(); } /** * returns the COSString contained in the Recipients field at position i. * * @param i the position in the Recipients field array. * * @return a COSString object containing information about the recipient number i. */ public COSString getRecipientStringAt(int i) { COSArray array = (COSArray) dictionary.getItem(COSName.RECIPIENTS); return (COSString) array.get(i); } /** * Returns the standard crypt filter. * * @return the standard crypt filter if available. */ public PDCryptFilterDictionary getStdCryptFilterDictionary() { return getCryptFilterDictionary(COSName.STD_CF); } /** * Returns the crypt filter with the given name. * * @param cryptFilterName the name of the crypt filter * * @return the crypt filter with the given name if available */ public PDCryptFilterDictionary getCryptFilterDictionary(COSName cryptFilterName) { COSDictionary cryptFilterDictionary = (COSDictionary) dictionary .getDictionaryObject(COSName.CF); if (cryptFilterDictionary != null) { COSDictionary stdCryptFilterDictionary = (COSDictionary) cryptFilterDictionary .getDictionaryObject(cryptFilterName); if (stdCryptFilterDictionary != null) { return new PDCryptFilterDictionary(stdCryptFilterDictionary); } } return null; } /** * Sets the crypt filter with the given name. * * @param cryptFilterName the name of the crypt filter * @param cryptFilterDictionary the crypt filter to set */ public void setCryptFilterDictionary(COSName cryptFilterName, PDCryptFilterDictionary cryptFilterDictionary) { COSDictionary cfDictionary = (COSDictionary) dictionary.getDictionaryObject(COSName.CF); if (cfDictionary == null) { cfDictionary = new COSDictionary(); dictionary.setItem(COSName.CF, cfDictionary); } cfDictionary.setItem(cryptFilterName, cryptFilterDictionary.getCOSDictionary()); } /** * Sets the standard crypt filter. * * @param cryptFilterDictionary the standard crypt filter to set */ public void setStdCryptFilterDictionary(PDCryptFilterDictionary cryptFilterDictionary) { setCryptFilterDictionary(COSName.STD_CF, cryptFilterDictionary); } /** * Returns the name of the filter which is used for de/encrypting streams. Default value is "Identity". * * @return the name of the filter */ public COSName getStreamFilterName() { COSName stmF = (COSName) dictionary.getDictionaryObject(COSName.STM_F); if (stmF == null) { stmF = COSName.IDENTITY; } return stmF; } /** * Sets the name of the filter which is used for de/encrypting streams. * * @param streamFilterName the name of the filter */ public void setStreamFilterName(COSName streamFilterName) { dictionary.setItem(COSName.STM_F, streamFilterName); } /** * Returns the name of the filter which is used for de/encrypting strings. Default value is "Identity". * * @return the name of the filter */ public COSName getStringFilterName() { COSName strF = (COSName) dictionary.getDictionaryObject(COSName.STR_F); if (strF == null) { strF = COSName.IDENTITY; } return strF; } /** * Sets the name of the filter which is used for de/encrypting strings. * * @param stringFilterName the name of the filter */ public void setStringFilterName(COSName stringFilterName) { dictionary.setItem(COSName.STR_F, stringFilterName); } /** * Set the Perms entry in the encryption dictionary. * * @param perms A 16 byte array. * * @throws IOException If there is an error setting the data. */ public void setPerms(byte[] perms) { dictionary.setItem(COSName.PERMS, COSString.newInstance(perms)); } /** * Get the Perms entry in the encryption dictionary. * * @return A 16 byte array or null if there is no Perms entry. * * @throws IOException If there is an error accessing the data. */ public byte[] getPerms() { COSString permsCosString = dictionary.getDictionaryObject(COSName.PERMS, COSString.class); if (nonNull(permsCosString)) { return permsCosString.getBytes(); } return null; } /** * remove CF, StmF, and StrF entries. This is to be called if V is not 4 or 5. */ public void removeV45filters() { dictionary.setItem(COSName.CF, null); dictionary.setItem(COSName.STM_F, null); dictionary.setItem(COSName.STR_F, null); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/ProtectionPolicy.java000066400000000000000000000041051320103431700306570ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; /** * This class represents the protection policy to apply to a document. * * Objects implementing this abstract class can be passed to the protect method of PDDocument * to protect a document. * * * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) */ public abstract class ProtectionPolicy { private static final int DEFAULT_KEY_LENGTH = 40; private int encryptionKeyLength = DEFAULT_KEY_LENGTH; /** * set the length in (bits) of the secret key that will be * used to encrypt document data. * The default value is 40 bits, which provides a low security level * but is compatible with old versions of Acrobat Reader. * * @param l the length in bits (must be 40, 128 or 256) */ public void setEncryptionKeyLength(int l) { if(l!=40 && l!=128 && l!=256) { throw new IllegalArgumentException("Invalid key length '" + l + "' value must be 40, 128 or 256!"); } encryptionKeyLength = l; } /** * Get the length of the secrete key that will be used to encrypt * document data. * * @return The length (in bits) of the encryption key. */ public int getEncryptionKeyLength() { return encryptionKeyLength; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/PublicKeyDecryptionMaterial.java000066400000000000000000000106751320103431700327710ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; import java.util.Enumeration; /** * This class holds necessary information to decrypt a PDF document * protected by the public key security handler. * * To decrypt such a document, we need: *

    *
  • a valid X509 certificate which correspond to one of the recipient of the document
  • *
  • the private key corresponding to this certificate *
  • the password to decrypt the private key if necessary
  • *
* * @author Benoit Guillon * */ public class PublicKeyDecryptionMaterial extends DecryptionMaterial { private String password = null; private KeyStore keyStore = null; private String alias = null; /** * Create a new public key decryption material. * * @param keystore The keystore were the private key and the certificate are * @param a The alias of the private key and the certificate. * If the keystore contains only 1 entry, this parameter can be left null. * @param pwd The password to extract the private key from the keystore. */ public PublicKeyDecryptionMaterial(KeyStore keystore, String a, String pwd) { keyStore = keystore; alias = a; password = pwd; } /** * Returns the certificate contained in the keystore. * * @return The certificate that will be used to try to open the document. * * @throws KeyStoreException If there is an error accessing the certificate. */ public X509Certificate getCertificate() throws KeyStoreException { if(keyStore.size() == 1) { Enumeration aliases = keyStore.aliases(); String keyStoreAlias = aliases.nextElement(); return (X509Certificate)keyStore.getCertificate(keyStoreAlias); } else { if(keyStore.containsAlias(alias)) { return (X509Certificate)keyStore.getCertificate(alias); } throw new KeyStoreException("the keystore does not contain the given alias"); } } /** * Returns the password given by the user and that will be used * to open the private key. * * @return The password. */ public String getPassword() { return password; } /** * returns The private key that will be used to open the document protection. * @return The private key. * @throws KeyStoreException If there is an error accessing the key. */ public Key getPrivateKey() throws KeyStoreException { try { if(keyStore.size() == 1) { Enumeration aliases = keyStore.aliases(); String keyStoreAlias = aliases.nextElement(); return keyStore.getKey(keyStoreAlias, password.toCharArray()); } else { if(keyStore.containsAlias(alias)) { return keyStore.getKey(alias, password.toCharArray()); } throw new KeyStoreException("the keystore does not contain the given alias"); } } catch(UnrecoverableKeyException ex) { throw new KeyStoreException("the private key is not recoverable", ex); } catch(NoSuchAlgorithmException ex) { throw new KeyStoreException("the algorithm necessary to recover the key is not available", ex); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/PublicKeyProtectionPolicy.java000066400000000000000000000100101320103431700324570ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * The protection policy to use to protect a document with the public key security handler. * * PDF documents are encrypted so that they can be decrypted by * one or more recipients. Each recipient have its own access permission. * * The following code sample shows how to protect a document using * the public key security handler. In this code sample, doc is * a PDDocument object. * *
 * PublicKeyProtectionPolicy policy = new PublicKeyProtectionPolicy();
 * PublicKeyRecipient recip = new PublicKeyRecipient();
 * AccessPermission ap = new AccessPermission();
 * ap.setCanModify(false);
 * recip.setPermission(ap);
 *
 * // load the recipient's certificate
 * InputStream inStream = new FileInputStream(certificate_path);
 * CertificateFactory cf = CertificateFactory.getInstance("X.509");
 * X509Certificate certificate = (X509Certificate)cf.generateCertificate(inStream);
 * inStream.close();
 *
 * recip.setX509(certificate); // set the recipient's certificate
 * policy.addRecipient(recip);
 * policy.setEncryptionKeyLength(128); // the document will be encrypted with 128 bits secret key
 * doc.protect(policy);
 * doc.save(out);
 * 
* * @see AccessPermission * @see PublicKeyRecipient * @author Benoit Guillon */ public final class PublicKeyProtectionPolicy extends ProtectionPolicy { public final List recipients = new ArrayList(); private X509Certificate decryptionCertificate; /** * Creates a new PublicKeyProtectionPolicy with an empty recipients list. */ public PublicKeyProtectionPolicy() { } /** * Adds a new recipient to the recipients list. * @param recipient A new recipient. */ public void addRecipient(PublicKeyRecipient recipient) { recipients.add(recipient); } /** * Removes a recipient from the recipients list. * @param recipient The recipient to remove. * @return true If a recipient was found and removed. */ public boolean removeRecipient(PublicKeyRecipient recipient) { return recipients.remove(recipient); } /** * Returns an iterator to browse the list of recipients. * Object found in this iterator are PublicKeyRecipient. * @return The recipients list iterator. */ public Iterator getRecipientsIterator() { return recipients.iterator(); } /** * Returns the decryption certificate. * @return the decryption certificate */ public X509Certificate getDecryptionCertificate() { return decryptionCertificate; } /** * Sets the decryption certificate * @param decryptionCertificate the new decryption certificate. */ public void setDecryptionCertificate(X509Certificate decryptionCertificate) { this.decryptionCertificate = decryptionCertificate; } /** * Returns the number of recipients * @return the number of recipients */ public int getNumberOfRecipients() { return recipients.size(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/PublicKeyRecipient.java000066400000000000000000000037041320103431700311070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.security.cert.X509Certificate; /** * Represents a recipient in the public key protection policy. * * @see PublicKeyProtectionPolicy * * @author Benoit Guillon * */ public class PublicKeyRecipient { private X509Certificate x509; private AccessPermission permission; /** * Returns the X509 certificate of the recipient. * * @return The X509 certificate */ public X509Certificate getX509() { return x509; } /** * Set the X509 certificate of the recipient. * * @param aX509 The X509 certificate */ public void setX509(X509Certificate aX509) { this.x509 = aX509; } /** * Returns the access permission granted to the recipient. * * @return The access permission object. */ public AccessPermission getPermission() { return permission; } /** * Set the access permission granted to the recipient. * * @param permissions The permission to set. */ public void setPermission(AccessPermission permissions) { this.permission = permissions; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/PublicKeySecurityHandler.java000066400000000000000000000227361320103431700323000ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.io.IOException; import java.math.BigInteger; import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Iterator; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cms.CMSEnvelopedData; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.KeyTransRecipientId; import org.bouncycastle.cms.RecipientId; import org.bouncycastle.cms.RecipientInformation; import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.PDDocument; /** * This class implements the public key security handler described in the PDF specification. * * @see PublicKeyProtectionPolicy to see how to protect document with this security handler. * @author Benoit Guillon */ public final class PublicKeySecurityHandler extends SecurityHandler { /** The filter name. */ public static final String FILTER = "Adobe.PubSec"; private PublicKeyProtectionPolicy policy = null; /** * Constructor. */ public PublicKeySecurityHandler() { } /** * Constructor used for encryption. * * @param p The protection policy. */ public PublicKeySecurityHandler(PublicKeyProtectionPolicy p) { policy = p; this.keyLength = policy.getEncryptionKeyLength(); } /** * Prepares everything to decrypt the document. * * @param encryption encryption dictionary, can be retrieved via * {@link PDDocument#getEncryption()} * @param documentIDArray document id which is returned via * {@link org.apache.pdfbox.cos.COSDocument#getDocumentID()} (not used by * this handler) * @param decryptionMaterial Information used to decrypt the document. * * @throws IOException If there is an error accessing data. If verbose mode * is enabled, the exception message will provide more details why the * match wasn't successful. */ @Override public void prepareForDecryption(PDEncryption encryption, COSArray documentIDArray, DecryptionMaterial decryptionMaterial) throws IOException { if (!(decryptionMaterial instanceof PublicKeyDecryptionMaterial)) { throw new IOException( "Provided decryption material is not compatible with the document"); } setDecryptMetadata(encryption.isEncryptMetaData()); if (encryption.getLength() != 0) { this.keyLength = encryption.getLength(); } PublicKeyDecryptionMaterial material = (PublicKeyDecryptionMaterial) decryptionMaterial; try { boolean foundRecipient = false; // the decrypted content of the enveloped data that match // the certificate in the decryption material provided byte[] envelopedData = null; // the bytes of each recipient in the recipients array byte[][] recipientFieldsBytes = new byte[encryption.getRecipientsLength()][]; int recipientFieldsLength = 0; int i = 0; StringBuilder extraInfo = new StringBuilder(); for (; i < encryption.getRecipientsLength(); i++) { COSString recipientFieldString = encryption.getRecipientStringAt(i); byte[] recipientBytes = recipientFieldString.getBytes(); CMSEnvelopedData data = new CMSEnvelopedData(recipientBytes); Iterator recipCertificatesIt = data.getRecipientInfos().getRecipients() .iterator(); int j = 0; while (recipCertificatesIt.hasNext()) { RecipientInformation ri = (RecipientInformation) recipCertificatesIt.next(); // Impl: if a matching certificate was previously found it is an error, // here we just don't care about it X509Certificate certificate = material.getCertificate(); X509CertificateHolder materialCert = null; if (null != certificate) { materialCert = new X509CertificateHolder(certificate.getEncoded()); } RecipientId rid = ri.getRID(); if (rid.match(materialCert) && !foundRecipient) { foundRecipient = true; PrivateKey privateKey = (PrivateKey) material.getPrivateKey(); envelopedData = ri.getContent(new JceKeyTransEnvelopedRecipient(privateKey)); break; } j++; if (certificate != null) { extraInfo.append('\n'); extraInfo.append(j); extraInfo.append(": "); if (rid instanceof KeyTransRecipientId) { appendCertInfo(extraInfo, (KeyTransRecipientId) rid, certificate, materialCert); } } } recipientFieldsBytes[i] = recipientBytes; recipientFieldsLength += recipientBytes.length; } if (!foundRecipient || envelopedData == null) { throw new IOException("The certificate matches none of " + i + " recipient entries" + extraInfo.toString()); } if (envelopedData.length != 24) { throw new IOException("The enveloped data does not contain 24 bytes"); } // now envelopedData contains: // - the 20 bytes seed // - the 4 bytes of permission for the current user byte[] accessBytes = new byte[4]; System.arraycopy(envelopedData, 20, accessBytes, 0, 4); AccessPermission currentAccessPermission = new AccessPermission(accessBytes); currentAccessPermission.setReadOnly(); setCurrentAccessPermission(currentAccessPermission); // what we will put in the SHA1 = the seed + each byte contained in the recipients array byte[] sha1Input = new byte[recipientFieldsLength + 20]; // put the seed in the sha1 input System.arraycopy(envelopedData, 0, sha1Input, 0, 20); // put each bytes of the recipients array in the sha1 input int sha1InputOffset = 20; for (byte[] recipientFieldsByte : recipientFieldsBytes) { System.arraycopy(recipientFieldsByte, 0, sha1Input, sha1InputOffset, recipientFieldsByte.length); sha1InputOffset += recipientFieldsByte.length; } MessageDigest md = MessageDigests.getSHA1(); byte[] mdResult = md.digest(sha1Input); // we have the encryption key ... setEncryptionKey(new byte[this.keyLength / 8]); System.arraycopy(mdResult, 0, getEncryptionKey(), 0, this.keyLength / 8); } catch (CMSException e) { throw new IOException(e); } catch (KeyStoreException e) { throw new IOException(e); } catch (CertificateEncodingException e) { throw new IOException(e); } } private void appendCertInfo(StringBuilder extraInfo, KeyTransRecipientId ktRid, X509Certificate certificate, X509CertificateHolder materialCert) { BigInteger ridSerialNumber = ktRid.getSerialNumber(); if (ridSerialNumber != null) { String certSerial = "unknown"; BigInteger certSerialNumber = certificate.getSerialNumber(); if (certSerialNumber != null) { certSerial = certSerialNumber.toString(16); } extraInfo.append("serial-#: rid "); extraInfo.append(ridSerialNumber.toString(16)); extraInfo.append(" vs. cert "); extraInfo.append(certSerial); extraInfo.append(" issuer: rid \'"); extraInfo.append(ktRid.getIssuer()); extraInfo.append("\' vs. cert \'"); extraInfo.append(materialCert == null ? "null" : materialCert.getIssuer()); extraInfo.append("\' "); } } /** * {@inheritDoc} */ @Override public boolean hasProtectionPolicy() { return policy != null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/RC4Cipher.java000066400000000000000000000112251320103431700270750ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * An implementation of the RC4 stream cipher. * * @author Ben Litchfield */ class RC4Cipher { private final int[] salt; private int b; private int c; /** * Constructor. */ RC4Cipher() { salt = new int[256]; } /** * This will reset the key to be used. * * @param key The RC4 key used during encryption. */ public void setKey( byte[] key ) { b = 0; c = 0; if(key.length < 1 || key.length > 32) { throw new IllegalArgumentException("number of bytes must be between 1 and 32"); } for(int i = 0; i < salt.length; i++) { salt[i] = i; } int keyIndex = 0; int saltIndex = 0; for( int i = 0; i < salt.length; i++) { saltIndex = (fixByte(key[keyIndex]) + salt[i] + saltIndex) % 256; swap( salt, i, saltIndex ); keyIndex = (keyIndex + 1) % key.length; } } /** * Thie will ensure that the value for a byte >=0. * * @param aByte The byte to test against. * * @return A value >=0 and < 256 */ private static int fixByte( byte aByte ) { return aByte < 0 ? 256 + aByte : aByte; } /** * This will swap two values in an array. * * @param data The array to swap from. * @param firstIndex The index of the first element to swap. * @param secondIndex The index of the second element to swap. */ private static void swap( int[] data, int firstIndex, int secondIndex ) { int tmp = data[ firstIndex ]; data[ firstIndex ] = data[ secondIndex ]; data[ secondIndex ] = tmp; } /** * This will encrypt and write the next byte. * * @param aByte The byte to encrypt. * @param output The stream to write to. * * @throws IOException If there is an error writing to the output stream. */ public void write( byte aByte, OutputStream output ) throws IOException { b = (b + 1) % 256; c = (salt[b] + c) % 256; swap( salt, b, c ); int saltIndex = (salt[b] + salt[c]) % 256; output.write(aByte ^ (byte)salt[saltIndex]); } /** * This will encrypt and write the data. * * @param data The data to encrypt. * @param output The stream to write to. * * @throws IOException If there is an error writing to the output stream. */ public void write( byte[] data, OutputStream output ) throws IOException { for( int i = 0; i < data.length; i++ ) { write( data[i], output ); } } /** * This will encrypt and write the data. * * @param data The data to encrypt. * @param output The stream to write to. * * @throws IOException If there is an error writing to the output stream. */ public void write( InputStream data, OutputStream output ) throws IOException { byte[] buffer = new byte[1024]; int amountRead; while( (amountRead = data.read( buffer )) != -1 ) { write( buffer, 0, amountRead, output ); } } /** * This will encrypt and write the data. * * @param data The data to encrypt. * @param offset The offset into the array to start reading data from. * @param len The number of bytes to attempt to read. * @param output The stream to write to. * * @throws IOException If there is an error writing to the output stream. */ public void write( byte[] data, int offset, int len, OutputStream output) throws IOException { for( int i = offset; i < offset + len; i++ ) { write( data[i], output ); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/SecurityHandler.java000066400000000000000000000432401320103431700304610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.io.CipherInputStream; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A security handler as described in the PDF specifications. A security handler is responsible of documents protection. * * @author Ben Litchfield * @author Benoit Guillon * @author Manuel Kasper */ public abstract class SecurityHandler { private static final Logger LOG = LoggerFactory.getLogger(SecurityHandler.class); private static final int DEFAULT_KEY_LENGTH = 40; // see 7.6.2, page 58, PDF 32000-1:2008 private static final byte[] AES_SALT = { (byte) 0x73, (byte) 0x41, (byte) 0x6c, (byte) 0x54 }; /** The length of the secret key used to encrypt the document. */ protected int keyLength = DEFAULT_KEY_LENGTH; /** The encryption key that will used to encrypt / decrypt. */ private byte[] encryptionKey; /** The RC4 implementation used for cryptographic functions. */ private final RC4Cipher rc4 = new RC4Cipher(); /** indicates if the Metadata have to be decrypted of not. */ private boolean decryptMetadata; private final Set objects = new HashSet<>(); private boolean useAES; /** * The access permission granted to the current user for the document. These permissions are computed during * decryption and are in read only mode. */ private AccessPermission currentAccessPermission = null; /** * Set wether to decrypt meta data. * * @param decryptMetadata true if meta data has to be decrypted. */ protected void setDecryptMetadata(boolean decryptMetadata) { this.decryptMetadata = decryptMetadata; } /** * Prepares everything to decrypt the document. * * @param encryption encryption dictionary, can be retrieved via {@link PDDocument#getEncryption()} * @param documentIDArray document id which is returned via * {@link org.apache.pdfbox.cos.COSDocument#getDocumentID()} * @param decryptionMaterial Information used to decrypt the document. * * @throws IOException If there is an error accessing data. */ public abstract void prepareForDecryption(PDEncryption encryption, COSArray documentIDArray, DecryptionMaterial decryptionMaterial) throws InvalidPasswordException, IOException; /** * Encrypt or decrypt a set of data. * * @param objectNumber The data object number. * @param genNumber The data generation number. * @param data The data to encrypt. * @param output The output to write the encrypted data to. * * @throws IOException If there is an error reading the data. */ private void decryptData(long objectNumber, long genNumber, InputStream data, OutputStream output) throws IOException { // Determine whether we're using Algorithm 1 (for RC4 and AES-128), or 1.A (for AES-256) if (useAES && encryptionKey.length == 32) { decryptDataAES256(data, output); } else { byte[] finalKey = calcFinalKey(objectNumber, genNumber); if (useAES) { decryptDataAESother(finalKey, data, output); } else { decryptDataRC4(finalKey, data, output); } } IOUtils.close(output); } /** * Calculate the key to be used for RC4 and AES-128. * * @param objectNumber The data object number. * @param genNumber The data generation number. * @return the calculated key. */ private byte[] calcFinalKey(long objectNumber, long genNumber) { byte[] newKey = new byte[encryptionKey.length + 5]; System.arraycopy(encryptionKey, 0, newKey, 0, encryptionKey.length); // PDF 1.4 reference pg 73 // step 1 // we have the reference // step 2 newKey[newKey.length - 5] = (byte) (objectNumber & 0xff); newKey[newKey.length - 4] = (byte) (objectNumber >> 8 & 0xff); newKey[newKey.length - 3] = (byte) (objectNumber >> 16 & 0xff); newKey[newKey.length - 2] = (byte) (genNumber & 0xff); newKey[newKey.length - 1] = (byte) (genNumber >> 8 & 0xff); // step 3 MessageDigest md = MessageDigests.getMD5(); md.update(newKey); if (useAES) { md.update(AES_SALT); } byte[] digestedKey = md.digest(); // step 4 int length = Math.min(newKey.length, 16); byte[] finalKey = new byte[length]; System.arraycopy(digestedKey, 0, finalKey, 0, length); return finalKey; } /** * Encrypt or decrypt data with RC4. * * @param finalKey The final key obtained with via {@link #calcFinalKey(long, long)}. * @param input The data to encrypt. * @param output The output to write the encrypted data to. * * @throws IOException If there is an error reading the data. */ protected void decryptDataRC4(byte[] finalKey, InputStream input, OutputStream output) throws IOException { rc4.setKey(finalKey); rc4.write(input, output); } /** * Encrypt or decrypt data with RC4. * * @param finalKey The final key obtained with via {@link #calcFinalKey(long, long)}. * @param input The data to encrypt. * @param output The output to write the encrypted data to. * * @throws IOException If there is an error reading the data. */ protected void decryptDataRC4(byte[] finalKey, byte[] input, OutputStream output) throws IOException { rc4.setKey(finalKey); rc4.write(input, output); } /** * Encrypt or decrypt data with AES with key length other than 256 bits. * * @param finalKey The final key obtained with via {@link #calcFinalKey()}. * @param data The data to encrypt. * @param output The output to write the encrypted data to. * * @throws IOException If there is an error reading the data. */ private void decryptDataAESother(byte[] finalKey, InputStream data, OutputStream output) throws IOException { byte[] iv = new byte[16]; int ivSize = data.read(iv); if (ivSize == -1) { return; } if (ivSize != iv.length) { throw new IOException("AES initialization vector not fully read: only " + ivSize + " bytes read instead of " + iv.length); } try { Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKey aesKey = new SecretKeySpec(finalKey, "AES"); IvParameterSpec ips = new IvParameterSpec(iv); decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ips); byte[] buffer = new byte[256]; int n; while ((n = data.read(buffer)) != -1) { byte[] update = decryptCipher.update(buffer, 0, n); if (update != null) { output.write(update); } } output.write(decryptCipher.doFinal()); } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e) { throw new IOException(e); } } /** * Encrypt or decrypt data with AES256. * * @param data The data to encrypt. * @param output The output to write the encrypted data to. * * @throws IOException If there is an error reading the data. */ private void decryptDataAES256(InputStream data, OutputStream output) throws IOException { byte[] iv = new byte[16]; // read IV from stream int ivSize = data.read(iv); if (ivSize == -1) { return; } if (ivSize != iv.length) { throw new IOException("AES initialization vector not fully read: only " + ivSize + " bytes read instead of " + iv.length); } PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher( new CBCBlockCipher(new AESFastEngine())); cipher.init(false, new ParametersWithIV(new KeyParameter(encryptionKey), iv)); try (CipherInputStream cis = new CipherInputStream(data, cipher)) { org.apache.commons.io.IOUtils.copy(cis, output); } } /** * This will dispatch to the correct method. * * @param obj The object to decrypt. * @param objNum The object number. * @param genNum The object generation Number. * * @throws IOException If there is an error getting the stream data. */ public void decrypt(COSBase obj, long objNum, long genNum) throws IOException { if (!objects.contains(obj)) { objects.add(obj); if (obj instanceof COSString) { decryptString((COSString) obj, objNum, genNum); } else if (obj instanceof COSStream) { decryptStream((COSStream) obj, objNum, genNum); } else if (obj instanceof COSDictionary) { decryptDictionary((COSDictionary) obj, objNum, genNum); } else if (obj instanceof COSArray) { decryptArray((COSArray) obj, objNum, genNum); } } } /** * This will decrypt a stream. * * @param stream The stream to decrypt. * @param objNum The object number. * @param genNum The object generation number. * * @throws IOException If there is an error getting the stream data. */ public void decryptStream(COSStream stream, long objNum, long genNum) throws IOException { COSBase type = stream.getCOSName(COSName.TYPE); if (!decryptMetadata && COSName.METADATA.equals(type)) { return; } // "The cross-reference stream shall not be encrypted" if (COSName.XREF.equals(type)) { return; } if (COSName.METADATA.equals(type)) { byte buf[] = new byte[10]; // PDFBOX-3229 check case where metadata is not encrypted despite /EncryptMetadata missing try (InputStream is = stream.getUnfilteredStream()) { is.read(buf); } if (Arrays.equals(buf, " entry : dictionary.entrySet()) { if (isSignature && COSName.CONTENTS.equals(entry.getKey())) { // do not decrypt the signature contents string continue; } COSBase value = entry.getValue(); // within a dictionary only the following kind of COS objects have to be decrypted if (value instanceof COSString || value instanceof COSArray || value instanceof COSDictionary) { decrypt(value, objNum, genNum); } } } /** * This will decrypt a string. * * @param string the string to decrypt. * @param objNum The object number. * @param genNum The object generation number. * * @throws IOException If an error occurs writing the new string. */ private void decryptString(COSString string, long objNum, long genNum) throws IOException { ByteArrayInputStream data = new ByteArrayInputStream(string.getBytes()); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { decryptData(objNum, genNum, data, outputStream); string.setValue(outputStream.toByteArray()); } catch (IOException ex) { LOG.error("Failed to decrypt COSString of length " + string.getBytes().length + " in object " + objNum + ": " + ex.getMessage()); } } /** * This will decrypt an array. * * @param array The array to decrypt. * @param objNum The object number. * @param genNum The object generation number. * * @throws IOException If there is an error accessing the data. */ private void decryptArray(COSArray array, long objNum, long genNum) throws IOException { for (int i = 0; i < array.size(); i++) { decrypt(array.get(i), objNum, genNum); } } /** * Getter of the property keyLength. * * @return Returns the keyLength. */ public int getKeyLength() { return keyLength; } /** * Setter of the property keyLength. * * @param keyLen The keyLength to set. */ public void setKeyLength(int keyLen) { this.keyLength = keyLen; } /** * Sets the access permissions. * * @param currentAccessPermission The access permissions to be set. */ public void setCurrentAccessPermission(AccessPermission currentAccessPermission) { this.currentAccessPermission = currentAccessPermission; } /** * Returns the access permissions that were computed during document decryption. The returned object is in read only * mode. * * @return the access permissions or null if the document was not decrypted. */ public AccessPermission getCurrentAccessPermission() { return currentAccessPermission; } /** * True if AES is used for encryption and decryption. * * @return true if AEs is used */ public boolean isAES() { return useAES; } /** * Set to true if AES for encryption and decryption should be used. * * @param aesValue if true AES will be used * */ public void setAES(boolean aesValue) { useAES = aesValue; } public byte[] getEncryptionKey() { return encryptionKey; } protected void setEncryptionKey(byte[] encryptionKey) { this.encryptionKey = encryptionKey; } /** * Returns whether a protection policy has been set. * * @return true if a protection policy has been set. */ public abstract boolean hasProtectionPolicy(); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/SecurityHandlerFactory.java000066400000000000000000000131421320103431700320070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; /** * Manages security handlers for the application. * It follows the singleton pattern. * To be usable, security managers must be registered in it. * Security managers are retrieved by the application when necessary. * * @author Benoit Guillon * @author John Hewson */ public final class SecurityHandlerFactory { /** Singleton instance */ public static final SecurityHandlerFactory INSTANCE = new SecurityHandlerFactory(); private final Map> nameToHandler = new HashMap<>(); private final Map, Class> policyToHandler = new HashMap<>(); private SecurityHandlerFactory() { registerHandler(StandardSecurityHandler.FILTER, StandardSecurityHandler.class, StandardProtectionPolicy.class); registerHandler(PublicKeySecurityHandler.FILTER, PublicKeySecurityHandler.class, PublicKeyProtectionPolicy.class); } /** * Registers a security handler. * * If the security handler was already registered an exception is thrown. * If another handler was previously registered for the same filter name or * for the same policy name, an exception is thrown * * @param name the name of the filter * @param securityHandler security handler class to register * @param protectionPolicy protection policy class to register */ public void registerHandler(String name, Class securityHandler, Class protectionPolicy) { if (nameToHandler.containsKey(name)) { throw new IllegalStateException("The security handler name is already registered"); } nameToHandler.put(name, securityHandler); policyToHandler.put(protectionPolicy, securityHandler); } /** * Returns a new security handler for the given protection policy, or null none is available. * @param policy the protection policy for which to create a security handler * @return a new SecurityHandler instance, or null if none is available */ public SecurityHandler newSecurityHandlerForPolicy(ProtectionPolicy policy) { Class handlerClass = policyToHandler.get(policy.getClass()); if (handlerClass == null) { return null; } Class[] argsClasses = { policy.getClass() }; Object[] args = { policy }; return newSecurityHandler(handlerClass, argsClasses, args); } /** * Returns a new security handler for the given Filter name, or null none is available. * @param name the Filter name from the PDF encryption dictionary * @return a new SecurityHandler instance, or null if none is available */ public SecurityHandler newSecurityHandlerForFilter(String name) { Class handlerClass = nameToHandler.get(name); if (handlerClass == null) { return null; } Class[] argsClasses = { }; Object[] args = { }; return newSecurityHandler(handlerClass, argsClasses, args); } /* Returns a new security handler for the given parameters, or null none is available. * * @param handlerClass the handler class. * @param argsClasses the parameter array. * @param args array of objects to be passed as arguments to the constructor call. * @return a new SecurityHandler instance, or null if none is available. */ private SecurityHandler newSecurityHandler(Class handlerClass, Class[] argsClasses, Object[] args) { try { Constructor ctor = handlerClass.getDeclaredConstructor(argsClasses); return ctor.newInstance(args); } catch(NoSuchMethodException e) { // should not happen in normal operation throw new RuntimeException(e); } catch(IllegalAccessException e) { // should not happen in normal operation throw new RuntimeException(e); } catch(InstantiationException e) { // should not happen in normal operation throw new RuntimeException(e); } catch(InvocationTargetException e) { // should not happen in normal operation throw new RuntimeException(e); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/StandardDecryptionMaterial.java000066400000000000000000000030431320103431700326310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; /** * * Represents the necessary information to decrypt a document protected by * the standard security handler (password protection). * * This is only composed of a password. * @author Benoit Guillon * */ public class StandardDecryptionMaterial extends DecryptionMaterial { private String password = null; /** * Create a new standard decryption material with the given password. * * @param pwd The password. */ public StandardDecryptionMaterial(String pwd) { password = pwd; } /** * Returns the password. * * @return The password used to decrypt the document. */ public String getPassword() { return password; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/StandardProtectionPolicy.java000066400000000000000000000064411320103431700323450ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; /** * The protection policy to add to a document for password-based protection. * * The following example shows how to protect a PDF document with password. * In this example, the document will be protected so that someone opening * the document with the user password user_pwd will not be * able to modify the document. * *
 * AccessPermission ap = new AccessPermission();
 * ap.setCanModify(false);
 * StandardProtectionPolicy policy = new StandardProtectionPolicy(owner_pwd, user_pwd, ap);
 * doc.protect(policy);
 * 
* * @author Benoit Guillon */ public final class StandardProtectionPolicy extends ProtectionPolicy { private AccessPermission permissions; private String ownerPassword = ""; private String userPassword = ""; /** * Creates an new instance of the standard protection policy * in order to protect a PDF document with passwords. * * @param ownerPassword The owner's password. * @param userPassword The users's password. * @param permissions The access permissions given to the user. */ public StandardProtectionPolicy(String ownerPassword, String userPassword, AccessPermission permissions) { this.ownerPassword = ownerPassword; this.userPassword = userPassword; this.permissions = permissions; } /** * Returns the access permissions * @return the access permissions */ public AccessPermission getPermissions() { return permissions; } /** * Sets the access permissions * @param permissions the new access permissions */ public void setPermissions(AccessPermission permissions) { this.permissions = permissions; } /** * Returns the owner password. * @return the owner password */ public String getOwnerPassword() { return ownerPassword; } /** * Sets the owner password * @param ownerPassword the new owner password */ public void setOwnerPassword(String ownerPassword) { this.ownerPassword = ownerPassword; } /** * Returns the user password. * @return the user password */ public String getUserPassword() { return userPassword; } /** * Sets the user password. * @param userPassword the new user password */ public void setUserPassword(String userPassword) { this.userPassword = userPassword; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/StandardSecurityHandler.java000066400000000000000000001004351320103431700321420ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.encryption; import static org.bouncycastle.util.Arrays.copyOf; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The standard security handler. This security handler protects document with password. * * @see StandardProtectionPolicy to see how to protect document with this security handler. * @author Ben Litchfield * @author Benoit Guillon * @author Manuel Kasper */ public final class StandardSecurityHandler extends SecurityHandler { /** * Log instance. */ private static final Logger LOG = LoggerFactory.getLogger(StandardSecurityHandler.class); /** Type of security handler. */ public static final String FILTER = "Standard"; /** Protection policy class for this handler. */ public static final Class PROTECTION_POLICY_CLASS = StandardProtectionPolicy.class; /** Standard padding for encryption. */ private static final byte[] ENCRYPT_PADDING = { (byte) 0x28, (byte) 0xBF, (byte) 0x4E, (byte) 0x5E, (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41, (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56, (byte) 0xFF, (byte) 0xFA, (byte) 0x01, (byte) 0x08, (byte) 0x2E, (byte) 0x2E, (byte) 0x00, (byte) 0xB6, (byte) 0xD0, (byte) 0x68, (byte) 0x3E, (byte) 0x80, (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE, (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A }; // hashes used for Algorithm 2.B, depending on remainder from E modulo 3 private static final String[] HASHES_2B = new String[] { "SHA-256", "SHA-384", "SHA-512" }; private StandardProtectionPolicy policy; /** * Constructor. */ public StandardSecurityHandler() { } /** * Constructor used for encryption. * * @param p The protection policy. */ public StandardSecurityHandler(StandardProtectionPolicy p) { policy = p; keyLength = policy.getEncryptionKeyLength(); } /** * Prepares everything to decrypt the document. * * Only if decryption of single objects is needed this should be called. * * @param encryption encryption dictionary * @param documentIDArray document id * @param decryptionMaterial Information used to decrypt the document. * * @throws InvalidPasswordException If the password is incorrect. * @throws IOException If there is an error accessing data. */ @Override public void prepareForDecryption(PDEncryption encryption, COSArray documentIDArray, DecryptionMaterial decryptionMaterial) throws InvalidPasswordException, IOException { if (!(decryptionMaterial instanceof StandardDecryptionMaterial)) { throw new IOException("Decryption material is not compatible with the document"); } setDecryptMetadata(encryption.isEncryptMetaData()); StandardDecryptionMaterial material = (StandardDecryptionMaterial) decryptionMaterial; String password = material.getPassword(); if (password == null) { password = ""; } int dicPermissions = encryption.getPermissions(); int dicRevision = encryption.getRevision(); int dicLength = encryption.getVersion() == 1 ? 5 : encryption.getLength() / 8; byte[] documentIDBytes = getDocumentIDBytes(documentIDArray); // we need to know whether the meta data was encrypted for password calculation boolean encryptMetadata = encryption.isEncryptMetaData(); byte[] userKey = encryption.getUserKey(); byte[] ownerKey = encryption.getOwnerKey(); byte[] ue = null, oe = null; Charset passwordCharset = StandardCharsets.ISO_8859_1; if (dicRevision == 6 || dicRevision == 5) { passwordCharset = StandardCharsets.UTF_8; ue = encryption.getUserEncryptionKey(); oe = encryption.getOwnerEncryptionKey(); } AccessPermission currentAccessPermission; if (isOwnerPassword(password.getBytes(passwordCharset), userKey, ownerKey, dicPermissions, documentIDBytes, dicRevision, dicLength, encryptMetadata)) { currentAccessPermission = AccessPermission.getOwnerAccessPermission(); setCurrentAccessPermission(currentAccessPermission); byte[] computedPassword; if (dicRevision == 6 || dicRevision == 5) { computedPassword = password.getBytes(passwordCharset); } else { computedPassword = getUserPassword(password.getBytes(passwordCharset), ownerKey, dicRevision, dicLength); } setEncryptionKey( computeEncryptedKey(computedPassword, ownerKey, userKey, oe, ue, dicPermissions, documentIDBytes, dicRevision, dicLength, encryptMetadata, true)); } else if (isUserPassword(password.getBytes(passwordCharset), userKey, ownerKey, dicPermissions, documentIDBytes, dicRevision, dicLength, encryptMetadata)) { currentAccessPermission = new AccessPermission(dicPermissions); setCurrentAccessPermission(currentAccessPermission); setEncryptionKey(computeEncryptedKey(password.getBytes(passwordCharset), ownerKey, userKey, oe, ue, dicPermissions, documentIDBytes, dicRevision, dicLength, encryptMetadata, false)); } else { throw new InvalidPasswordException("Cannot decrypt PDF, the password is incorrect"); } if (dicRevision == 6 || dicRevision == 5) { validatePerms(encryption, dicPermissions, encryptMetadata); } if (encryption.getVersion() == 4 || encryption.getVersion() == 5) { // detect whether AES encryption is used. This assumes that the encryption algo is // stored in the PDCryptFilterDictionary // However, crypt filters are used only when V is 4 or 5. PDCryptFilterDictionary stdCryptFilterDictionary = encryption .getStdCryptFilterDictionary(); if (stdCryptFilterDictionary != null) { COSName cryptFilterMethod = stdCryptFilterDictionary.getCryptFilterMethod(); if (cryptFilterMethod != null) { setAES("AESV2".equalsIgnoreCase(cryptFilterMethod.getName()) || "AESV3".equalsIgnoreCase(cryptFilterMethod.getName())); } } } } private byte[] getDocumentIDBytes(COSArray documentIDArray) { // some documents may not have document id, see // test\encryption\encrypted_doc_no_id.pdf byte[] documentIDBytes; if (documentIDArray != null && documentIDArray.size() >= 1) { COSString id = (COSString) documentIDArray.getObject(0); documentIDBytes = id.getBytes(); } else { documentIDBytes = new byte[0]; } return documentIDBytes; } // Algorithm 13: validate permissions ("Perms" field). Relaxed to accomodate buggy encoders private void validatePerms(PDEncryption encryption, int dicPermissions, boolean encryptMetadata) throws IOException { try { BufferedBlockCipher cipher = new BufferedBlockCipher(new AESFastEngine()); cipher.init(false, new KeyParameter(getEncryptionKey())); byte[] buf = new byte[cipher.getOutputSize(encryption.getPerms().length)]; int len = cipher.processBytes(encryption.getPerms(), 0, encryption.getPerms().length, buf, 0); len += cipher.doFinal(buf, len); byte[] perms = copyOf(buf, len); if (perms[9] != 'a' || perms[10] != 'd' || perms[11] != 'b') { LOG.warn("Verification of permissions failed (constant)"); } int permsP = perms[0] & 0xFF | (perms[1] & 0xFF) << 8 | (perms[2] & 0xFF) << 16 | (perms[3] & 0xFF) << 24; if (permsP != dicPermissions) { LOG.warn("Verification of permissions failed (" + String.format("%08X", permsP) + " != " + String.format("%08X", dicPermissions) + ")"); } if (encryptMetadata && perms[8] != 'T' || !encryptMetadata && perms[8] != 'F') { LOG.warn("Verification of permissions failed (EncryptMetadata)"); } } catch (DataLengthException | IllegalStateException | InvalidCipherTextException e) { throw new IOException(e); } } /** * Check for owner password. * * @param ownerPassword The owner password. * @param user The u entry of the encryption dictionary. * @param owner The o entry of the encryption dictionary. * @param permissions The set of permissions on the document. * @param id The document id. * @param encRevision The encryption algorithm revision. * @param length The encryption key length. * @param encryptMetadata The encryption metadata * * @return True If the ownerPassword param is the owner password. * * @throws IOException If there is an error accessing data. */ public boolean isOwnerPassword(byte[] ownerPassword, byte[] user, byte[] owner, int permissions, byte[] id, int encRevision, int length, boolean encryptMetadata) throws IOException { if (encRevision == 6 || encRevision == 5) { byte[] truncatedOwnerPassword = truncate127(ownerPassword); byte[] oHash = new byte[32]; byte[] oValidationSalt = new byte[8]; System.arraycopy(owner, 0, oHash, 0, 32); System.arraycopy(owner, 32, oValidationSalt, 0, 8); byte[] hash; if (encRevision == 5) { hash = computeSHA256(truncatedOwnerPassword, oValidationSalt, user); } else { hash = computeHash2A(truncatedOwnerPassword, oValidationSalt, user); } return Arrays.equals(hash, oHash); } else { byte[] userPassword = getUserPassword(ownerPassword, owner, encRevision, length); return isUserPassword(userPassword, user, owner, permissions, id, encRevision, length, encryptMetadata); } } /** * Get the user password based on the owner password. * * @param ownerPassword The plaintext owner password. * @param owner The o entry of the encryption dictionary. * @param encRevision The encryption revision number. * @param length The key length. * * @return The u entry of the encryption dictionary. * * @throws IOException If there is an error accessing data while generating the user password. */ public byte[] getUserPassword(byte[] ownerPassword, byte[] owner, int encRevision, int length) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); byte[] rc4Key = computeRC4key(ownerPassword, encRevision, length); if (encRevision == 2) { decryptDataRC4(rc4Key, owner, result); } else if (encRevision == 3 || encRevision == 4) { byte[] iterationKey = new byte[rc4Key.length]; byte[] otemp = new byte[owner.length]; System.arraycopy(owner, 0, otemp, 0, owner.length); for (int i = 19; i >= 0; i--) { System.arraycopy(rc4Key, 0, iterationKey, 0, rc4Key.length); for (int j = 0; j < iterationKey.length; j++) { iterationKey[j] = (byte) (iterationKey[j] ^ (byte) i); } result.reset(); decryptDataRC4(iterationKey, otemp, result); otemp = result.toByteArray(); } } return result.toByteArray(); } /** * Compute the encryption key. * * @param password The password to compute the encrypted key. * @param o The O entry of the encryption dictionary. * @param u The U entry of the encryption dictionary. * @param oe The OE entry of the encryption dictionary. * @param ue The UE entry of the encryption dictionary. * @param permissions The permissions for the document. * @param id The document id. * @param encRevision The revision of the encryption algorithm. * @param length The length of the encryption key. * @param encryptMetadata The encryption metadata * @param isOwnerPassword whether the password given is the owner password (for revision 6) * * @return The encrypted key bytes. * * @throws IOException If there is an error with encryption. */ public byte[] computeEncryptedKey(byte[] password, byte[] o, byte[] u, byte[] oe, byte[] ue, int permissions, byte[] id, int encRevision, int length, boolean encryptMetadata, boolean isOwnerPassword) throws IOException { if (encRevision == 6 || encRevision == 5) { return computeEncryptedKeyRev56(password, isOwnerPassword, o, u, oe, ue, encRevision); } else { return computeEncryptedKeyRev234(password, o, permissions, id, encryptMetadata, length, encRevision); } } private byte[] computeEncryptedKeyRev234(byte[] password, byte[] o, int permissions, byte[] id, boolean encryptMetadata, int length, int encRevision) { // Algorithm 2, based on MD5 // PDFReference 1.4 pg 78 byte[] padded = truncateOrPad(password); MessageDigest md = MessageDigests.getMD5(); md.update(padded); md.update(o); md.update((byte) permissions); md.update((byte) (permissions >>> 8)); md.update((byte) (permissions >>> 16)); md.update((byte) (permissions >>> 24)); md.update(id); // (Security handlers of revision 4 or greater) If document metadata is not being // encrypted, pass 4 bytes with the value 0xFFFFFFFF to the MD5 hash function. // see 7.6.3.3 Algorithm 2 Step f of PDF 32000-1:2008 if (encRevision == 4 && !encryptMetadata) { md.update(new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }); } byte[] digest = md.digest(); if (encRevision == 3 || encRevision == 4) { for (int i = 0; i < 50; i++) { md.update(digest, 0, length); digest = md.digest(); } } byte[] result = new byte[length]; System.arraycopy(digest, 0, result, 0, length); return result; } private byte[] computeEncryptedKeyRev56(byte[] password, boolean isOwnerPassword, byte[] o, byte[] u, byte[] oe, byte[] ue, int encRevision) throws IOException { byte[] hash, fileKeyEnc; if (isOwnerPassword) { byte[] oKeySalt = new byte[8]; System.arraycopy(o, 40, oKeySalt, 0, 8); if (encRevision == 5) { hash = computeSHA256(password, oKeySalt, u); } else { hash = computeHash2A(password, oKeySalt, u); } fileKeyEnc = oe; } else { byte[] uKeySalt = new byte[8]; System.arraycopy(u, 40, uKeySalt, 0, 8); if (encRevision == 5) { hash = computeSHA256(password, uKeySalt, null); } else { hash = computeHash2A(password, uKeySalt, null); } fileKeyEnc = ue; } try { BufferedBlockCipher cipher = new BufferedBlockCipher( new CBCBlockCipher(new AESFastEngine())); cipher.init(false, new KeyParameter(hash)); byte[] buf = new byte[cipher.getOutputSize(fileKeyEnc.length)]; int len = cipher.processBytes(fileKeyEnc, 0, fileKeyEnc.length, buf, 0); len += cipher.doFinal(buf, len); return copyOf(buf, len); } catch (DataLengthException | IllegalStateException | InvalidCipherTextException e) { throw new IOException(e); } } /** * This will compute the user password hash. * * @param password The plain text password. * @param owner The owner password hash. * @param permissions The document permissions. * @param id The document id. * @param encRevision The revision of the encryption. * @param length The length of the encryption key. * @param encryptMetadata The encryption metadata * * @return The user password. * * @throws IOException if the password could not be computed */ public byte[] computeUserPassword(byte[] password, byte[] owner, int permissions, byte[] id, int encRevision, int length, boolean encryptMetadata) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); byte[] encKey = computeEncryptedKey(password, owner, null, null, null, permissions, id, encRevision, length, encryptMetadata, true); if (encRevision == 2) { decryptDataRC4(encKey, ENCRYPT_PADDING, result); } else if (encRevision == 3 || encRevision == 4) { MessageDigest md = MessageDigests.getMD5(); md.update(ENCRYPT_PADDING); md.update(id); result.write(md.digest()); byte[] iterationKey = new byte[encKey.length]; for (int i = 0; i < 20; i++) { System.arraycopy(encKey, 0, iterationKey, 0, iterationKey.length); for (int j = 0; j < iterationKey.length; j++) { iterationKey[j] = (byte) (iterationKey[j] ^ i); } ByteArrayInputStream input = new ByteArrayInputStream(result.toByteArray()); result.reset(); decryptDataRC4(iterationKey, input, result); } byte[] finalResult = new byte[32]; System.arraycopy(result.toByteArray(), 0, finalResult, 0, 16); System.arraycopy(ENCRYPT_PADDING, 0, finalResult, 16, 16); result.reset(); result.write(finalResult); } return result.toByteArray(); } /** * Compute the owner entry in the encryption dictionary. * * @param ownerPassword The plaintext owner password. * @param userPassword The plaintext user password. * @param encRevision The revision number of the encryption algorithm. * @param length The length of the encryption key. * * @return The o entry of the encryption dictionary. * * @throws IOException if the owner password could not be computed */ public byte[] computeOwnerPassword(byte[] ownerPassword, byte[] userPassword, int encRevision, int length) throws IOException { if (encRevision == 2 && length != 5) { throw new IOException("Expected length=5 actual=" + length); } byte[] rc4Key = computeRC4key(ownerPassword, encRevision, length); byte[] paddedUser = truncateOrPad(userPassword); ByteArrayOutputStream encrypted = new ByteArrayOutputStream(); decryptDataRC4(rc4Key, new ByteArrayInputStream(paddedUser), encrypted); if (encRevision == 3 || encRevision == 4) { byte[] iterationKey = new byte[rc4Key.length]; for (int i = 1; i < 20; i++) { System.arraycopy(rc4Key, 0, iterationKey, 0, rc4Key.length); for (int j = 0; j < iterationKey.length; j++) { iterationKey[j] = (byte) (iterationKey[j] ^ (byte) i); } ByteArrayInputStream input = new ByteArrayInputStream(encrypted.toByteArray()); encrypted.reset(); decryptDataRC4(iterationKey, input, encrypted); } } return encrypted.toByteArray(); } // steps (a) to (d) of "Algorithm 3: Computing the encryption dictionary?s O (owner password) value". private byte[] computeRC4key(byte[] ownerPassword, int encRevision, int length) { MessageDigest md = MessageDigests.getMD5(); byte[] digest = md.digest(truncateOrPad(ownerPassword)); if (encRevision == 3 || encRevision == 4) { for (int i = 0; i < 50; i++) { // this deviates from the spec - however, omitting the length // parameter prevents the file to be opened in Adobe Reader // with the owner password when the key length is 40 bit (= 5 bytes) md.update(digest, 0, length); digest = md.digest(); } } byte[] rc4Key = new byte[length]; System.arraycopy(digest, 0, rc4Key, 0, length); return rc4Key; } /** * This will take the password and truncate or pad it as necessary. * * @param password The password to pad or truncate. * * @return The padded or truncated password. */ private byte[] truncateOrPad(byte[] password) { byte[] padded = new byte[ENCRYPT_PADDING.length]; int bytesBeforePad = Math.min(password.length, padded.length); System.arraycopy(password, 0, padded, 0, bytesBeforePad); System.arraycopy(ENCRYPT_PADDING, 0, padded, bytesBeforePad, ENCRYPT_PADDING.length - bytesBeforePad); return padded; } /** * Check if a plaintext password is the user password. * * @param password The plaintext password. * @param user The u entry of the encryption dictionary. * @param owner The o entry of the encryption dictionary. * @param permissions The permissions set in the PDF. * @param id The document id used for encryption. * @param encRevision The revision of the encryption algorithm. * @param length The length of the encryption key. * @param encryptMetadata The encryption metadata * * @return true If the plaintext password is the user password. * * @throws IOException If there is an error accessing data. */ public boolean isUserPassword(byte[] password, byte[] user, byte[] owner, int permissions, byte[] id, int encRevision, int length, boolean encryptMetadata) throws IOException { if (encRevision == 2) { byte[] passwordBytes = computeUserPassword(password, owner, permissions, id, encRevision, length, encryptMetadata); return Arrays.equals(user, passwordBytes); } else if (encRevision == 3 || encRevision == 4) { byte[] passwordBytes = computeUserPassword(password, owner, permissions, id, encRevision, length, encryptMetadata); // compare first 16 bytes only return Arrays.equals(Arrays.copyOf(user, 16), Arrays.copyOf(passwordBytes, 16)); } else if (encRevision == 6 || encRevision == 5) { byte[] truncatedPassword = truncate127(password); byte[] uHash = new byte[32]; byte[] uValidationSalt = new byte[8]; System.arraycopy(user, 0, uHash, 0, 32); System.arraycopy(user, 32, uValidationSalt, 0, 8); byte[] hash; if (encRevision == 5) { hash = computeSHA256(truncatedPassword, uValidationSalt, null); } else { hash = computeHash2A(truncatedPassword, uValidationSalt, null); } return Arrays.equals(hash, uHash); } else { throw new IOException("Unknown Encryption Revision " + encRevision); } } /** * Check if a plaintext password is the user password. * * @param password The plaintext password. * @param user The u entry of the encryption dictionary. * @param owner The o entry of the encryption dictionary. * @param permissions The permissions set in the PDF. * @param id The document id used for encryption. * @param encRevision The revision of the encryption algorithm. * @param length The length of the encryption key. * @param encryptMetadata The encryption metadata * * @return true If the plaintext password is the user password. * * @throws IOException If there is an error accessing data. */ public boolean isUserPassword(String password, byte[] user, byte[] owner, int permissions, byte[] id, int encRevision, int length, boolean encryptMetadata) throws IOException { if (encRevision == 6 || encRevision == 5) { return isUserPassword(password.getBytes(StandardCharsets.UTF_8), user, owner, permissions, id, encRevision, length, encryptMetadata); } else { return isUserPassword(password.getBytes(StandardCharsets.ISO_8859_1), user, owner, permissions, id, encRevision, length, encryptMetadata); } } /** * Check for owner password. * * @param password The owner password. * @param user The u entry of the encryption dictionary. * @param owner The o entry of the encryption dictionary. * @param permissions The set of permissions on the document. * @param id The document id. * @param encRevision The encryption algorithm revision. * @param length The encryption key length. * @param encryptMetadata The encryption metadata * * @return True If the ownerPassword param is the owner password. * * @throws IOException If there is an error accessing data. */ public boolean isOwnerPassword(String password, byte[] user, byte[] owner, int permissions, byte[] id, int encRevision, int length, boolean encryptMetadata) throws IOException { return isOwnerPassword(password.getBytes(StandardCharsets.ISO_8859_1), user, owner, permissions, id, encRevision, length, encryptMetadata); } // Algorithm 2.A from ISO 32000-1 private byte[] computeHash2A(byte[] password, byte[] salt, byte[] u) throws IOException { byte[] userKey; if (u == null) { userKey = new byte[0]; } else if (u.length < 48) { throw new IOException("Bad U length"); } else if (u.length > 48) { // must truncate userKey = new byte[48]; System.arraycopy(u, 0, userKey, 0, 48); } else { userKey = u; } byte[] truncatedPassword = truncate127(password); byte[] input = concat(truncatedPassword, salt, userKey); return computeHash2B(input, truncatedPassword, userKey); } // Algorithm 2.B from ISO 32000-2 private static byte[] computeHash2B(byte[] input, byte[] password, byte[] userKey) throws IOException { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] k = md.digest(input); byte[] e = null; for (int round = 0; round < 64 || (e[e.length - 1] & 0xFF) > round - 32; round++) { byte[] k1; if (userKey != null && userKey.length >= 48) { k1 = new byte[64 * (password.length + k.length + 48)]; } else { k1 = new byte[64 * (password.length + k.length)]; } int pos = 0; for (int i = 0; i < 64; i++) { System.arraycopy(password, 0, k1, pos, password.length); pos += password.length; System.arraycopy(k, 0, k1, pos, k.length); pos += k.length; if (userKey != null && userKey.length >= 48) { System.arraycopy(userKey, 0, k1, pos, 48); pos += 48; } } byte[] kFirst = new byte[16]; byte[] kSecond = new byte[16]; System.arraycopy(k, 0, kFirst, 0, 16); System.arraycopy(k, 16, kSecond, 0, 16); Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); SecretKeySpec keySpec = new SecretKeySpec(kFirst, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(kSecond); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); e = cipher.doFinal(k1); byte[] eFirst = new byte[16]; System.arraycopy(e, 0, eFirst, 0, 16); BigInteger bi = new BigInteger(1, eFirst); BigInteger remainder = bi.mod(new BigInteger("3")); String nextHash = HASHES_2B[remainder.intValue()]; md = MessageDigest.getInstance(nextHash); k = md.digest(e); } if (k.length > 32) { byte[] kTrunc = new byte[32]; System.arraycopy(k, 0, kTrunc, 0, 32); return kTrunc; } return k; } catch (GeneralSecurityException e) { throw new IOException(e); } } private static byte[] computeSHA256(byte[] input, byte[] password, byte[] userKey) throws IOException { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(input); md.update(password); return userKey == null ? md.digest() : md.digest(userKey); } catch (NoSuchAlgorithmException e) { throw new IOException(e); } } private static byte[] concat(byte[] a, byte[] b, byte[] c) { byte[] o = new byte[a.length + b.length + c.length]; System.arraycopy(a, 0, o, 0, a.length); System.arraycopy(b, 0, o, a.length, b.length); System.arraycopy(c, 0, o, a.length + b.length, c.length); return o; } private static byte[] truncate127(byte[] in) { if (in.length <= 127) { return in; } byte[] trunc = new byte[127]; System.arraycopy(in, 0, trunc, 0, 127); return trunc; } @Override public boolean hasProtectionPolicy() { return policy != null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/encryption/package.html000066400000000000000000000020111320103431700267610ustar00rootroot00000000000000 The encryption package will handle the PDF document security handlers and the functionality of pluggable security handlers. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/000077500000000000000000000000001320103431700232625ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/CIDFontMapping.java000066400000000000000000000032331320103431700266700ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.ttf.OpenTypeFont; /** * A CIDFontMapping is a kind of FontMapping which allows for an additional TrueTypeFont substitute * to be provided if a CID font is not available. * * @author John Hewson */ public final class CIDFontMapping extends FontMapping { private final FontBoxFont ttf; public CIDFontMapping(OpenTypeFont font, FontBoxFont fontBoxFont, boolean isFallback) { super(font, isFallback); this.ttf = fontBoxFont; } /** * Returns a TrueType font when isCIDFont() is true, otherwise null. */ public FontBoxFont getTrueTypeFont() { return ttf; } /** * Returns true if this is a CID font. */ public boolean isCIDFont() { return getFont() != null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/CIDSystemInfo.java000066400000000000000000000031071320103431700265460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; /** * Represents a CIDSystemInfo for the FontMapper API. * * @author John Hewson */ public final class CIDSystemInfo { private final String registry; private final String ordering; private final int supplement; CIDSystemInfo(String registry, String ordering, int supplement) { this.registry = registry; this.ordering = ordering; this.supplement = supplement; } public String getRegistry() { return registry; } public String getOrdering() { return ordering; } public int getSupplement() { return supplement; } @Override public String toString() { return getRegistry() + "-" + getOrdering() + "-" + getSupplement(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/CMapManager.java000066400000000000000000000051051320103431700262410ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.fontbox.cmap.CMap; import org.apache.fontbox.cmap.CMapParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * CMap resource loader and cache. */ final class CMapManager { static Map cMapCache = Collections.synchronizedMap(new HashMap()); private static final Logger LOG = LoggerFactory.getLogger(CMapManager.class); private CMapManager() { } /** * Fetches the predefined CMap from disk (or cache). * * @param cMapName CMap name * @return The predefined CMap, never null. * @throws IOException */ public static CMap getPredefinedCMap(String cMapName) throws IOException { CMap cmap = cMapCache.get(cMapName); if (cmap != null) { return cmap; } CMapParser parser = new CMapParser(); CMap targetCmap = parser.parsePredefined(cMapName); // limit the cache to predefined CMaps cMapCache.put(targetCmap.getName(), targetCmap); return targetCmap; } /** * Parse the given CMap. * * @param cMapStream the CMap to be read * @return the parsed CMap */ public static CMap parseCMap(InputStream cMapStream) throws IOException { CMap targetCmap = null; if (cMapStream != null) { CMapParser parser = new CMapParser(); try { targetCmap = parser.parse(cMapStream); } catch(IOException e) { LOG.warn("Failed to parse CMap for font", e); } } return targetCmap; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FileSystemFontProvider.java000066400000000000000000000566241320103431700305700ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; import java.security.AccessControlException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.cff.CFFCIDFont; import org.apache.fontbox.cff.CFFFont; import org.apache.fontbox.ttf.NamingTable; import org.apache.fontbox.ttf.OTFParser; import org.apache.fontbox.ttf.OpenTypeFont; import org.apache.fontbox.ttf.TTFParser; import org.apache.fontbox.ttf.TrueTypeCollection; import org.apache.fontbox.ttf.TrueTypeCollection.TrueTypeFontProcessor; import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.fontbox.type1.Type1Font; import org.apache.fontbox.util.autodetect.FontFileFinder; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A FontProvider which searches for fonts on the local filesystem. * * @author John Hewson */ final class FileSystemFontProvider extends FontProvider { private static final String FONT_CACHE_SEPARATOR = "|"; private static final Logger LOG = LoggerFactory.getLogger(FileSystemFontProvider.class); private final List fontInfoList = new ArrayList<>(); private final FontCache cache; private static class FSFontInfo extends FontInfo { private final String postScriptName; private final FontFormat format; private final CIDSystemInfo cidSystemInfo; private final int usWeightClass; private final int sFamilyClass; private final int ulCodePageRange1; private final int ulCodePageRange2; private final int macStyle; private final PDPanoseClassification panose; private final File file; private transient FileSystemFontProvider parent; private FSFontInfo(File file, FontFormat format, String postScriptName, CIDSystemInfo cidSystemInfo, int usWeightClass, int sFamilyClass, int ulCodePageRange1, int ulCodePageRange2, int macStyle, byte[] panose, FileSystemFontProvider parent) { this.file = file; this.format = format; this.postScriptName = postScriptName; this.cidSystemInfo = cidSystemInfo; this.usWeightClass = usWeightClass; this.sFamilyClass = sFamilyClass; this.ulCodePageRange1 = ulCodePageRange1; this.ulCodePageRange2 = ulCodePageRange2; this.macStyle = macStyle; this.panose = panose != null ? new PDPanoseClassification(panose) : null; this.parent = parent; } @Override public String getPostScriptName() { return postScriptName; } @Override public FontFormat getFormat() { return format; } @Override public CIDSystemInfo getCIDSystemInfo() { return cidSystemInfo; } @Override public FontBoxFont getFont() { FontBoxFont cached = parent.cache.getFont(this); if (cached != null) { return cached; } FontBoxFont font; LOG.debug("Loading {} from {}", postScriptName, file); switch (format) { case PFB: font = parent.getType1Font(file); break; case TTF: font = parent.getTrueTypeFont(postScriptName, file); break; case OTF: font = parent.getOTFFont(file); break; default: throw new RuntimeException("can't happen"); } parent.cache.addFont(this, font); return font; } @Override public int getFamilyClass() { return sFamilyClass; } @Override public int getWeightClass() { return usWeightClass; } @Override public int getCodePageRange1() { return ulCodePageRange1; } @Override public int getCodePageRange2() { return ulCodePageRange2; } @Override public int getMacStyle() { return macStyle; } @Override public PDPanoseClassification getPanose() { return panose; } @Override public String toString() { return super.toString() + " " + file; } } /** * Represents ignored fonts (i.e. bitmap fonts). */ private static final class FSIgnored extends FSFontInfo { private FSIgnored(File file, FontFormat format, String postScriptName) { super(file, format, postScriptName, null, 0, 0, 0, 0, 0, null, null); } } /** * Constructor. */ FileSystemFontProvider(FontCache cache) { this.cache = cache; try { LOG.trace("Will search the local system for fonts"); // scan the local system for font files List files = new ArrayList<>(); FontFileFinder fontFileFinder = new FontFileFinder(); List fonts = fontFileFinder.find(); for (URI font : fonts) { files.add(new File(font)); } LOG.trace("Found " + files.size() + " fonts on the local system"); // load cached FontInfo objects List cachedInfos = loadDiskCache(files); if (cachedInfos != null && cachedInfos.size() > 0) { fontInfoList.addAll(cachedInfos); } else { LOG.warn("Building on-disk font cache, this may take a while"); scanFonts(files); saveDiskCache(); LOG.warn("Finished building on-disk font cache, found " + fontInfoList.size() + " fonts"); } } catch (AccessControlException e) { LOG.error("Error accessing the file system", e); } } private void scanFonts(List files) { for (File file : files) { if (file.getPath().toLowerCase().endsWith(".ttf") || file.getPath().toLowerCase().endsWith(".otf")) { addTrueTypeFont(file); } else if (file.getPath().toLowerCase().endsWith(".ttc") || file.getPath().toLowerCase().endsWith(".otc")) { addTrueTypeCollection(file); } else if (file.getPath().toLowerCase().endsWith(".pfb")) { addType1Font(file); } } } private File getDiskCacheFile() { String path = System.getProperty("org.sambox.fontcache"); if (path == null) { path = System.getProperty("user.home"); if (path == null) { path = System.getProperty("java.io.tmpdir"); } } return new File(path, ".sambox.cache"); } /** * Saves the font metadata cache to disk. */ private void saveDiskCache() { File file = getDiskCacheFile(); try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { for (FSFontInfo fontInfo : fontInfoList) { writer.write(fontInfo.postScriptName.trim().replace("|", "\\|")); writer.write(FONT_CACHE_SEPARATOR); writer.write(fontInfo.format.toString()); writer.write(FONT_CACHE_SEPARATOR); if (fontInfo.cidSystemInfo != null) { writer.write(fontInfo.cidSystemInfo.getRegistry() + '-' + fontInfo.cidSystemInfo.getOrdering() + '-' + fontInfo.cidSystemInfo.getSupplement()); } writer.write(FONT_CACHE_SEPARATOR); if (fontInfo.usWeightClass > -1) { writer.write(Integer.toHexString(fontInfo.usWeightClass)); } writer.write(FONT_CACHE_SEPARATOR); if (fontInfo.sFamilyClass > -1) { writer.write(Integer.toHexString(fontInfo.sFamilyClass)); } writer.write(FONT_CACHE_SEPARATOR); writer.write(Integer.toHexString(fontInfo.ulCodePageRange1)); writer.write(FONT_CACHE_SEPARATOR); writer.write(Integer.toHexString(fontInfo.ulCodePageRange2)); writer.write(FONT_CACHE_SEPARATOR); if (fontInfo.macStyle > -1) { writer.write(Integer.toHexString(fontInfo.macStyle)); } writer.write(FONT_CACHE_SEPARATOR); if (fontInfo.panose != null) { byte[] bytes = fontInfo.panose.getBytes(); for (int i = 0; i < 10; i++) { String str = Integer.toHexString(bytes[i]); if (str.length() == 1) { writer.write('0'); } writer.write(str); } } writer.write(FONT_CACHE_SEPARATOR); writer.write(fontInfo.file.getAbsolutePath()); writer.newLine(); } } catch (IOException | SecurityException e) { LOG.error("Could not write to font cache", e); } } /** * Loads the font metadata cache from disk. */ private List loadDiskCache(List files) { Set pending = new HashSet<>(); for (File file : files) { pending.add(file.getAbsolutePath()); } List results = new ArrayList<>(); File file = getDiskCacheFile(); boolean fileExists = false; try { fileExists = file.exists(); } catch (SecurityException e) { } if (fileExists) { try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String line; while ((line = reader.readLine()) != null) { String[] parts = line.split("(? 0) { String[] ros = parts[2].split("-"); cidSystemInfo = new CIDSystemInfo(ros[0], ros[1], Integer.parseInt(ros[2])); } if (parts[3].length() > 0) { usWeightClass = (int) Long.parseLong(parts[3], 16); } if (parts[4].length() > 0) { sFamilyClass = (int) Long.parseLong(parts[4], 16); } ulCodePageRange1 = (int) Long.parseLong(parts[5], 16); ulCodePageRange2 = (int) Long.parseLong(parts[6], 16); if (parts[7].length() > 0) { macStyle = (int) Long.parseLong(parts[7], 16); } if (parts[8].length() > 0) { panose = new byte[10]; for (int i = 0; i < 10; i++) { String str = parts[8].substring(i * 2, i * 2 + 2); int b = Integer.parseInt(str, 16); panose[i] = (byte) (b & 0xff); } } fontFile = new File(parts[9]); FSFontInfo info = new FSFontInfo(fontFile, format, postScriptName, cidSystemInfo, usWeightClass, sFamilyClass, ulCodePageRange1, ulCodePageRange2, macStyle, panose, this); results.add(info); pending.remove(fontFile.getAbsolutePath()); } } catch (IOException e) { LOG.error("Error loading font cache, will be re-built", e); return null; } } if (pending.size() > 0) { // re-build the entire cache if we encounter un-cached fonts (could be optimised) LOG.warn("New fonts found, font cache will be re-built"); return null; } return results; } /** * Adds a TTC or OTC to the file cache. To reduce memory, the parsed font is not cached. */ private void addTrueTypeCollection(final File ttcFile) { try (TrueTypeCollection ttc = new TrueTypeCollection(ttcFile)) { ttc.processAllFonts(new TrueTypeFontProcessor() { @Override public void process(TrueTypeFont ttf) throws IOException { addTrueTypeFontImpl(ttf, ttcFile); } }); } catch (NullPointerException e) // TTF parser is buggy { LOG.error("Could not load font file: " + ttcFile, e); } catch (IOException e) { LOG.error("Could not load font file: " + ttcFile, e); } } /** * Adds an OTF or TTF font to the file cache. To reduce memory, the parsed font is not cached. */ private void addTrueTypeFont(File ttfFile) { try { if (ttfFile.getPath().endsWith(".otf")) { OTFParser parser = new OTFParser(false, true); OpenTypeFont otf = parser.parse(ttfFile); addTrueTypeFontImpl(otf, ttfFile); } else { TTFParser parser = new TTFParser(false, true); TrueTypeFont ttf = parser.parse(ttfFile); addTrueTypeFontImpl(ttf, ttfFile); } } catch (NullPointerException e) // TTF parser is buggy { LOG.error("Could not load font file: " + ttfFile, e); } catch (IOException e) { LOG.error("Could not load font file: " + ttfFile, e); } } /** * Adds an OTF or TTF font to the file cache. To reduce memory, the parsed font is not cached. */ private void addTrueTypeFontImpl(TrueTypeFont ttf, File file) throws IOException { try { // read PostScript name, if any if (ttf.getName() != null) { // ignore bitmap fonts if (ttf.getHeader() == null) { fontInfoList.add(new FSIgnored(file, FontFormat.TTF, ttf.getName())); return; } int macStyle = ttf.getHeader().getMacStyle(); int sFamilyClass = -1; int usWeightClass = -1; int ulCodePageRange1 = 0; int ulCodePageRange2 = 0; byte[] panose = null; // Apple's AAT fonts don't have an OS/2 table if (ttf.getOS2Windows() != null) { sFamilyClass = ttf.getOS2Windows().getFamilyClass(); usWeightClass = ttf.getOS2Windows().getWeightClass(); ulCodePageRange1 = (int) ttf.getOS2Windows().getCodePageRange1(); ulCodePageRange2 = (int) ttf.getOS2Windows().getCodePageRange2(); panose = ttf.getOS2Windows().getPanose(); } String format; if (ttf instanceof OpenTypeFont && ((OpenTypeFont) ttf).isPostScript()) { format = "OTF"; CFFFont cff = ((OpenTypeFont) ttf).getCFF().getFont(); CIDSystemInfo ros = null; if (cff instanceof CFFCIDFont) { CFFCIDFont cidFont = (CFFCIDFont) cff; String registry = cidFont.getRegistry(); String ordering = cidFont.getOrdering(); int supplement = cidFont.getSupplement(); ros = new CIDSystemInfo(registry, ordering, supplement); } fontInfoList.add(new FSFontInfo(file, FontFormat.OTF, ttf.getName(), ros, usWeightClass, sFamilyClass, ulCodePageRange1, ulCodePageRange2, macStyle, panose, this)); } else { CIDSystemInfo ros = null; if (ttf.getTableMap().containsKey("gcid")) { // Apple's AAT fonts have a "gcid" table with CID info byte[] bytes = ttf.getTableBytes(ttf.getTableMap().get("gcid")); String reg = new String(bytes, 10, 64, StandardCharsets.US_ASCII); String registryName = reg.substring(0, reg.indexOf('\0')); String ord = new String(bytes, 76, 64, StandardCharsets.US_ASCII); String orderName = ord.substring(0, ord.indexOf('\0')); int supplementVersion = bytes[140] << 8 & bytes[141]; ros = new CIDSystemInfo(registryName, orderName, supplementVersion); } format = "TTF"; fontInfoList.add(new FSFontInfo(file, FontFormat.TTF, ttf.getName(), ros, usWeightClass, sFamilyClass, ulCodePageRange1, ulCodePageRange2, macStyle, panose, this)); } if (LOG.isTraceEnabled()) { NamingTable name = ttf.getNaming(); if (name != null) { LOG.trace(format + ": '" + name.getPostScriptName() + "' / '" + name.getFontFamily() + "' / '" + name.getFontSubFamily() + "'"); } } } else { fontInfoList.add(new FSIgnored(file, FontFormat.TTF, "*skipnoname*")); LOG.warn("Missing 'name' entry for PostScript name in font " + file); } } catch (IOException e) { fontInfoList.add(new FSIgnored(file, FontFormat.TTF, "*skipexception*")); LOG.error("Could not load font file: " + file, e); } finally { IOUtils.close(ttf); } } /** * Adds a Type 1 font to the file cache. To reduce memory, the parsed font is not cached. */ private void addType1Font(File pfbFile) { try (InputStream input = new FileInputStream(pfbFile)) { Type1Font type1 = Type1Font.createWithPFB(input); fontInfoList.add(new FSFontInfo(pfbFile, FontFormat.PFB, type1.getName(), null, -1, -1, 0, 0, -1, null, this)); if (LOG.isTraceEnabled()) { LOG.trace("PFB: '" + type1.getName() + "' / '" + type1.getFamilyName() + "' / '" + type1.getWeight() + "'"); } } catch (IOException e) { LOG.error("Could not load font file: " + pfbFile, e); } } private TrueTypeFont getTrueTypeFont(String postScriptName, File file) { try { return readTrueTypeFont(postScriptName, file); } catch (NullPointerException | IOException e) // TTF parser is buggy { LOG.error("Could not load font file: " + file, e); } return null; } private TrueTypeFont readTrueTypeFont(String postScriptName, File file) throws IOException { if (file.getName().toLowerCase().endsWith(".ttc")) { TrueTypeCollection ttc = new TrueTypeCollection(file); TrueTypeFont ttf = ttc.getFontByName(postScriptName); if (ttf == null) { ttc.close(); throw new IOException("Font " + postScriptName + " not found in " + file); } return ttf; } return new TTFParser(false, true).parse(file); } private OpenTypeFont getOTFFont(File file) { try { // todo JH: we don't yet support loading CFF fonts from OTC collections return new OTFParser(false, true).parse(file); } catch (IOException e) { LOG.error("Could not load font file: " + file, e); } return null; } private Type1Font getType1Font(File file) { try (InputStream input = new FileInputStream(file)) { return Type1Font.createWithPFB(input); } catch (IOException e) { LOG.error("Could not load font file: " + file, e); } return null; } @Override public String toDebugString() { StringBuilder sb = new StringBuilder(); for (FSFontInfo info : fontInfoList) { sb.append(info.getFormat()); sb.append(": "); sb.append(info.getPostScriptName()); sb.append(": "); sb.append(info.file.getPath()); sb.append('\n'); } return sb.toString(); } @Override public List getFontInfo() { return fontInfoList; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FontCache.java000066400000000000000000000034011320103431700257550ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.lang.ref.SoftReference; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.fontbox.FontBoxFont; /** * An in-memory cache for system fonts. This allows PDFBox to manage caching for a {@link FontProvider}. PDFBox is free * to purge this cache at will. * * @author John Hewson */ public final class FontCache { private final Map> cache = new ConcurrentHashMap>(); /** * Adds the given FontBox font to the cache. */ public void addFont(FontInfo info, FontBoxFont font) { cache.put(info, new SoftReference<>(font)); } /** * Returns the FontBox font associated with the given FontInfo. */ public FontBoxFont getFont(FontInfo info) { SoftReference reference = cache.get(info); return reference != null ? reference.get() : null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FontFormat.java000066400000000000000000000020631320103431700262050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; /** * Font file format. * * @author John Hewson */ public enum FontFormat { /** * TrueType font. */ TTF, /** * OpenType font. */ OTF, /** * Type 1 (binary) font. */ PFB } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FontInfo.java000066400000000000000000000071751320103431700256610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import org.apache.fontbox.FontBoxFont; /** * Information about a font on the system. * * @author John Hewson */ public abstract class FontInfo { /** * Returns the PostScript name of the font. */ public abstract String getPostScriptName(); /** * Returns the font's format. */ public abstract FontFormat getFormat(); /** * Returns the CIDSystemInfo associated with the font, if any. */ public abstract CIDSystemInfo getCIDSystemInfo(); /** * Returns a new FontBox font instance for the font. Implementors of this method must not * cache the return value of this method unless doing so via the current {@link FontCache}. */ public abstract FontBoxFont getFont(); /** * Returns the sFamilyClass field of the "OS/2" table, or -1. */ public abstract int getFamilyClass(); /** * Returns the usWeightClass field of the "OS/2" table, or -1. */ public abstract int getWeightClass(); /** * Returns the usWeightClass field as a Panose Weight. */ final int getWeightClassAsPanose() { int usWeightClass = getWeightClass(); switch (usWeightClass) { case -1: return 0; case 0: return 0; case 100: return 2; case 200: return 3; case 300: return 4; case 400: return 5; case 500: return 6; case 600: return 7; case 700: return 8; case 800: return 9; case 900: return 10; default: return 0; } } /** * Returns the ulCodePageRange1 field of the "OS/2" table, or 0. */ public abstract int getCodePageRange1(); /** * Returns the ulCodePageRange2 field of the "OS/2" table, or 0. */ public abstract int getCodePageRange2(); /** * Returns the ulCodePageRange1 and ulCodePageRange1 field of the "OS/2" table, or 0. */ final long getCodePageRange() { long range1 = getCodePageRange1() & 0x00000000ffffffffL; long range2 = getCodePageRange2() & 0x00000000ffffffffL; return range2 << 32 | range1; } /** * Returns the macStyle field of the "head" table, or -1. */ public abstract int getMacStyle(); /** * Returns the Panose classification of the font, if any. */ public abstract PDPanoseClassification getPanose(); // todo: 'post' table for Italic. Also: OS/2 fsSelection for italic/bold. // todo: ulUnicodeRange too? @Override public String toString() { return getPostScriptName() + " (" + getFormat() + ", mac: 0x" + Integer.toHexString(getMacStyle()) + ", os/2: 0x" + Integer.toHexString(getFamilyClass()) + ", cid: " + getCIDSystemInfo() + ")"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FontMapper.java000066400000000000000000000042351320103431700262040ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.ttf.TrueTypeFont; /** * Font mapper, locates non-embedded fonts. If you implement this then you're responsible for caching the fonts. * SoftReference is recommended. * * @author John Hewson */ public interface FontMapper { /** * Finds a TrueType font with the given PostScript name, or a suitable substitute, or null. * * @param fontDescriptor FontDescriptor */ FontMapping getTrueTypeFont(String baseFont, PDFontDescriptor fontDescriptor); /** * Finds a font with the given PostScript name, or a suitable substitute, or null. This allows * any font to be substituted with a PFB, TTF or OTF. * * @param fontDescriptor the FontDescriptor of the font to find */ FontMapping getFontBoxFont(String baseFont, PDFontDescriptor fontDescriptor); /** * Finds a CFF CID-Keyed font with the given PostScript name, or a suitable substitute, or null. * This method can also map CJK fonts via their CIDSystemInfo (ROS). * * @param fontDescriptor FontDescriptor * @param cidSystemInfo the CID system info, e.g. "Adobe-Japan1", if any. */ CIDFontMapping getCIDFont(String baseFont, PDFontDescriptor fontDescriptor, PDCIDSystemInfo cidSystemInfo); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FontMapperImpl.java000066400000000000000000000561051320103431700270310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Set; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.ttf.OpenTypeFont; import org.apache.fontbox.ttf.TTFParser; import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.fontbox.type1.Type1Font; /** * Font mapper, locates non-embedded fonts via a pluggable FontProvider. * * @author John Hewson */ final class FontMapperImpl implements FontMapper { private static final FontCache fontCache = new FontCache(); // todo: static cache isn't ideal private FontProvider fontProvider; private Map fontInfoByName; private final TrueTypeFont lastResortFont; /** Map of PostScript name substitutes, in priority order. */ private final Map> substitutes = new HashMap<>(); FontMapperImpl() { // substitutes for standard 14 fonts substitutes.put("Courier", Arrays.asList("CourierNew", "CourierNewPSMT", "LiberationMono", "NimbusMonL-Regu")); substitutes.put("Courier-Bold", Arrays.asList("CourierNewPS-BoldMT", "CourierNew-Bold", "LiberationMono-Bold", "NimbusMonL-Bold")); substitutes.put("Courier-Oblique", Arrays.asList("CourierNewPS-ItalicMT", "CourierNew-Italic", "LiberationMono-Italic", "NimbusMonL-ReguObli")); substitutes.put("Courier-BoldOblique", Arrays.asList("CourierNewPS-BoldItalicMT", "CourierNew-BoldItalic", "LiberationMono-BoldItalic", "NimbusMonL-BoldObli")); substitutes.put("Helvetica", Arrays.asList("ArialMT", "Arial", "LiberationSans", "NimbusSanL-Regu")); substitutes.put("Helvetica-Bold", Arrays.asList("Arial-BoldMT", "Arial-Bold", "LiberationSans-Bold", "NimbusSanL-Bold")); substitutes.put("Helvetica-Oblique", Arrays.asList("Arial-ItalicMT", "Arial-Italic", "Helvetica-Italic", "LiberationSans-Italic", "NimbusSanL-ReguItal")); substitutes.put("Helvetica-BoldOblique", Arrays.asList("Arial-BoldItalicMT", "Helvetica-BoldItalic", "LiberationSans-BoldItalic", "NimbusSanL-BoldItal")); substitutes.put("Times-Roman", Arrays.asList("TimesNewRomanPSMT", "TimesNewRoman", "TimesNewRomanPS", "LiberationSerif", "NimbusRomNo9L-Regu")); substitutes.put("Times-Bold", Arrays.asList("TimesNewRomanPS-BoldMT", "TimesNewRomanPS-Bold", "TimesNewRoman-Bold", "LiberationSerif-Bold", "NimbusRomNo9L-Medi")); substitutes.put("Times-Italic", Arrays.asList("TimesNewRomanPS-ItalicMT", "TimesNewRomanPS-Italic", "TimesNewRoman-Italic", "LiberationSerif-Italic", "NimbusRomNo9L-ReguItal")); substitutes.put("Times-BoldItalic", Arrays.asList("TimesNewRomanPS-BoldItalicMT", "TimesNewRomanPS-BoldItalic", "TimesNewRoman-BoldItalic", "LiberationSerif-BoldItalic", "NimbusRomNo9L-MediItal")); substitutes.put("Symbol", Arrays.asList("Symbol", "SymbolMT", "StandardSymL")); substitutes.put("ZapfDingbats", Arrays.asList("ZapfDingbatsITC", "Dingbats", "MS-Gothic")); // Acrobat also uses alternative names for Standard 14 fonts, which we map to those above // these include names such as "Arial" and "TimesNewRoman" for (String baseName : Standard14Fonts.getNames()) { if (!substitutes.containsKey(baseName)) { String mappedName = Standard14Fonts.getMappedFontName(baseName); substitutes.put(baseName, copySubstitutes(mappedName)); } } // ------------------------- try { String ttfName = "org/sejda/sambox/resources/ttf/LiberationSans-Regular.ttf"; URL url = FontMapper.class.getClassLoader().getResource(ttfName); if (url == null) { throw new IOException("Error loading resource: " + ttfName); } InputStream ttfStream = url.openStream(); TTFParser ttfParser = new TTFParser(); lastResortFont = ttfParser.parse(ttfStream); } catch (IOException e) { throw new RuntimeException(e); } } // lazy thread safe singleton private static class DefaultFontProvider { private static final FontProvider INSTANCE = new FileSystemFontProvider(fontCache); } /** * Sets the font service provider. */ public synchronized void setProvider(FontProvider fontProvider) { fontInfoByName = createFontInfoByName(fontProvider.getFontInfo()); this.fontProvider = fontProvider; } /** * Returns the font service provider. Defaults to using FileSystemFontProvider. */ public synchronized FontProvider getProvider() { if (fontProvider == null) { setProvider(DefaultFontProvider.INSTANCE); } return fontProvider; } /** * Returns the font cache associated with this FontMapper. This method is needed by FontProvider subclasses. */ public FontCache getFontCache() { return fontCache; } private Map createFontInfoByName(List fontInfoList) { Map map = new LinkedHashMap<>(); for (FontInfo info : fontInfoList) { for (String name : getPostScriptNames(info.getPostScriptName())) { map.put(name, info); } } return map; } /** * Gets alternative names, as seen in some PDFs, e.g. PDFBOX-142. */ private Set getPostScriptNames(String postScriptName) { Set names = new HashSet(); // built-in PostScript name names.add(postScriptName); // remove hyphens (e.g. Arial-Black -> ArialBlack) names.add(postScriptName.replaceAll("-", "")); return names; } /** * Copies a list of font substitutes, adding the original font at the start of the list. */ private List copySubstitutes(String postScriptName) { return new ArrayList(substitutes.get(postScriptName)); } /** * Adds a top-priority substitute for the given font. * * @param match PostScript name of the font to match * @param replace PostScript name of the font to use as a replacement */ public void addSubstitute(String match, String replace) { if (!substitutes.containsKey(match)) { substitutes.put(match, new ArrayList()); } substitutes.get(match).add(replace); } /** * Returns the substitutes for a given font. */ private List getSubstitutes(String postScriptName) { List subs = substitutes.get(postScriptName.replaceAll(" ", "")); if (subs != null) { return subs; } return Collections.emptyList(); } /** * Attempts to find a good fallback based on the font descriptor. */ private String getFallbackFontName(PDFontDescriptor fontDescriptor) { String fontName; if (fontDescriptor != null) { // heuristic detection of bold boolean isBold = false; String name = fontDescriptor.getFontName(); if (name != null) { String lower = fontDescriptor.getFontName().toLowerCase(); isBold = lower.contains("bold") || lower.contains("black") || lower.contains("heavy"); } // font descriptor flags should describe the style if (fontDescriptor.isFixedPitch()) { fontName = "Courier"; if (isBold && fontDescriptor.isItalic()) { fontName += "-BoldOblique"; } else if (isBold) { fontName += "-Bold"; } else if (fontDescriptor.isItalic()) { fontName += "-Oblique"; } } else if (fontDescriptor.isSerif()) { fontName = "Times"; if (isBold && fontDescriptor.isItalic()) { fontName += "-BoldItalic"; } else if (isBold) { fontName += "-Bold"; } else if (fontDescriptor.isItalic()) { fontName += "-Italic"; } else { fontName += "-Roman"; } } else { fontName = "Helvetica"; if (isBold && fontDescriptor.isItalic()) { fontName += "-BoldOblique"; } else if (isBold) { fontName += "-Bold"; } else if (fontDescriptor.isItalic()) { fontName += "-Oblique"; } } } else { // if there is no FontDescriptor then we just fall back to Times Roman fontName = "Times-Roman"; } return fontName; } /** * Finds a TrueType font with the given PostScript name, or a suitable substitute, or null. * * @param fontDescriptor FontDescriptor */ @Override public FontMapping getTrueTypeFont(String baseFont, PDFontDescriptor fontDescriptor) { TrueTypeFont ttf = (TrueTypeFont) findFont(FontFormat.TTF, baseFont); if (ttf != null) { return new FontMapping(ttf, false); } // fallback - todo: i.e. fuzzy match String fontName = getFallbackFontName(fontDescriptor); ttf = (TrueTypeFont) findFont(FontFormat.TTF, fontName); if (ttf == null) { // we have to return something here as TTFs aren't strictly required on the system ttf = lastResortFont; } return new FontMapping(ttf, true); } /** * Finds a font with the given PostScript name, or a suitable substitute, or null. This allows any font to be * substituted with a PFB, TTF or OTF. * * @param fontDescriptor the FontDescriptor of the font to find */ @Override public FontMapping getFontBoxFont(String baseFont, PDFontDescriptor fontDescriptor) { FontBoxFont font = findFontBoxFont(baseFont); if (font != null) { return new FontMapping(font, false); } // fallback - todo: i.e. fuzzy match String fallbackName = getFallbackFontName(fontDescriptor); font = findFontBoxFont(fallbackName); if (font == null) { // we have to return something here as TTFs aren't strictly required on the system font = lastResortFont; } return new FontMapping(font, true); } /** * Finds a font with the given PostScript name, or a suitable substitute, or null. * * @param postScriptName PostScript font name */ private FontBoxFont findFontBoxFont(String postScriptName) { Type1Font t1 = (Type1Font) findFont(FontFormat.PFB, postScriptName); if (t1 != null) { return t1; } TrueTypeFont ttf = (TrueTypeFont) findFont(FontFormat.TTF, postScriptName); if (ttf != null) { return ttf; } OpenTypeFont otf = (OpenTypeFont) findFont(FontFormat.OTF, postScriptName); if (otf != null) { return otf; } return null; } /** * Finds a font with the given PostScript name, or a suitable substitute, or null. * * @param postScriptName PostScript font name */ private FontBoxFont findFont(FontFormat format, String postScriptName) { // handle damaged PDFs, see PDFBOX-2884 if (postScriptName == null) { return null; } // make sure the font provider is initialized if (fontProvider == null) { getProvider(); } // first try to match the PostScript name FontInfo info = getFont(format, postScriptName); if (info != null) { return info.getFont(); } // remove hyphens (e.g. Arial-Black -> ArialBlack) info = getFont(format, postScriptName.replaceAll("-", "")); if (info != null) { return info.getFont(); } // then try named substitutes for (String substituteName : getSubstitutes(postScriptName)) { info = getFont(format, substituteName); if (info != null) { return info.getFont(); } } // then try converting Windows names e.g. (ArialNarrow,Bold) -> (ArialNarrow-Bold) info = getFont(format, postScriptName.replaceAll(",", "-")); if (info != null) { return info.getFont(); } // try appending "-Regular", works for Wingdings on windows info = getFont(format, postScriptName + "-Regular"); if (info != null) { return info.getFont(); } // no matches return null; } /** * Finds the named font with the given format. */ private FontInfo getFont(FontFormat format, String postScriptName) { // strip subset tag (happens when we substitute a corrupt embedded font, see PDFBOX-2642) if (postScriptName.contains("+")) { postScriptName = postScriptName.substring(postScriptName.indexOf('+') + 1); } // look up the PostScript name FontInfo info = fontInfoByName.get(postScriptName); if (info != null && info.getFormat() == format) { return info; } return null; } /** * Finds a CFF CID-Keyed font with the given PostScript name, or a suitable substitute, or null. This method can * also map CJK fonts via their CIDSystemInfo (ROS). * * @param fontDescriptor FontDescriptor * @param cidSystemInfo the CID system info, e.g. "Adobe-Japan1", if any. */ @Override public CIDFontMapping getCIDFont(String baseFont, PDFontDescriptor fontDescriptor, PDCIDSystemInfo cidSystemInfo) { // try name match or substitute with OTF OpenTypeFont otf1 = (OpenTypeFont) findFont(FontFormat.OTF, baseFont); if (otf1 != null) { return new CIDFontMapping(otf1, null, false); } // try name match or substitute with TTF TrueTypeFont ttf = (TrueTypeFont) findFont(FontFormat.TTF, baseFont); if (ttf != null) { return new CIDFontMapping(null, ttf, false); } if (cidSystemInfo != null) { // "In Acrobat 3.0.1 and later, Type 0 fonts that use a CMap whose CIDSystemInfo // dictionary defines the Adobe-GB1, Adobe-CNS1 Adobe-Japan1, or Adobe-Korea1 character // collection can also be substituted." - Adobe Supplement to the ISO 32000 String collection = cidSystemInfo.getRegistry() + "-" + cidSystemInfo.getOrdering(); if (collection.equals("Adobe-GB1") || collection.equals("Adobe-CNS1") || collection.equals("Adobe-Japan1") || collection.equals("Adobe-Korea1")) { // try automatic substitutes via character collection PriorityQueue queue = getFontMatches(fontDescriptor, cidSystemInfo); FontMatch bestMatch = queue.poll(); if (bestMatch != null) { FontBoxFont font = bestMatch.info.getFont(); if (font instanceof OpenTypeFont) { return new CIDFontMapping((OpenTypeFont) font, null, true); } else { return new CIDFontMapping(null, font, true); } } } } // last-resort fallback return new CIDFontMapping(null, lastResortFont, true); } /** * Returns a list of matching fonts, scored by suitability. Positive scores indicate matches for certain attributes, * while negative scores indicate mismatches. Zero scores are neutral. * * @param fontDescriptor FontDescriptor, always present. * @param cidSystemInfo Font's CIDSystemInfo, may be null. */ private PriorityQueue getFontMatches(PDFontDescriptor fontDescriptor, PDCIDSystemInfo cidSystemInfo) { PriorityQueue queue = new PriorityQueue<>(20); for (FontInfo info : fontInfoByName.values()) { // filter by CIDSystemInfo, if given if (cidSystemInfo != null && !isCharSetMatch(cidSystemInfo, info)) { continue; } FontMatch match = new FontMatch(info); // Panose is the most reliable if (fontDescriptor.getPanose() != null && info.getPanose() != null) { PDPanoseClassification panose = fontDescriptor.getPanose().getPanose(); if (panose.getFamilyKind() == info.getPanose().getFamilyKind()) { // serifs if (panose.getSerifStyle() == info.getPanose().getSerifStyle()) { // exact match match.score += 2; } else if (panose.getSerifStyle() >= 2 && panose.getSerifStyle() <= 5 && info.getPanose().getSerifStyle() >= 2 && info.getPanose().getSerifStyle() <= 5) { // cove (serif) match.score += 1; } else if (panose.getSerifStyle() >= 11 && panose.getSerifStyle() <= 13 && info.getPanose().getSerifStyle() >= 11 && info.getPanose().getSerifStyle() <= 13) { // sans-serif match.score += 1; } else if (panose.getSerifStyle() != 0 && info.getPanose().getSerifStyle() != 0) { // mismatch match.score -= 1; } // weight int weight = info.getPanose().getWeight(); int weightClass = info.getWeightClassAsPanose(); if (Math.abs(weight - weightClass) > 2) { // inconsistent data in system font, usWeightClass wins weight = weightClass; } if (panose.getWeight() == weight) { // exact match match.score += 2; } else if (panose.getWeight() > 1 && weight > 1) { float dist = Math.abs(panose.getWeight() - weight); match.score += 1 - dist * 0.5; } // todo: italic // ... } } else if (fontDescriptor.getFontWeight() > 0 && info.getWeightClass() > 0) { // usWeightClass is pretty reliable float dist = Math.abs(fontDescriptor.getFontWeight() - info.getWeightClass()); match.score += 1 - (dist / 100) * 0.5; } // todo: italic // ... queue.add(match); } return queue; } /** * Returns true if the character set described by CIDSystemInfo is present in the given font. Only applies to * Adobe-GB1, Adobe-CNS1, Adobe-Japan1, Adobe-Korea1, as per the PDF spec. */ private boolean isCharSetMatch(PDCIDSystemInfo cidSystemInfo, FontInfo info) { if (info.getCIDSystemInfo() != null) { return info.getCIDSystemInfo().getRegistry().equals(cidSystemInfo.getRegistry()) && info.getCIDSystemInfo().getOrdering().equals(cidSystemInfo.getOrdering()); } long codePageRange = info.getCodePageRange(); long JIS_JAPAN = 1 << 17; long CHINESE_SIMPLIFIED = 1 << 18; long KOREAN_WANSUNG = 1 << 19; long CHINESE_TRADITIONAL = 1 << 20; long KOREAN_JOHAB = 1 << 21; if (cidSystemInfo.getOrdering().equals("GB1") && (codePageRange & CHINESE_SIMPLIFIED) == CHINESE_SIMPLIFIED) { return true; } else if (cidSystemInfo.getOrdering().equals("CNS1") && (codePageRange & CHINESE_TRADITIONAL) == CHINESE_TRADITIONAL) { return true; } else if (cidSystemInfo.getOrdering().equals("Japan1") && (codePageRange & JIS_JAPAN) == JIS_JAPAN) { return true; } else { return cidSystemInfo.getOrdering().equals("Korea1") && (codePageRange & KOREAN_WANSUNG) == KOREAN_WANSUNG || (codePageRange & KOREAN_JOHAB) == KOREAN_JOHAB; } } /** * A potential match for a font substitution. */ private static class FontMatch implements Comparable { double score; final FontInfo info; FontMatch(FontInfo info) { this.info = info; } @Override public int compareTo(FontMatch match) { return Double.compare(match.score, this.score); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FontMappers.java000066400000000000000000000030751320103431700263700ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; /** * FontMapper factory class. * * @author John Hewson */ public final class FontMappers { private static FontMapper instance; private FontMappers() { } // lazy thread safe singleton private static class DefaultFontMapper { private static final FontMapper INSTANCE = new FontMapperImpl(); } /** * Returns the singleton FontMapper instance. */ public static FontMapper instance() { if (instance == null) { instance = DefaultFontMapper.INSTANCE; } return instance; } /** * Sets the singleton FontMapper instance. */ public static synchronized void set(FontMapper fontMapper) { instance = fontMapper; } }sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FontMapping.java000066400000000000000000000030671320103431700263550ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import org.apache.fontbox.FontBoxFont; /** * A font mapping from a PDF font to a FontBox font. * * @author John Hewson */ public class FontMapping { private final T font; private final boolean isFallback; public FontMapping(T font, boolean isFallback) { this.font = font; this.isFallback = isFallback; } /** * Returns the mapped, FontBox font. This is never null. */ public T getFont() { return font; } /** * Returns true if the mapped font is a fallback, i.e. a substitute based on basic font style, * such as bold/italic, rather than font name. */ public boolean isFallback() { return isFallback; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/FontProvider.java000066400000000000000000000025241320103431700265510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.util.List; /** * External font service provider interface. * * @author John Hewson */ public abstract class FontProvider { /** * Returns a string containing debugging information. This will be written to the log if no * suitable fonts are found and no fallback fonts are available. May be null. */ public abstract String toDebugString(); /** * Returns a list of information about fonts on the system. */ public abstract List getFontInfo(); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDCIDFont.java000066400000000000000000000272001320103431700256000ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.nonNull; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.Vector; /** * A CIDFont. A CIDFont is a PDF object that contains information about a CIDFont program. Although its Type value is * Font, a CIDFont is not actually a font. * *

* It is not usually necessary to use this class directly, prefer {@link PDType0Font}. * * @author Ben Litchfield */ public abstract class PDCIDFont implements COSObjectable, PDFontLike, PDVectorFont { protected final PDType0Font parent; private Map widths; private float defaultWidth; private float averageWidth; private final Map verticalDisplacementY = new HashMap<>(); // w1y private final Map positionVectors = new HashMap<>(); // v private float[] dw2 = new float[] { 880, -1000 }; protected final COSDictionary dict; private PDFontDescriptor fontDescriptor; /** * Constructor. * * @param fontDictionary The font dictionary according to the PDF specification. */ PDCIDFont(COSDictionary fontDictionary, PDType0Font parent) throws IOException { this.dict = fontDictionary; this.parent = parent; readWidths(); readVerticalDisplacements(); } private void readWidths() { widths = new HashMap<>(); COSArray wArray = dict.getDictionaryObject(COSName.W, COSArray.class); if (nonNull(wArray)) { int size = wArray.size(); int counter = 0; while (counter < size) { COSNumber firstCode = (COSNumber) wArray.getObject(counter++); COSBase next = wArray.getObject(counter++); if (next instanceof COSArray) { COSArray array = (COSArray) next; int startRange = firstCode.intValue(); int arraySize = array.size(); for (int i = 0; i < arraySize; i++) { COSNumber width = (COSNumber) array.getObject(i); widths.put(startRange + i, width.floatValue()); } } else { COSNumber secondCode = (COSNumber) next; COSNumber rangeWidth = (COSNumber) wArray.getObject(counter++); int startRange = firstCode.intValue(); int endRange = secondCode.intValue(); float width = rangeWidth.floatValue(); for (int i = startRange; i <= endRange; i++) { widths.put(i, width); } } } } } private void readVerticalDisplacements() { // default position vector and vertical displacement vector COSArray dw2Array = dict.getDictionaryObject(COSName.DW2, COSArray.class); if (nonNull(dw2Array)) { dw2 = new float[2]; COSBase base0 = dw2Array.getObject(0); COSBase base1 = dw2Array.getObject(1); if (base0 instanceof COSNumber && base1 instanceof COSNumber) { dw2[0] = ((COSNumber) base0).floatValue(); dw2[1] = ((COSNumber) base1).floatValue(); } } // vertical metrics for individual CIDs. COSArray w2Array = dict.getDictionaryObject(COSName.W2, COSArray.class); if (nonNull(w2Array)) { for (int i = 0; i < w2Array.size(); i++) { COSNumber c = (COSNumber) w2Array.getObject(i); COSBase next = w2Array.getObject(++i); if (next instanceof COSArray) { COSArray array = (COSArray) next; for (int j = 0; j < array.size(); j++) { int cid = c.intValue() + j; COSNumber w1y = (COSNumber) array.getObject(j); COSNumber v1x = (COSNumber) array.getObject(++j); COSNumber v1y = (COSNumber) array.getObject(++j); verticalDisplacementY.put(cid, w1y.floatValue()); positionVectors.put(cid, new Vector(v1x.floatValue(), v1y.floatValue())); } } else { int first = c.intValue(); int last = ((COSNumber) next).intValue(); COSNumber w1y = (COSNumber) w2Array.getObject(++i); COSNumber v1x = (COSNumber) w2Array.getObject(++i); COSNumber v1y = (COSNumber) w2Array.getObject(++i); for (int cid = first; cid <= last; cid++) { verticalDisplacementY.put(cid, w1y.floatValue()); positionVectors.put(cid, new Vector(v1x.floatValue(), v1y.floatValue())); } } } } } @Override public COSDictionary getCOSObject() { return dict; } /** * The PostScript name of the font. * * @return The postscript name of the font. */ public String getBaseFont() { return dict.getNameAsString(COSName.BASE_FONT); } @Override public String getName() { return getBaseFont(); } @Override public PDFontDescriptor getFontDescriptor() { if (fontDescriptor == null) { COSDictionary fd = dict.getDictionaryObject(COSName.FONT_DESC, COSDictionary.class); if (fd != null) { fontDescriptor = new PDFontDescriptor(fd); } } return fontDescriptor; } @Override public abstract Matrix getFontMatrix(); /** * Returns the Type 0 font which is the parent of this font. * * @return parent Type 0 font */ public final PDType0Font getParent() { return parent; } @Override public abstract BoundingBox getBoundingBox() throws IOException; /** * This will get the default width. The default value for the default width is 1000. * * @return The default width for the glyphs in this font. */ private float getDefaultWidth() { if (defaultWidth == 0) { COSNumber number = dict.getDictionaryObject(COSName.DW, COSNumber.class); if (nonNull(number)) { defaultWidth = number.floatValue(); } else { defaultWidth = 1000; } } return defaultWidth; } /** * Returns the default position vector (v). * * @param cid CID */ private Vector getDefaultPositionVector(int cid) { return new Vector(getWidthForCID(cid) / 2, dw2[0]); } private float getWidthForCID(int cid) { Float width = widths.get(cid); if (width == null) { width = getDefaultWidth(); } return width; } @Override public Vector getPositionVector(int code) { int cid = codeToCID(code); Vector v = positionVectors.get(cid); if (v == null) { v = getDefaultPositionVector(cid); } return v; } /** * Returns the y-component of the vertical displacement vector (w1). * * @param code character code * @return w1y */ public float getVerticalDisplacementVectorY(int code) { int cid = codeToCID(code); Float w1y = verticalDisplacementY.get(cid); if (w1y == null) { w1y = dw2[1]; } return w1y; } @Override public abstract float getHeight(int code) throws IOException; @Override public float getWidth(int code) throws IOException { // these widths are supposed to be consistent with the actual widths given in the CIDFont // program, but PDFBOX-563 shows that when they are not, Acrobat overrides the embedded // font widths with the widths given in the font dictionary return getWidthForCID(codeToCID(code)); } @Override public abstract float getWidthFromFont(int code) throws IOException; @Override public abstract boolean isEmbedded(); @Override // todo: this method is highly suspicious, the average glyph width is not usually a good metric public float getAverageFontWidth() { if (averageWidth == 0) { float totalWidths = 0.0f; int characterCount = 0; if (widths != null) { for (Float width : widths.values()) { if (width > 0) { totalWidths += width; ++characterCount; } } } averageWidth = totalWidths / characterCount; if (averageWidth <= 0 || Float.isNaN(averageWidth)) { averageWidth = getDefaultWidth(); } } return averageWidth; } /** * Returns the CIDSystemInfo, or null if it is missing (which isn't allowed but could happen). */ public PDCIDSystemInfo getCIDSystemInfo() { COSDictionary cidSystemInfoDict = dict.getDictionaryObject(COSName.CIDSYSTEMINFO, COSDictionary.class); if (nonNull(cidSystemInfoDict)) { return new PDCIDSystemInfo(cidSystemInfoDict); } return null; } /** * Returns the CID for the given character code. If not found then CID 0 is returned. * * @param code character code * @return CID */ public abstract int codeToCID(int code); /** * Returns the GID for the given character code. * * @param code character code * @return GID */ public abstract int codeToGID(int code) throws IOException; /** * Encodes the given Unicode code point for use in a PDF content stream. Content streams use a multi-byte encoding * with 1 to 4 bytes. * *

* This method is called when embedding text in PDFs and when filling in fields. * * @param unicode Unicode code point. * @return Array of 1 to 4 PDF content stream bytes. * @throws IOException If the text could not be encoded. */ protected abstract byte[] encode(int unicode) throws IOException; } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDCIDFontType0.java000066400000000000000000000315471320103431700265330ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static org.sejda.sambox.pdmodel.font.UniUtil.getUniNameOfCodePoint; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.cff.CFFCIDFont; import org.apache.fontbox.cff.CFFFont; import org.apache.fontbox.cff.CFFParser; import org.apache.fontbox.cff.CFFType1Font; import org.apache.fontbox.cff.Type2CharString; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.util.Matrix; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Type 0 CIDFont (CFF). * * @author Ben Litchfield * @author John Hewson */ public class PDCIDFontType0 extends PDCIDFont { private static final Logger LOG = LoggerFactory.getLogger(PDCIDFontType0.class); private final CFFCIDFont cidFont; // Top DICT that uses CIDFont operators private final FontBoxFont t1Font; // Top DICT that does not use CIDFont operators private final Map glyphHeights = new HashMap<>(); private final boolean isEmbedded; private final boolean isDamaged; private Float avgWidth = null; private Matrix fontMatrix; private final AffineTransform fontMatrixTransform; private BoundingBox fontBBox; /** * Constructor. * * @param fontDictionary The font dictionary according to the PDF specification. * @param parent The parent font. */ public PDCIDFontType0(COSDictionary fontDictionary, PDType0Font parent) throws IOException { super(fontDictionary, parent); PDFontDescriptor fd = getFontDescriptor(); byte[] bytes = null; if (fd != null) { PDStream ff3Stream = fd.getFontFile3(); if (ff3Stream != null) { bytes = IOUtils.toByteArray(ff3Stream.createInputStream()); } } boolean fontIsDamaged = false; CFFFont cffFont = null; if (bytes != null && bytes.length > 0 && (bytes[0] & 0xff) == '%') { // PDFBOX-2642 contains a corrupt PFB font instead of a CFF LOG.warn("Found PFB but expected embedded CFF font " + fd.getFontName()); fontIsDamaged = true; } else if (bytes != null) { CFFParser cffParser = new CFFParser(); try { cffFont = cffParser.parse(bytes, new ByteSource()).get(0); } catch (IOException e) { LOG.error("Can't read the embedded CFF font " + fd.getFontName(), e); fontIsDamaged = true; } } if (cffFont != null) { // embedded if (cffFont instanceof CFFCIDFont) { cidFont = (CFFCIDFont) cffFont; t1Font = null; } else { cidFont = null; t1Font = cffFont; } isEmbedded = true; isDamaged = false; } else { // find font or substitute CIDFontMapping mapping = FontMappers.instance().getCIDFont(getBaseFont(), getFontDescriptor(), getCIDSystemInfo()); FontBoxFont font; if (mapping.isCIDFont()) { cffFont = mapping.getFont().getCFF().getFont(); if (cffFont instanceof CFFCIDFont) { cidFont = (CFFCIDFont) cffFont; t1Font = null; font = cidFont; } else { // PDFBOX-3515: OpenType fonts are loaded as CFFType1Font CFFType1Font f = (CFFType1Font) cffFont; cidFont = null; t1Font = f; font = f; } } else { cidFont = null; t1Font = mapping.getTrueTypeFont(); font = t1Font; } if (mapping.isFallback()) { LOG.warn("Using fallback " + font.getName() + " for CID-keyed font " + getBaseFont()); } isEmbedded = false; isDamaged = fontIsDamaged; } fontMatrixTransform = getFontMatrix().createAffineTransform(); fontMatrixTransform.scale(1000, 1000); } @Override public final Matrix getFontMatrix() { if (fontMatrix == null) { List numbers; if (cidFont != null) { numbers = cidFont.getFontMatrix(); } else { try { numbers = t1Font.getFontMatrix(); } catch (IOException e) { return new Matrix(0.001f, 0, 0, 0.001f, 0, 0); } } if (numbers != null && numbers.size() == 6) { fontMatrix = new Matrix(numbers.get(0).floatValue(), numbers.get(1).floatValue(), numbers.get(2).floatValue(), numbers.get(3).floatValue(), numbers.get(4).floatValue(), numbers.get(5).floatValue()); } else { fontMatrix = new Matrix(0.001f, 0, 0, 0.001f, 0, 0); } } return fontMatrix; } private class ByteSource implements CFFParser.ByteSource { @Override public byte[] getBytes() throws IOException { PDStream ff3Stream = getFontDescriptor().getFontFile3(); return IOUtils.toByteArray(ff3Stream.createInputStream()); } } @Override public BoundingBox getBoundingBox() { if (fontBBox == null) { fontBBox = generateBoundingBox(); } return fontBBox; } private BoundingBox generateBoundingBox() { if (getFontDescriptor() != null) { PDRectangle bbox = getFontDescriptor().getFontBoundingBox(); if (bbox.getLowerLeftX() != 0 || bbox.getLowerLeftY() != 0 || bbox.getUpperRightX() != 0 || bbox.getUpperRightY() != 0) { return new BoundingBox(bbox.getLowerLeftX(), bbox.getLowerLeftY(), bbox.getUpperRightX(), bbox.getUpperRightY()); } } if (cidFont != null) { return cidFont.getFontBBox(); } try { return t1Font.getFontBBox(); } catch (IOException e) { return new BoundingBox(); } } /** * Returns the embedded CFF CIDFont, or null if the substitute is not a CFF font. */ public CFFFont getCFFFont() { if (cidFont != null) { return cidFont; } else if (t1Font instanceof CFFType1Font) { return (CFFType1Font) t1Font; } return null; } /** * Returns the embedded or substituted font. */ public FontBoxFont getFontBoxFont() { if (cidFont != null) { return cidFont; } return t1Font; } /** * Returns the Type 2 charstring for the given CID, or null if the substituted font does not contain Type 2 * charstrings. * * @param cid CID * @throws IOException if the charstring could not be read */ public Type2CharString getType2CharString(int cid) throws IOException { if (cidFont != null) { return cidFont.getType2CharString(cid); } else if (t1Font instanceof CFFType1Font) { return ((CFFType1Font) t1Font).getType2CharString(cid); } else { return null; } } /** * Returns the name of the glyph with the given character code. This is done by looking up the code in the parent * font's ToUnicode map and generating a glyph name from that. */ private String getGlyphName(int code) throws IOException { String unicodes = parent.toUnicode(code); if (unicodes == null) { return ".notdef"; } return getUniNameOfCodePoint(unicodes.codePointAt(0)); } @Override public GeneralPath getPath(int code) throws IOException { int cid = codeToCID(code); Type2CharString charstring = getType2CharString(cid); if (charstring != null) { return charstring.getPath(); } else if (isEmbedded && t1Font instanceof CFFType1Font) { return ((CFFType1Font) t1Font).getType2CharString(cid).getPath(); } else { return t1Font.getPath(getGlyphName(code)); } } @Override public boolean hasGlyph(int code) throws IOException { int cid = codeToCID(code); Type2CharString charstring = getType2CharString(cid); if (charstring != null) { return charstring.getGID() != 0; } else if (isEmbedded && t1Font instanceof CFFType1Font) { return ((CFFType1Font) t1Font).getType2CharString(cid).getGID() != 0; } else { return t1Font.hasGlyph(getGlyphName(code)); } } /** * Returns the CID for the given character code. If not found then CID 0 is returned. * * @param code character code * @return CID */ @Override public int codeToCID(int code) { return parent.getCMap().toCID(code); } @Override public int codeToGID(int code) { int cid = codeToCID(code); if (cidFont != null) { // The CIDs shall be used to determine the GID value for the glyph procedure using the // charset table in the CFF program return cidFont.getCharset().getGIDForCID(cid); } else { // The CIDs shall be used directly as GID values return cid; } } @Override public byte[] encode(int unicode) { // todo: we can use a known character collection CMap for a CIDFont // and an Encoding for Type 1-equivalent throw new UnsupportedOperationException(); } @Override public float getWidthFromFont(int code) throws IOException { int cid = codeToCID(code); float width; if (cidFont != null) { width = getType2CharString(cid).getWidth(); } else if (isEmbedded && t1Font instanceof CFFType1Font) { width = ((CFFType1Font) t1Font).getType2CharString(cid).getWidth(); } else { width = t1Font.getWidth(getGlyphName(code)); } Point2D p = new Point2D.Float(width, 0); fontMatrixTransform.transform(p, p); return (float) p.getX(); } @Override public boolean isEmbedded() { return isEmbedded; } @Override public boolean isDamaged() { return isDamaged; } @Override public float getHeight(int code) throws IOException { int cid = codeToCID(code); float height = 0; if (!glyphHeights.containsKey(cid)) { height = (float) getType2CharString(cid).getBounds().getHeight(); glyphHeights.put(cid, height); } return height; } @Override public float getAverageFontWidth() { if (avgWidth == null) { avgWidth = getAverageCharacterWidth(); } return avgWidth; } // todo: this is a replacement for FontMetrics method private float getAverageCharacterWidth() { // todo: not implemented, highly suspect return 500; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDCIDFontType2.java000066400000000000000000000374671320103431700265440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.nonNull; import java.awt.geom.GeneralPath; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import org.apache.fontbox.cff.Type2CharString; import org.apache.fontbox.cmap.CMap; import org.apache.fontbox.ttf.CmapSubtable; import org.apache.fontbox.ttf.GlyphData; import org.apache.fontbox.ttf.OTFParser; import org.apache.fontbox.ttf.OpenTypeFont; import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.ReflectionUtils; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Type 2 CIDFont (TrueType). * * @author Ben Litchfield */ public class PDCIDFontType2 extends PDCIDFont { private static final Logger LOG = LoggerFactory.getLogger(PDCIDFontType2.class); private final TrueTypeFont ttf; private final int[] cid2gid; private final boolean isEmbedded; private final boolean isDamaged; private final CmapSubtable cmap; // may be null private Matrix fontMatrix; private BoundingBox fontBBox; /** * Constructor. * * @param fontDictionary The font dictionary according to the PDF specification. * @param parent The parent font. * @throws IOException */ public PDCIDFontType2(COSDictionary fontDictionary, PDType0Font parent) throws IOException { this(fontDictionary, parent, null); } /** * Constructor. * * @param fontDictionary The font dictionary according to the PDF specification. * @param parent The parent font. * @param trueTypeFont The true type font used to create the parent font * @throws IOException */ public PDCIDFontType2(COSDictionary fontDictionary, PDType0Font parent, TrueTypeFont trueTypeFont) throws IOException { super(fontDictionary, parent); PDFontDescriptor fd = getFontDescriptor(); if (trueTypeFont != null) { ttf = trueTypeFont; isEmbedded = true; isDamaged = false; } else { boolean fontIsDamaged = false; TrueTypeFont ttfFont = null; PDStream stream = null; if (fd != null) { stream = fd.getFontFile2(); if (stream == null) { stream = fd.getFontFile3(); } if (stream == null) { // Acrobat looks in FontFile too, even though it is not in the spec, see PDFBOX-2599 stream = fd.getFontFile(); } } if (stream != null) { try { // embedded OTF or TTF OTFParser otfParser = new OTFParser(true); OpenTypeFont otf = otfParser.parse(stream.createInputStream()); ttfFont = otf; if (otf.isPostScript()) { // PDFBOX-3344 contains PostScript outlines instead of TrueType fontIsDamaged = true; LOG.warn( "Found CFF/OTF but expected embedded TTF font " + fd.getFontName()); } if (otf.hasLayoutTables()) { LOG.info("OpenType Layout tables used in font " + getBaseFont() + " are not implemented in SAMBox and will be ignored"); } } catch (NullPointerException e) // TTF parser is buggy { fontIsDamaged = true; LOG.warn("Could not read embedded OTF for font " + getBaseFont(), e); } catch (IOException e) { fontIsDamaged = true; LOG.warn("Could not read embedded OTF for font " + getBaseFont(), e); } } isEmbedded = ttfFont != null; isDamaged = fontIsDamaged; if (ttfFont == null) { ttfFont = findFontOrSubstitute(); } ttf = ttfFont; } cmap = ttf.getUnicodeCmap(false); cid2gid = readCIDToGIDMap(); } private TrueTypeFont findFontOrSubstitute() throws IOException { TrueTypeFont ttfFont; CIDFontMapping mapping = FontMappers.instance().getCIDFont(getBaseFont(), getFontDescriptor(), getCIDSystemInfo()); if (mapping.isCIDFont()) { ttfFont = mapping.getFont(); } else { ttfFont = (TrueTypeFont) mapping.getTrueTypeFont(); } if (mapping.isFallback()) { LOG.warn("Using fallback font " + ttfFont.getName() + " for CID-keyed TrueType font " + getBaseFont()); } return ttfFont; } @Override public Matrix getFontMatrix() { if (fontMatrix == null) { // 1000 upem, this is not strictly true fontMatrix = new Matrix(0.001f, 0, 0, 0.001f, 0, 0); } return fontMatrix; } @Override public BoundingBox getBoundingBox() throws IOException { if (fontBBox == null) { fontBBox = generateBoundingBox(); } return fontBBox; } private BoundingBox generateBoundingBox() throws IOException { if (getFontDescriptor() != null) { PDRectangle bbox = getFontDescriptor().getFontBoundingBox(); if (nonNull(bbox) && bbox.getLowerLeftX() != 0 || bbox.getLowerLeftY() != 0 || bbox.getUpperRightX() != 0 || bbox.getUpperRightY() != 0) { return new BoundingBox(bbox.getLowerLeftX(), bbox.getLowerLeftY(), bbox.getUpperRightX(), bbox.getUpperRightY()); } } return ttf.getFontBBox(); } private int[] readCIDToGIDMap() throws IOException { int[] cid2gid = null; COSBase map = dict.getDictionaryObject(COSName.CID_TO_GID_MAP); if (map instanceof COSStream) { COSStream stream = (COSStream) map; InputStream is = stream.getUnfilteredStream(); byte[] mapAsBytes = IOUtils.toByteArray(is); IOUtils.closeQuietly(is); int numberOfInts = mapAsBytes.length / 2; cid2gid = new int[numberOfInts]; int offset = 0; for (int index = 0; index < numberOfInts; index++) { int gid = (mapAsBytes[offset] & 0xff) << 8 | mapAsBytes[offset + 1] & 0xff; cid2gid[index] = gid; offset += 2; } } return cid2gid; } @Override public int codeToCID(int code) { CMap cMap = parent.getCMap(); // Acrobat allows bad PDFs to use Unicode CMaps here instead of CID CMaps, see PDFBOX-1283 if (!cMap.hasCIDMappings() && cMap.hasUnicodeMappings()) { return cMap.toUnicode(code).codePointAt(0); // actually: code -> CID } return cMap.toCID(code); } /** * Returns the GID for the given character code. * * @param code character code * @return GID * @throws IOException */ @Override public int codeToGID(int code) throws IOException { if (!isEmbedded) { // The conforming reader shall select glyphs by translating characters from the // encoding specified by the predefined CMap to one of the encodings in the TrueType // font's 'cmap' table. The means by which this is accomplished are implementation- // dependent. // omit the CID2GID mapping if the embedded font is replaced by an external font if (cid2gid != null && !isDamaged) { // Acrobat allows non-embedded GIDs - todo: can we find a test PDF for this? LOG.warn("Using non-embedded GIDs in font " + getName()); int cid = codeToCID(code); return cid2gid[cid]; } // fallback to the ToUnicode CMap, test with PDFBOX-1422 and PDFBOX-2560 String unicode = parent.toUnicode(code); if (unicode == null) { LOG.warn("Failed to find a character mapping for " + code + " in " + getName()); // Acrobat is willing to use the CID as a GID, even when the font isn't embedded // see PDFBOX-2599 return codeToCID(code); } if (unicode.length() > 1) { LOG.warn("Trying to map multi-byte character using 'cmap', result will be poor"); } // a non-embedded font always has a cmap (otherwise FontMapper won't load it) return cmap.getGlyphId(unicode.codePointAt(0)); } // If the TrueType font program is embedded, the Type 2 CIDFont dictionary shall contain // a CIDToGIDMap entry that maps CIDs to the glyph indices for the appropriate glyph // descriptions in that font program. int cid = codeToCID(code); if (cid2gid != null) { // use CIDToGIDMap if (cid < cid2gid.length) { return cid2gid[cid]; } return 0; } // "Identity" is the default CIDToGIDMap if (cid < ttf.getNumberOfGlyphs()) { return cid; } // out of range CIDs map to GID 0 return 0; } @Override public float getHeight(int code) throws IOException { // todo: really we want the BBox, (for text extraction:) return (ttf.getHorizontalHeader().getAscender() + -ttf.getHorizontalHeader().getDescender()) / ttf.getUnitsPerEm(); // todo: shouldn't this be the yMax/yMin? } @Override public float getWidthFromFont(int code) throws IOException { int gid = codeToGID(code); int width = ttf.getAdvanceWidth(gid); int unitsPerEM = ttf.getUnitsPerEm(); if (unitsPerEM != 1000) { width *= 1000f / unitsPerEM; } return width; } @Override public byte[] encode(int unicode) { int cid = -1; if (isEmbedded) { // embedded fonts always use CIDToGIDMap, with Identity as the default if (parent.getCMap().getName().startsWith("Identity-")) { if (cmap != null) { cid = cmap.getGlyphId(unicode); } } else { // if the CMap is predefined then there will be a UCS-2 CMap if (parent.getCMapUCS2() != null) { cid = parent.getCMapUCS2().toCID(unicode); } } if (cid == -1) { // invert the ToUnicode CMap // this helps when re-encoding text with an existing subset font cid = lookupInInvertedUnicodeCmap(unicode); } // otherwise we require an explicit ToUnicode CMap if (cid == -1) { cid = 0; } } else { // a non-embedded font always has a cmap (otherwise it we wouldn't load it) cid = cmap.getGlyphId(unicode); } if (cid == 0) { throw new IllegalArgumentException( String.format("No glyph for U+%04X in font %s", unicode, getName())); } // CID is always 2-bytes (16-bit) for TrueType return new byte[] { (byte) (cid >> 8 & 0xff), (byte) (cid & 0xff) }; } private Map invertedUnicodeCmap = null; /** * Inverts the unicode cmap from the parent and uses it for lookup */ private Map generateInvertedUnicodeCmap() { CMap cMap = parent.getToUnicodeCMap(); if (cMap != null) { // fontbox doesn't expose the charToUnicode map via getter // use reflection to get access to the underlying data Class clazz = CMap.class; Field charToUnicodeField = ReflectionUtils.findField(clazz, "charToUnicode"); ReflectionUtils.makeAccessible(charToUnicodeField); final Map charToUnicode = (Map) ReflectionUtils .getField(charToUnicodeField, cMap); // if there's an char to unicode map, invert it and use it for lookup if (charToUnicode != null) { Map invertedUnicodeCmap = new HashMap<>(charToUnicode.size()); for (Integer code : charToUnicode.keySet()) { invertedUnicodeCmap.put(charToUnicode.get(code), code); } return invertedUnicodeCmap; } } return new HashMap<>(); } private int lookupInInvertedUnicodeCmap(int unicode) { if (invertedUnicodeCmap == null) { invertedUnicodeCmap = generateInvertedUnicodeCmap(); } String s = new String(Character.toChars(unicode)); if (invertedUnicodeCmap.containsKey(s)) { return invertedUnicodeCmap.get(s); } return -1; } @Override public boolean isEmbedded() { return isEmbedded; } @Override public boolean isDamaged() { return isDamaged; } /** * Returns the embedded or substituted TrueType font. May be an OpenType font if the font is not embedded. */ public TrueTypeFont getTrueTypeFont() { return ttf; } @Override public GeneralPath getPath(int code) throws IOException { if (ttf instanceof OpenTypeFont && ((OpenTypeFont) ttf).isPostScript()) { // we're not supposed to have CFF fonts inside PDCIDFontType2, but if we do, // then we treat their CIDs as GIDs, see PDFBOX-3344 int cid = codeToGID(code); Type2CharString charstring = ((OpenTypeFont) ttf).getCFF().getFont() .getType2CharString(cid); return charstring.getPath(); } int gid = codeToGID(code); GlyphData glyph = ttf.getGlyph().getGlyph(gid); if (glyph != null) { return glyph.getPath(); } return new GeneralPath(); } @Override public boolean hasGlyph(int code) throws IOException { return codeToGID(code) != 0; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDCIDFontType2Embedder.java000066400000000000000000000323411320103431700301560ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.fontbox.ttf.TrueTypeFont; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.util.SpecVersionUtils; /** * Embedded PDCIDFontType2 builder. Helper class to populate a PDCIDFontType2 and its parent PDType0Font from a TTF. * * @author Keiji Suzuki * @author John Hewson */ final class PDCIDFontType2Embedder extends TrueTypeEmbedder { private final PDDocument document; private final PDType0Font parent; private final COSDictionary dict; private final COSDictionary cidFont; /** * Creates a new TrueType font embedder for the given TTF as a PDCIDFontType2. * * @param document parent document * @param dict font dictionary * @param ttf True Type Font * @param parent parent Type 0 font * @throws IOException if the TTF could not be read */ PDCIDFontType2Embedder(PDDocument document, COSDictionary dict, TrueTypeFont ttf, boolean embedSubset, PDType0Font parent) throws IOException { super(dict, ttf, embedSubset); this.document = document; this.dict = dict; this.parent = parent; // parent Type 0 font dict.setItem(COSName.SUBTYPE, COSName.TYPE0); dict.setName(COSName.BASE_FONT, fontDescriptor.getFontName()); dict.setItem(COSName.ENCODING, COSName.IDENTITY_H); // CID = GID // descendant CIDFont cidFont = createCIDFont(); COSArray descendantFonts = new COSArray(); descendantFonts.add(cidFont); dict.setItem(COSName.DESCENDANT_FONTS, descendantFonts); if (!embedSubset) { // build GID -> Unicode map buildToUnicodeCMap(null); } // ToUnicode CMap buildToUnicodeCMap(null); } /** * Rebuild a font subset. */ @Override protected void buildSubset(InputStream ttfSubset, String tag, Map gidToCid) throws IOException { // build CID2GIDMap, because the content stream has been written with the old GIDs Map cidToGid = new HashMap<>(gidToCid.size()); for (Map.Entry entry : gidToCid.entrySet()) { int newGID = entry.getKey(); int oldGID = entry.getValue(); cidToGid.put(oldGID, newGID); } // build unicode mapping before subsetting as the subsetted font won't have a cmap buildToUnicodeCMap(gidToCid); // rebuild the relevant part of the font buildFontFile2(ttfSubset); addNameTag(tag); buildWidths(cidToGid); buildCIDToGIDMap(cidToGid); buildCIDSet(cidToGid); } private void buildToUnicodeCMap(Map newGIDToOldCID) throws IOException { ToUnicodeWriter toUniWriter = new ToUnicodeWriter(); boolean hasSurrogates = false; for (int gid = 1, max = ttf.getMaximumProfile().getNumGlyphs(); gid <= max; gid++) { // optional CID2GIDMap for subsetting int cid; if (newGIDToOldCID != null) { if (!newGIDToOldCID.containsKey(gid)) { continue; } cid = newGIDToOldCID.get(gid); } else { cid = gid; } // skip composite glyph components that have no code point List codes = cmap.getCharCodes(cid); // old GID -> Unicode if (codes != null) { // use the first entry even for ambiguous mappings int codePoint = codes.get(0); if (codePoint > 0xFFFF) { hasSurrogates = true; } toUniWriter.add(cid, new String(new int[] { codePoint }, 0, 1)); } } ByteArrayOutputStream out = new ByteArrayOutputStream(); toUniWriter.writeTo(out); InputStream cMapStream = new ByteArrayInputStream(out.toByteArray()); PDStream stream = new PDStream(cMapStream, COSName.FLATE_DECODE); // surrogate code points, requires PDF 1.5 if (hasSurrogates) { document.requireMinVersion(SpecVersionUtils.V1_5); } dict.setItem(COSName.TO_UNICODE, stream); } private COSDictionary toCIDSystemInfo(String registry, String ordering, int supplement) { COSDictionary info = new COSDictionary(); info.setString(COSName.REGISTRY, registry); info.setString(COSName.ORDERING, ordering); info.setInt(COSName.SUPPLEMENT, supplement); return info; } private COSDictionary createCIDFont() throws IOException { COSDictionary cidFont = new COSDictionary(); // Type, Subtype cidFont.setItem(COSName.TYPE, COSName.FONT); cidFont.setItem(COSName.SUBTYPE, COSName.CID_FONT_TYPE2); // BaseFont cidFont.setName(COSName.BASE_FONT, fontDescriptor.getFontName()); // CIDSystemInfo COSDictionary info = toCIDSystemInfo("Adobe", "Identity", 0); cidFont.setItem(COSName.CIDSYSTEMINFO, info); // FontDescriptor cidFont.setItem(COSName.FONT_DESC, fontDescriptor.getCOSObject()); // W - widths buildWidths(cidFont); // CIDToGIDMap cidFont.setItem(COSName.CID_TO_GID_MAP, COSName.IDENTITY); return cidFont; } private void addNameTag(String tag) throws IOException { String name = fontDescriptor.getFontName(); String newName = tag + name; dict.setName(COSName.BASE_FONT, newName); fontDescriptor.setFontName(newName); cidFont.setName(COSName.BASE_FONT, newName); } private void buildCIDToGIDMap(Map cidToGid) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); int cidMax = Collections.max(cidToGid.keySet()); for (int i = 0; i <= cidMax; i++) { int gid; if (cidToGid.containsKey(i)) { gid = cidToGid.get(i); } else { gid = 0; } out.write(new byte[] { (byte) (gid >> 8 & 0xff), (byte) (gid & 0xff) }); } byte[] byteArray = out.toByteArray(); InputStream input = new ByteArrayInputStream(byteArray); PDStream stream = new PDStream(input, COSName.FLATE_DECODE); stream.getCOSObject().setInt(COSName.LENGTH1, byteArray.length); cidFont.setItem(COSName.CID_TO_GID_MAP, stream); } /** * Builds the CIDSet entry, required by PDF/A. This lists all CIDs in the font, including those that don't have a * GID. */ private void buildCIDSet(Map cidToGid) throws IOException { int cidMax = Collections.max(cidToGid.keySet()); byte[] bytes = new byte[cidMax / 8 + 1]; for (int cid = 0; cid <= cidMax; cid++) { int mask = 1 << 7 - cid % 8; bytes[cid / 8] |= mask; } InputStream input = new ByteArrayInputStream(bytes); PDStream stream = new PDStream(input, COSName.FLATE_DECODE); fontDescriptor.setCIDSet(stream); } /** * Builds wieths with a custom CIDToGIDMap (for embedding font subset). */ private void buildWidths(Map cidToGid) throws IOException { float scaling = 1000f / ttf.getHeader().getUnitsPerEm(); COSArray widths = new COSArray(); COSArray ws = new COSArray(); int prev = Integer.MIN_VALUE; // Use a sorted list to get an optimal width array Set keys = new TreeSet<>(cidToGid.keySet()); for (int cid : keys) { int gid = cidToGid.get(cid); long width = Math.round(ttf.getHorizontalMetrics().getAdvanceWidth(gid) * scaling); if (width == 1000) { // skip default width continue; } // c [w1 w2 ... wn] if (prev != cid - 1) { ws = new COSArray(); widths.add(COSInteger.get(cid)); // c widths.add(ws); } ws.add(COSInteger.get(width)); // wi prev = cid; } cidFont.setItem(COSName.W, widths); } /** * Build widths with Identity CIDToGIDMap (for embedding full font). */ private void buildWidths(COSDictionary cidFont) throws IOException { int cidMax = ttf.getNumberOfGlyphs(); int[] gidwidths = new int[cidMax * 2]; for (int cid = 0; cid < cidMax; cid++) { gidwidths[cid * 2] = cid; gidwidths[cid * 2 + 1] = ttf.getHorizontalMetrics().getAdvanceWidth(cid); } cidFont.setItem(COSName.W, getWidths(gidwidths)); } enum State { FIRST, BRACKET, SERIAL } private COSArray getWidths(int[] widths) throws IOException { if (widths.length == 0) { throw new IllegalArgumentException("length of widths must be > 0"); } float scaling = 1000f / ttf.getHeader().getUnitsPerEm(); long lastCid = widths[0]; long lastValue = Math.round(widths[1] * scaling); COSArray inner = null; COSArray outer = new COSArray(); outer.add(COSInteger.get(lastCid)); State state = State.FIRST; for (int i = 2; i < widths.length; i += 2) { long cid = widths[i]; long value = Math.round(widths[i + 1] * scaling); switch (state) { case FIRST: if (cid == lastCid + 1 && value == lastValue) { state = State.SERIAL; } else if (cid == lastCid + 1) { state = State.BRACKET; inner = new COSArray(); inner.add(COSInteger.get(lastValue)); } else { inner = new COSArray(); inner.add(COSInteger.get(lastValue)); outer.add(inner); outer.add(COSInteger.get(cid)); } break; case BRACKET: if (cid == lastCid + 1 && value == lastValue) { state = State.SERIAL; outer.add(inner); outer.add(COSInteger.get(lastCid)); } else if (cid == lastCid + 1) { inner.add(COSInteger.get(lastValue)); } else { state = State.FIRST; inner.add(COSInteger.get(lastValue)); outer.add(inner); outer.add(COSInteger.get(cid)); } break; case SERIAL: if (cid != lastCid + 1 || value != lastValue) { outer.add(COSInteger.get(lastCid)); outer.add(COSInteger.get(lastValue)); outer.add(COSInteger.get(cid)); state = State.FIRST; } break; } lastValue = value; lastCid = cid; } switch (state) { case FIRST: inner = new COSArray(); inner.add(COSInteger.get(lastValue)); outer.add(inner); break; case BRACKET: inner.add(COSInteger.get(lastValue)); outer.add(inner); break; case SERIAL: outer.add(COSInteger.get(lastCid)); outer.add(COSInteger.get(lastValue)); break; } return outer; } /** * Returns the descendant CIDFont. */ public PDCIDFont getCIDFont() throws IOException { return new PDCIDFontType2(cidFont, parent, ttf); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDCIDSystemInfo.java000066400000000000000000000041011320103431700267650ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * Represents a CIDSystemInfo. * * @author John Hewson */ public final class PDCIDSystemInfo implements COSObjectable { private final COSDictionary dictionary; PDCIDSystemInfo(String registry, String ordering, int supplement) { dictionary = new COSDictionary(); dictionary.setString(COSName.REGISTRY, registry); dictionary.setString(COSName.ORDERING, ordering); dictionary.setInt(COSName.SUPPLEMENT, supplement); } PDCIDSystemInfo(COSDictionary dictionary) { this.dictionary = dictionary; } public String getRegistry() { return dictionary.getNameAsString(COSName.REGISTRY); } public String getOrdering() { return dictionary.getNameAsString(COSName.ORDERING); } public int getSupplement() { return dictionary.getInt(COSName.SUPPLEMENT); } @Override public COSBase getCOSObject() { return dictionary; } @Override public String toString() { return getRegistry() + "-" + getOrdering() + "-" + getSupplement(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDFont.java000066400000000000000000000447601320103431700252720ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.nonNull; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.io.IOUtils; import org.apache.fontbox.afm.FontMetrics; import org.apache.fontbox.cmap.CMap; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.font.encoding.GlyphList; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is the base class for all PDF fonts. * * @author Ben Litchfield */ public abstract class PDFont implements COSObjectable, PDFontLike { private static final Logger LOG = LoggerFactory.getLogger(PDFont.class); protected static final Matrix DEFAULT_FONT_MATRIX = new Matrix(0.001f, 0, 0, 0.001f, 0, 0); protected final COSDictionary dict; private final CMap toUnicodeCMap; private final FontMetrics afmStandard14; // AFM for standard 14 fonts private PDFontDescriptor fontDescriptor; private List widths; private float avgFontWidth; private float fontWidthOfSpace = -1f; private final Map codeToWidthMap; PDFont() { dict = new COSDictionary(); dict.setItem(COSName.TYPE, COSName.FONT); toUnicodeCMap = null; fontDescriptor = null; afmStandard14 = null; codeToWidthMap = new HashMap<>(); } /** * Constructor for Standard 14. */ PDFont(String baseFont) { dict = new COSDictionary(); dict.setItem(COSName.TYPE, COSName.FONT); toUnicodeCMap = null; afmStandard14 = Standard14Fonts.getAFM(baseFont); if (afmStandard14 == null) { throw new IllegalArgumentException("No AFM for font " + baseFont); } fontDescriptor = PDType1FontEmbedder.buildFontDescriptor(afmStandard14); // standard 14 fonts may be accessed concurrently, as they are singletons codeToWidthMap = new ConcurrentHashMap<>(); } /** * Constructor. * * @param fontDictionary Font dictionary. */ protected PDFont(COSDictionary fontDictionary) throws IOException { dict = fontDictionary; codeToWidthMap = new HashMap<>(); // standard 14 fonts use an AFM afmStandard14 = Standard14Fonts.getAFM(getName()); // may be null (it usually is) // font descriptor COSDictionary fd = (COSDictionary) dict.getDictionaryObject(COSName.FONT_DESC); if (fd != null) { fontDescriptor = new PDFontDescriptor(fd); } else if (afmStandard14 != null) { // build font descriptor from the AFM fontDescriptor = PDType1FontEmbedder.buildFontDescriptor(afmStandard14); } else { fontDescriptor = null; } // ToUnicode CMap COSBase toUnicode = dict.getDictionaryObject(COSName.TO_UNICODE); if (toUnicode != null) { CMap cmap = null; try { cmap = readCMap(toUnicode); if (cmap != null && !cmap.hasUnicodeMappings()) { LOG.warn("Invalid ToUnicode CMap in font " + getName()); } } catch (IOException ex) { LOG.error("Could not read ToUnicode CMap in font " + getName(), ex); } toUnicodeCMap = cmap; } else { toUnicodeCMap = null; } } /** * Returns the AFM if this is a Standard 14 font. */ protected final FontMetrics getStandard14AFM() { return afmStandard14; } @Override public PDFontDescriptor getFontDescriptor() { return fontDescriptor; } /** * Sets the font descriptor when embedding a font. */ protected final void setFontDescriptor(PDFontDescriptor fontDescriptor) { this.fontDescriptor = fontDescriptor; } /** * * /** Reads a CMap given a COS Stream or Name. May return null if a predefined CMap does not exist. * * @param base COSName or COSStream */ protected final CMap readCMap(COSBase base) throws IOException { if (base instanceof COSName) { // predefined CMap String name = ((COSName) base).getName(); return CMapManager.getPredefinedCMap(name); } else if (base instanceof COSStream) { // embedded CMap InputStream input = null; try { input = ((COSStream) base).getUnfilteredStream(); return CMapManager.parseCMap(input); } finally { IOUtils.closeQuietly(input); } } else { throw new IOException("Expected Name or Stream"); } } @Override public COSDictionary getCOSObject() { return dict; } @Override public Vector getPositionVector(int code) { throw new UnsupportedOperationException("Horizontal fonts have no position vector"); } /** * Returns the displacement vector (w0, w1) in text space, for the given character. For horizontal text only the x * component is used, for vertical text only the y component. * * @param code character code * @return displacement vector */ public Vector getDisplacement(int code) throws IOException { return new Vector(getWidth(code) / 1000, 0); } @Override public float getWidth(int code) throws IOException { Float width = codeToWidthMap.get(code); if (width != null) { return width; } // Acrobat overrides the widths in the font program on the conforming reader's system with // the widths specified in the font dictionary." (Adobe Supplement to the ISO 32000) // // Note: The Adobe Supplement says that the override happens "If the font program is not // embedded", however PDFBOX-427 shows that it also applies to embedded fonts. // Type1, Type1C, Type3 if (dict.getDictionaryObject(COSName.WIDTHS) != null || dict.containsKey(COSName.MISSING_WIDTH)) { int firstChar = dict.getInt(COSName.FIRST_CHAR, -1); int lastChar = dict.getInt(COSName.LAST_CHAR, -1); int siz = getWidths().size(); int idx = code - firstChar; if (siz > 0 && code >= firstChar && code <= lastChar && idx < siz) { width = getWidths().get(idx); if (width == null) { width = 0f; } codeToWidthMap.put(code, width); return width; } PDFontDescriptor fd = getFontDescriptor(); if (fd != null) { // get entry from /MissingWidth entry width = fd.getMissingWidth(); codeToWidthMap.put(code, width); return width; } } // standard 14 font widths are specified by an AFM if (isStandard14()) { width = getStandard14Width(code); codeToWidthMap.put(code, width); return width; } // if there's nothing to override with, then obviously we fall back to the font width = getWidthFromFont(code); codeToWidthMap.put(code, width); return width; } /** * Returns the glyph width from the AFM if this is a Standard 14 font. * * @param code character code * @return width in 1/1000 text space */ protected abstract float getStandard14Width(int code); @Override public abstract float getWidthFromFont(int code) throws IOException; @Override public abstract boolean isEmbedded(); @Override public abstract float getHeight(int code) throws IOException; /** * Encodes the given string for use in a PDF content stream. * * @param text Any Unicode text. * @return Array of PDF content stream bytes. * @throws IOException If the text could not be encoded. */ public final byte[] encode(String text) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); for (int offset = 0; offset < text.length();) { int codePoint = text.codePointAt(offset); // multi-byte encoding with 1 to 4 bytes byte[] bytes = encode(codePoint); out.write(bytes); offset += Character.charCount(codePoint); } return out.toByteArray(); } /** * Encodes the given Unicode code point for use in a PDF content stream. Content streams use a multi-byte encoding * with 1 to 4 bytes. * *

* This method is called when embedding text in PDFs and when filling in fields. * * @param unicode Unicode code point. * @return Array of 1 to 4 PDF content stream bytes. * @throws IOException If the text could not be encoded. */ protected abstract byte[] encode(int unicode) throws IOException; /** * Returns the width of the given Unicode string. * * @param text The text to get the width of. * @return The width of the string in 1/1000 units of text space. * @throws IOException If there is an error getting the width information. */ public float getStringWidth(String text) throws IOException { byte[] bytes = encode(text); ByteArrayInputStream in = new ByteArrayInputStream(bytes); float width = 0; while (in.available() > 0) { int code = readCode(in); width += getWidth(code); } return width; } /** * This will get the average font width for all characters. * * @return The width is in 1000 unit of text space, ie 333 or 777 */ // todo: this method is highly suspicious, the average glyph width is not usually a good metric @Override public float getAverageFontWidth() { float average; if (avgFontWidth != 0.0f) { average = avgFontWidth; } else { float totalWidth = 0.0f; float characterCount = 0.0f; COSArray widths = (COSArray) dict.getDictionaryObject(COSName.WIDTHS); if (widths != null) { for (int i = 0; i < widths.size(); i++) { COSNumber fontWidth = (COSNumber) widths.getObject(i); if (fontWidth.floatValue() > 0) { totalWidth += fontWidth.floatValue(); characterCount += 1; } } } if (totalWidth > 0) { average = totalWidth / characterCount; } else { average = 0; } avgFontWidth = average; } return average; } /** * Reads a character code from a content stream string. Codes may be up to 4 bytes long. * * @param in string stream * @return character code * @throws IOException if the CMap or stream cannot be read */ public abstract int readCode(InputStream in) throws IOException; /** * Returns the Unicode character sequence which corresponds to the given character code. * * @param code character code * @param customGlyphList a custom glyph list to use instead of the Adobe Glyph List * @return Unicode character(s) */ public String toUnicode(int code, GlyphList customGlyphList) throws IOException { return toUnicode(code); } /** * Returns the Unicode character sequence which corresponds to the given character code. * * @param code character code * @return Unicode character(s) */ public String toUnicode(int code) throws IOException { // if the font dictionary containsName a ToUnicode CMap, use that CMap if (toUnicodeCMap != null) { if (toUnicodeCMap.getName() != null && toUnicodeCMap.getName().startsWith("Identity-") && dict.getDictionaryObject(COSName.TO_UNICODE) instanceof COSName) { // handle the undocumented case of using Identity-H/V as a ToUnicode CMap, this // isn't actually valid as the Identity-x CMaps are code->CID maps, not // code->Unicode maps. See sample_fonts_solidconvertor.pdf for an example. // PDFBOX-3123: do this only if the /ToUnicode entry is a name return new String(new char[] { (char) code }); } // proceed as normal return toUnicodeCMap.toUnicode(code); } // if no value has been produced, there is no way to obtain Unicode for the character. // this behaviour can be overridden is subclasses, but this method *must* return null here return null; } /** * This will always return "Font" for fonts. * * @return The type of object that this is. */ public String getType() { return dict.getNameAsString(COSName.TYPE); } /** * This will get the subtype of font. */ public String getSubType() { return dict.getNameAsString(COSName.SUBTYPE); } @Override public abstract String getName(); @Override public abstract BoundingBox getBoundingBox() throws IOException; /** * The widths of the characters. This will be null for the standard 14 fonts. * * @return The widths of the characters. */ protected final List getWidths() { if (widths == null) { COSArray array = (COSArray) dict.getDictionaryObject(COSName.WIDTHS); if (array != null) { widths = COSArrayList.convertFloatCOSArrayToList(array); } else { widths = Collections.emptyList(); } } return widths; } @Override public Matrix getFontMatrix() { return DEFAULT_FONT_MATRIX; } /** * Determines the width of the space character. * * @return the width of the space character */ public float getSpaceWidth() { if (fontWidthOfSpace == -1f) { COSBase toUnicode = dict.getDictionaryObject(COSName.TO_UNICODE); try { if (nonNull(toUnicode) && nonNull(toUnicodeCMap)) { int spaceMapping = toUnicodeCMap.getSpaceMapping(); if (spaceMapping > -1) { fontWidthOfSpace = getWidth(spaceMapping); } } else { fontWidthOfSpace = getWidth(32); } // try to get it from the font itself if (fontWidthOfSpace <= 0) { fontWidthOfSpace = getWidthFromFont(32); } // use the average font width as fall back if (fontWidthOfSpace <= 0) { fontWidthOfSpace = getAverageFontWidth(); } } catch (Exception e) { LOG.error("Can't determine the width of the space character, assuming 250", e); fontWidthOfSpace = 250f; } } return fontWidthOfSpace; } /** * Returns true if the font uses vertical writing mode. */ public abstract boolean isVertical(); /** * Returns true if this font is one of the "Standard 14" fonts and receives special handling. */ public boolean isStandard14() { // this logic is based on Acrobat's behaviour, see see PDFBOX-2372 // embedded fonts never get special treatment if (isEmbedded()) { return false; } // if the name matches, this is a Standard 14 font return Standard14Fonts.containsName(getName()); } /** * Adds the given Unicode point to the subset. * * @param codePoint Unicode code point */ public abstract void addToSubset(int codePoint); /** * Replaces this font with a subset containing only the given Unicode characters. * * @throws IOException if the subset could not be written */ public abstract void subset() throws IOException; /** * Returns true if this font will be subset when embedded. */ public abstract boolean willBeSubset(); @Override public abstract boolean isDamaged(); @Override public boolean equals(Object other) { return other instanceof PDFont && ((PDFont) other).getCOSObject() == this.getCOSObject(); } @Override public int hashCode() { return this.getCOSObject().hashCode(); } @Override public String toString() { return getClass().getSimpleName() + " " + getName(); } protected CMap getToUnicodeCMap() { return toUnicodeCMap; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDFontDescriptor.java000066400000000000000000000501301320103431700273150ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; /** * A font descriptor. * * @author Ben Litchfield */ public final class PDFontDescriptor implements COSObjectable { private static final int FLAG_FIXED_PITCH = 1; private static final int FLAG_SERIF = 2; private static final int FLAG_SYMBOLIC = 4; private static final int FLAG_SCRIPT = 8; private static final int FLAG_NON_SYMBOLIC = 32; private static final int FLAG_ITALIC = 64; private static final int FLAG_ALL_CAP = 65536; private static final int FLAG_SMALL_CAP = 131072; private static final int FLAG_FORCE_BOLD = 262144; private final COSDictionary dic; private float xHeight = Float.NEGATIVE_INFINITY; private float capHeight = Float.NEGATIVE_INFINITY; private int flags = -1; /** * Package-private constructor, for embedding. */ PDFontDescriptor() { dic = new COSDictionary(); dic.setItem(COSName.TYPE, COSName.FONT_DESC); } /** * Creates a PDFontDescriptor from a COS dictionary. * * @param desc The wrapped COS Dictionary. */ public PDFontDescriptor(COSDictionary desc) { dic = desc; } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isFixedPitch() { return isFlagBitOn(FLAG_FIXED_PITCH); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setFixedPitch(boolean flag) { setFlagBit(FLAG_FIXED_PITCH, flag); } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isSerif() { return isFlagBitOn(FLAG_SERIF); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setSerif(boolean flag) { setFlagBit(FLAG_SERIF, flag); } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isSymbolic() { return isFlagBitOn(FLAG_SYMBOLIC); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setSymbolic(boolean flag) { setFlagBit(FLAG_SYMBOLIC, flag); } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isScript() { return isFlagBitOn(FLAG_SCRIPT); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setScript(boolean flag) { setFlagBit(FLAG_SCRIPT, flag); } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isNonSymbolic() { return isFlagBitOn(FLAG_NON_SYMBOLIC); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setNonSymbolic(boolean flag) { setFlagBit(FLAG_NON_SYMBOLIC, flag); } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isItalic() { return isFlagBitOn(FLAG_ITALIC); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setItalic(boolean flag) { setFlagBit(FLAG_ITALIC, flag); } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isAllCap() { return isFlagBitOn(FLAG_ALL_CAP); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setAllCap(boolean flag) { setFlagBit(FLAG_ALL_CAP, flag); } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isSmallCap() { return isFlagBitOn(FLAG_SMALL_CAP); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setSmallCap(boolean flag) { setFlagBit(FLAG_SMALL_CAP, flag); } /** * A convenience method that checks the flag bit. * * @return The flag value. */ public boolean isForceBold() { return isFlagBitOn(FLAG_FORCE_BOLD); } /** * A convenience method that sets the flag bit. * * @param flag The flag value. */ public void setForceBold(boolean flag) { setFlagBit(FLAG_FORCE_BOLD, flag); } private boolean isFlagBitOn(int bit) { return (getFlags() & bit) != 0; } private void setFlagBit(int bit, boolean value) { int flags = getFlags(); if (value) { flags = flags | bit; } else { flags = flags & (~bit); } setFlags(flags); } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return dic; } /** * Get the font name. * * @return The name of the font. */ public String getFontName() { COSBase base = dic.getDictionaryObject(COSName.FONT_NAME); if (base instanceof COSName) { return ((COSName) base).getName(); } return null; } /** * This will set the font name. * * @param fontName The new name for the font. */ public void setFontName(String fontName) { COSName name = null; if (fontName != null) { name = COSName.getPDFName(fontName); } dic.setItem(COSName.FONT_NAME, name); } /** * A string representing the preferred font family. * * @return The font family. */ public String getFontFamily() { String retval = null; COSString name = (COSString) dic.getDictionaryObject(COSName.FONT_FAMILY); if (name != null) { retval = name.getString(); } return retval; } /** * This will set the font family. * * @param fontFamily The font family. */ public void setFontFamily(String fontFamily) { COSString name = null; if (fontFamily != null) { name = COSString.parseLiteral(fontFamily); } dic.setItem(COSName.FONT_FAMILY, name); } /** * The weight of the font. According to the PDF spec "possible values are 100, 200, 300, 400, 500, 600, 700, 800 or * 900" Where a higher number is more weight and appears to be more bold. * * @return The font weight. */ public float getFontWeight() { return dic.getFloat(COSName.FONT_WEIGHT, 0); } /** * Set the weight of the font. * * @param fontWeight The new weight of the font. */ public void setFontWeight(float fontWeight) { dic.setFloat(COSName.FONT_WEIGHT, fontWeight); } /** * A string representing the preferred font stretch. According to the PDF Spec: The font stretch value; it must be * one of the following (ordered from narrowest to widest): UltraCondensed, ExtraCondensed, Condensed, * SemiCondensed, Normal, SemiExpanded, Expanded, ExtraExpanded or UltraExpanded. * * @return The stretch of the font. */ public String getFontStretch() { String retval = null; COSName name = (COSName) dic.getDictionaryObject(COSName.FONT_STRETCH); if (name != null) { retval = name.getName(); } return retval; } /** * This will set the font stretch. * * @param fontStretch The new stretch for the font. */ public void setFontStretch(String fontStretch) { COSName name = null; if (fontStretch != null) { name = COSName.getPDFName(fontStretch); } dic.setItem(COSName.FONT_STRETCH, name); } /** * This will get the font flags. * * @return The font flags. */ public int getFlags() { if (flags == -1) { flags = dic.getInt(COSName.FLAGS, 0); } return flags; } /** * This will set the font flags. * * @param flags The new font flags. */ public void setFlags(int flags) { dic.setInt(COSName.FLAGS, flags); this.flags = flags; } /** * This will get the fonts bounding box. * * @return The fonts bounding box. */ public PDRectangle getFontBoundingBox() { COSArray rect = (COSArray) dic.getDictionaryObject(COSName.FONT_BBOX); PDRectangle retval = null; if (rect != null) { retval = new PDRectangle(rect); } return retval; } /** * Set the fonts bounding box. * * @param rect The new bouding box. */ public void setFontBoundingBox(PDRectangle rect) { COSArray array = null; if (rect != null) { array = rect.getCOSObject(); } dic.setItem(COSName.FONT_BBOX, array); } /** * This will get the italic angle for the font. * * @return The italic angle. */ public float getItalicAngle() { return dic.getFloat(COSName.ITALIC_ANGLE, 0); } /** * This will set the italic angle for the font. * * @param angle The new italic angle for the font. */ public void setItalicAngle(float angle) { dic.setFloat(COSName.ITALIC_ANGLE, angle); } /** * This will get the ascent for the font. * * @return The ascent. */ public float getAscent() { return dic.getFloat(COSName.ASCENT, 0); } /** * This will set the ascent for the font. * * @param ascent The new ascent for the font. */ public void setAscent(float ascent) { dic.setFloat(COSName.ASCENT, ascent); } /** * This will get the descent for the font. * * @return The descent. */ public float getDescent() { return dic.getFloat(COSName.DESCENT, 0); } /** * This will set the descent for the font. * * @param descent The new descent for the font. */ public void setDescent(float descent) { dic.setFloat(COSName.DESCENT, descent); } /** * This will get the leading for the font. * * @return The leading. */ public float getLeading() { return dic.getFloat(COSName.LEADING, 0); } /** * This will set the leading for the font. * * @param leading The new leading for the font. */ public void setLeading(float leading) { dic.setFloat(COSName.LEADING, leading); } /** * This will get the CapHeight for the font. * * @return The cap height. */ public float getCapHeight() { if (capHeight == Float.NEGATIVE_INFINITY) { /* * We observed a negative value being returned with the Scheherazade font. PDFBOX-429 was logged for this. * We are not sure if returning the absolute value is the correct fix, but it seems to work. */ capHeight = java.lang.Math.abs(dic.getFloat(COSName.CAP_HEIGHT, 0)); } return capHeight; } /** * This will set the cap height for the font. * * @param capHeight The new cap height for the font. */ public void setCapHeight(float capHeight) { dic.setFloat(COSName.CAP_HEIGHT, capHeight); this.capHeight = capHeight; } /** * This will get the x height for the font. * * @return The x height. */ public float getXHeight() { if (xHeight == Float.NEGATIVE_INFINITY) { /* * We observed a negative value being returned with the Scheherazade font. PDFBOX-429 was logged for this. * We are not sure if returning the absolute value is the correct fix, but it seems to work. */ xHeight = java.lang.Math.abs(dic.getFloat(COSName.XHEIGHT, 0)); } return xHeight; } /** * This will set the x height for the font. * * @param xHeight The new x height for the font. */ public void setXHeight(float xHeight) { dic.setFloat(COSName.XHEIGHT, xHeight); this.xHeight = xHeight; } /** * This will get the stemV for the font. * * @return The stem v value. */ public float getStemV() { return dic.getFloat(COSName.STEM_V, 0); } /** * This will set the stem V for the font. * * @param stemV The new stem v for the font. */ public void setStemV(float stemV) { dic.setFloat(COSName.STEM_V, stemV); } /** * This will get the stemH for the font. * * @return The stem h value. */ public float getStemH() { return dic.getFloat(COSName.STEM_H, 0); } /** * This will set the stem H for the font. * * @param stemH The new stem h for the font. */ public void setStemH(float stemH) { dic.setFloat(COSName.STEM_H, stemH); } /** * This will get the average width for the font. * * @return The average width value. */ public float getAverageWidth() { return dic.getFloat(COSName.AVG_WIDTH, 0); } /** * This will set the average width for the font. * * @param averageWidth The new average width for the font. */ public void setAverageWidth(float averageWidth) { dic.setFloat(COSName.AVG_WIDTH, averageWidth); } /** * This will get the max width for the font. * * @return The max width value. */ public float getMaxWidth() { return dic.getFloat(COSName.MAX_WIDTH, 0); } /** * This will set the max width for the font. * * @param maxWidth The new max width for the font. */ public void setMaxWidth(float maxWidth) { dic.setFloat(COSName.MAX_WIDTH, maxWidth); } /** * Returns true if widths are present in the font descriptor. */ public boolean hasWidths() { return dic.containsKey(COSName.WIDTHS) || dic.containsKey(COSName.MISSING_WIDTH); } /** * Returns true if the missing widths entry is present in the font descriptor. */ public boolean hasMissingWidth() { return dic.containsKey(COSName.MISSING_WIDTH); } /** * This will get the missing width for the font from the /MissingWidth dictionary entry. * * @return The missing width value, or 0 if there is no such dictionary entry. */ public float getMissingWidth() { return dic.getFloat(COSName.MISSING_WIDTH, 0); } /** * This will set the missing width for the font. * * @param missingWidth The new missing width for the font. */ public void setMissingWidth(float missingWidth) { dic.setFloat(COSName.MISSING_WIDTH, missingWidth); } /** * This will get the character set for the font. * * @return The character set value. */ public String getCharSet() { String retval = null; COSString name = (COSString) dic.getDictionaryObject(COSName.CHAR_SET); if (name != null) { retval = name.getString(); } return retval; } /** * This will set the character set for the font. * * @param charSet The new character set for the font. */ public void setCharacterSet(String charSet) { COSString name = null; if (charSet != null) { name = COSString.parseLiteral(charSet); } dic.setItem(COSName.CHAR_SET, name); } /** * A stream containing a Type 1 font program. * * @return A stream containing a Type 1 font program. */ public PDStream getFontFile() { PDStream retval = null; COSBase obj = dic.getDictionaryObject(COSName.FONT_FILE); if (obj instanceof COSStream) { retval = new PDStream((COSStream) obj); } return retval; } /** * Set the type 1 font program. * * @param type1Stream The type 1 stream. */ public void setFontFile(PDStream type1Stream) { dic.setItem(COSName.FONT_FILE, type1Stream); } /** * A stream containing a true type font program. * * @return A stream containing a true type font program. */ public PDStream getFontFile2() { PDStream retval = null; COSBase obj = dic.getDictionaryObject(COSName.FONT_FILE2); if (obj instanceof COSStream) { retval = new PDStream((COSStream) obj); } return retval; } /** * Set the true type font program. * * @param ttfStream The true type stream. */ public void setFontFile2(PDStream ttfStream) { dic.setItem(COSName.FONT_FILE2, ttfStream); } /** * A stream containing a font program that is not true type or type 1. * * @return A stream containing a font program. */ public PDStream getFontFile3() { PDStream retval = null; COSBase obj = dic.getDictionaryObject(COSName.FONT_FILE3); if (obj instanceof COSStream) { retval = new PDStream((COSStream) obj); } return retval; } /** * Set a stream containing a font program that is not true type or type 1. * * @param stream The font program stream. */ public void setFontFile3(PDStream stream) { dic.setItem(COSName.FONT_FILE3, stream); } /** * Get the CIDSet stream. * * @return A stream containing a CIDSet. */ public PDStream getCIDSet() { COSObjectable cidSet = dic.getDictionaryObject(COSName.CID_SET); if (cidSet instanceof COSStream) { return new PDStream((COSStream) cidSet); } return null; } /** * Set a stream containing a CIDSet. * * @param stream The font program stream. */ public void setCIDSet(PDStream stream) { dic.setItem(COSName.CID_SET, stream); } /** * Returns the Panose entry of the Style dictionary, if any. * * @return A Panose wrapper object. */ public PDPanose getPanose() { COSDictionary style = (COSDictionary) dic.getDictionaryObject(COSName.STYLE); if (style != null) { COSString panose = (COSString) style.getDictionaryObject(COSName.PANOSE); byte[] bytes = panose.getBytes(); return new PDPanose(bytes); } return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDFontFactory.java000066400000000000000000000122411320103431700266070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Creates the appropriate font subtype based on information in the dictionary. * @author Ben Litchfield */ public final class PDFontFactory { private static final Logger LOG = LoggerFactory.getLogger(PDFontFactory.class); private PDFontFactory() { } /** * Creates a new PDFont instance with the appropriate subclass. * * @param dictionary a font dictionary * @return a PDFont instance, based on the SubType entry of the dictionary * @throws IOException if something goes wrong */ public static PDFont createFont(COSDictionary dictionary) throws IOException { COSName type = dictionary.getCOSName(COSName.TYPE, COSName.FONT); if (!COSName.FONT.equals(type)) { LOG.error("Expected 'Font' dictionary but found '" + type.getName() + "'"); } COSName subType = dictionary.getCOSName(COSName.SUBTYPE); if (COSName.TYPE1.equals(subType)) { COSBase fd = dictionary.getDictionaryObject(COSName.FONT_DESC); if (fd instanceof COSDictionary && ((COSDictionary) fd).containsKey(COSName.FONT_FILE3)) { return new PDType1CFont(dictionary); } return new PDType1Font(dictionary); } else if (COSName.MM_TYPE1.equals(subType)) { COSBase fd = dictionary.getDictionaryObject(COSName.FONT_DESC); if (fd instanceof COSDictionary && ((COSDictionary) fd).containsKey(COSName.FONT_FILE3)) { return new PDType1CFont(dictionary); } return new PDMMType1Font(dictionary); } else if (COSName.TRUE_TYPE.equals(subType)) { return new PDTrueTypeFont(dictionary); } else if (COSName.TYPE3.equals(subType)) { return new PDType3Font(dictionary); } else if (COSName.TYPE0.equals(subType)) { return new PDType0Font(dictionary); } else if (COSName.CID_FONT_TYPE0.equals(subType)) { throw new IllegalArgumentException("Type 0 descendant font not allowed"); } else if (COSName.CID_FONT_TYPE2.equals(subType)) { throw new IllegalArgumentException("Type 2 descendant font not allowed"); } else { // assuming Type 1 font (see PDFBOX-1988) because it seems that Adobe Reader does this // however, we may need more sophisticated logic perhaps looking at the FontFile LOG.warn("Invalid font subtype '" + subType + "'"); return new PDType1Font(dictionary); } } /** * Creates a new PDCIDFont instance with the appropriate subclass. * * @param dictionary descendant font dictionary * @return a PDCIDFont instance, based on the SubType entry of the dictionary * @throws IOException if something goes wrong */ static PDCIDFont createDescendantFont(COSDictionary dictionary, PDType0Font parent) throws IOException { COSName type = dictionary.getCOSName(COSName.TYPE, COSName.FONT); if (!COSName.FONT.equals(type)) { throw new IllegalArgumentException("Expected 'Font' dictionary but found '" + type.getName() + "'"); } COSName subType = dictionary.getCOSName(COSName.SUBTYPE); if (COSName.CID_FONT_TYPE0.equals(subType)) { return new PDCIDFontType0(dictionary, parent); } else if (COSName.CID_FONT_TYPE2.equals(subType)) { return new PDCIDFontType2(dictionary, parent); } else { throw new IOException("Invalid font type: " + type); } } /** * Create a default font. * * @return a default font * @throws IOException if something goes wrong */ public static PDFont createDefaultFont() throws IOException { COSDictionary dict = new COSDictionary(); dict.setItem(COSName.TYPE, COSName.FONT); dict.setItem(COSName.SUBTYPE, COSName.TRUE_TYPE); dict.setString(COSName.BASE_FONT, "Arial"); return createFont(dict); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDFontLike.java000066400000000000000000000076431320103431700260760ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.Vector; /** * A font-like object. * * @author John Hewson */ public interface PDFontLike { /** * Returns the name of this font, either the PostScript "BaseName" or the Type 3 "Name". */ String getName(); /** * Returns the font descriptor, may be null. */ PDFontDescriptor getFontDescriptor(); /** * Returns the font matrix, which represents the transformation from glyph space to text space. */ Matrix getFontMatrix(); /** * Returns the font's bounding box. */ BoundingBox getBoundingBox() throws IOException; /** * Returns the position vector (v), in text space, for the given character. This represents the position of vertical * origin relative to horizontal origin, for horizontal writing it will always be (0, 0). For vertical writing both * x and y are set. * * @param code character code * @return position vector */ Vector getPositionVector(int code); /** * Returns the height of the given character, in glyph space. This can be expensive to calculate. Results are only * approximate. *

* * Warning: This method is deprecated in PDFBox 2.0 because there is no meaningful value which it can return. The * {@link #getWidth} method returns the advance width of a glyph, but there is no corresponding advance height. The * logical height of a character is the same for every character in a font, so if you want that, retrieve the font * bbox's height. Otherwise if you want the visual bounds of the glyph then call getPath(..) on the appropriate * PDFont subclass to retrieve the glyph outline as a GeneralPath. * * @param code character code * @deprecated Use {@link #getBoundingBox().getHeight()} instead. */ @Deprecated float getHeight(int code) throws IOException; /** * Returns the advance width of the given character, in glyph space. *

* * If you want the visual bounds of the glyph then call getPath(..) on the appropriate PDFont subclass to retrieve * the glyph outline as a GeneralPath instead. * * @param code character code */ float getWidth(int code) throws IOException; /** * Returns the width of a glyph in the embedded font file. * * @param code character code * @return width in glyph space * @throws IOException if the font could not be read */ float getWidthFromFont(int code) throws IOException; /** * Returns true if the font file is embedded in the PDF. */ boolean isEmbedded(); /** * Returns true if the embedded font file is damaged. */ boolean isDamaged(); /** * This will get the average font width for all characters. * * @return The width is in 1000 unit of text space, ie 333 or 777 */ // todo: this method is highly suspicious, the average glyph width is not usually a good metric float getAverageFontWidth(); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDMMType1Font.java000066400000000000000000000024041320103431700264340ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import org.sejda.sambox.cos.COSDictionary; /** * Type 1 Multiple Master Font. * * @author Ben Litchfield */ public class PDMMType1Font extends PDType1Font { /** * Creates an MMType1Font from a Font dictionary in a PDF. * * @param fontDictionary font dictionary */ public PDMMType1Font(COSDictionary fontDictionary) throws IOException { super(fontDictionary); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDPanose.java000066400000000000000000000036461320103431700256070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.util.Arrays; /** * Represents the "Panose" entry of a FontDescriptor's Style dictionary. This is a sequence of 12 * bytes which contain both the TTF sFamilyClass and PANOSE classification bytes. * * @author John Hewson */ public class PDPanose { private final byte[] bytes; public PDPanose(byte[] bytes) { this.bytes = bytes; } /** * The font family class and subclass ID bytes, given in the sFamilyClass field of the * “OS/2” table in a TrueType font. * * @see http://www.microsoft.com/typography/otspec/ibmfc.htm */ public int getFamilyClass() { return bytes[0] << 8 | bytes[1]; } /** * Ten bytes for the PANOSE classification number for the font. * * @see http://www.monotype.com/services/pan1 */ public PDPanoseClassification getPanose() { byte[] panose = Arrays.copyOfRange(bytes, 2, 12); return new PDPanoseClassification(panose); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDPanoseClassification.java000066400000000000000000000045621320103431700304610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; /** * Represents a 10-byte PANOSE classification. * * @link http://www.monotype.com/services/pan2 * @author John Hewson */ public class PDPanoseClassification { private final byte[] bytes; PDPanoseClassification(byte[] bytes) { this.bytes = bytes; } public int getFamilyKind() { return bytes[0]; } public int getSerifStyle() { return bytes[1]; } public int getWeight() { return bytes[2]; } public int getProportion() { return bytes[3]; } public int getContrast() { return bytes[4]; } public int getStrokeVariation() { return bytes[5]; } public int getArmStyle() { return bytes[6]; } public int getLetterform() { return bytes[7]; } public int getMidline() { return bytes[8]; } public int getXHeight() { return bytes[9]; } public byte[] getBytes() { return bytes; } @Override public String toString() { return "{ FamilyType = " + getFamilyKind() + ", " + "SerifStyle = " + getSerifStyle() + ", " + "Weight = " + getWeight() + ", " + "Proportion = " + getProportion() + ", " + "Contrast = " + getContrast() + ", " + "StrokeVariation = " + getStrokeVariation() + ", " + "ArmStyle = " + getArmStyle() + ", " + "Letterform = " + getLetterform() + ", " + "Midline = " + getMidline() + ", " + "XHeight = " + getXHeight() + "}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDSimpleFont.java000066400000000000000000000331061320103431700264340ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.isNull; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.fontbox.FontBoxFont; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.font.encoding.DictionaryEncoding; import org.sejda.sambox.pdmodel.font.encoding.Encoding; import org.sejda.sambox.pdmodel.font.encoding.GlyphList; import org.sejda.sambox.pdmodel.font.encoding.MacRomanEncoding; import org.sejda.sambox.pdmodel.font.encoding.StandardEncoding; import org.sejda.sambox.pdmodel.font.encoding.WinAnsiEncoding; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A simple font. Simple fonts use a PostScript encoding vector. * * @author John Hewson */ public abstract class PDSimpleFont extends PDFont { private static final Logger LOG = LoggerFactory.getLogger(PDSimpleFont.class); protected Encoding encoding; protected GlyphList glyphList; private Boolean isSymbolic; private final Set noUnicode = new HashSet<>(); // for logging /** * Constructor for embedding. */ PDSimpleFont() { super(); } /** * Constructor for Standard 14. */ PDSimpleFont(String baseFont) { super(baseFont); // assign the glyph list based on the font if ("ZapfDingbats".equals(baseFont)) { glyphList = GlyphList.getZapfDingbats(); } else { glyphList = GlyphList.getAdobeGlyphList(); } } /** * Constructor. * * @param fontDictionary Font dictionary. */ PDSimpleFont(COSDictionary fontDictionary) throws IOException { super(fontDictionary); } /** * Reads the Encoding from the Font dictionary or the embedded or substituted font file. Must be called at the end * of any subclass constructors. * * @throws IOException if the font file could not be read */ protected void readEncoding() throws IOException { COSBase encoding = dict.getDictionaryObject(COSName.ENCODING); if (encoding != null) { if (encoding instanceof COSName) { COSName encodingName = (COSName) encoding; this.encoding = Encoding.getInstance(encodingName); if (this.encoding == null) { LOG.warn("Unknown encoding: " + encodingName.getName()); this.encoding = readEncodingFromFont(); // fallback } } else if (encoding instanceof COSDictionary) { COSDictionary encodingDict = (COSDictionary) encoding; Encoding builtIn = null; Boolean symbolic = getSymbolicFlag(); boolean isFlaggedAsSymbolic = symbolic != null && symbolic; COSName baseEncoding = encodingDict.getCOSName(COSName.BASE_ENCODING); boolean hasValidBaseEncoding = baseEncoding != null && Encoding.getInstance(baseEncoding) != null; if (!hasValidBaseEncoding && isFlaggedAsSymbolic) { builtIn = readEncodingFromFont(); } if (symbolic == null) { symbolic = false; } this.encoding = new DictionaryEncoding(encodingDict, !symbolic, builtIn); } } else { this.encoding = readEncodingFromFont(); } // normalise the standard 14 name, e.g "Symbol,Italic" -> "Symbol" String standard14Name = Standard14Fonts.getMappedFontName(getName()); // assign the glyph list based on the font if ("ZapfDingbats".equals(standard14Name)) { glyphList = GlyphList.getZapfDingbats(); } else { // StandardEncoding and Symbol are in the AGL glyphList = GlyphList.getAdobeGlyphList(); } } /** * Called by readEncoding() if the encoding needs to be extracted from the font file. * * @throws IOException if the font file could not be read. */ protected abstract Encoding readEncodingFromFont() throws IOException; /** * Returns the Encoding vector. */ public Encoding getEncoding() { return encoding; } /** * Returns the Encoding vector. */ public GlyphList getGlyphList() { return glyphList; } /** * Returns true the font is a symbolic (that is, it does not use the Adobe Standard Roman character set). */ public final boolean isSymbolic() { if (isSymbolic == null) { Boolean result = isFontSymbolic(); if (result != null) { isSymbolic = result; } else { // unless we can prove that the font is symbolic, we assume that it is not isSymbolic = true; } } return isSymbolic; } /** * Internal implementation of isSymbolic, allowing for the fact that the result may be indeterminate. */ protected Boolean isFontSymbolic() { Boolean result = getSymbolicFlag(); if (result != null) { return result; } else if (isStandard14()) { String mappedName = Standard14Fonts.getMappedFontName(getName()); return mappedName.equals("Symbol") || mappedName.equals("ZapfDingbats"); } else { if (encoding == null) { // sanity check, should never happen if (!(this instanceof PDTrueTypeFont)) { throw new IllegalStateException("Encoding should not be null!"); } // TTF without its non-symbolic flag set must be symbolic return true; } else if (encoding instanceof WinAnsiEncoding || encoding instanceof MacRomanEncoding || encoding instanceof StandardEncoding) { return false; } else if (encoding instanceof DictionaryEncoding) { // each name in Differences array must also be in the latin character set for (String name : ((DictionaryEncoding) encoding).getDifferences().values()) { if (".notdef".equals(name)) { // skip } else if (!(WinAnsiEncoding.INSTANCE.contains(name) && MacRomanEncoding.INSTANCE.contains(name) && StandardEncoding.INSTANCE.contains(name))) { return true; } } return false; } else { // we don't know return null; } } } /** * Returns the value of the symbolic flag, allowing for the fact that the result may be indeterminate. */ protected final Boolean getSymbolicFlag() { if (getFontDescriptor() != null) { // fixme: isSymbolic() defaults to false if the flag is missing so we can't trust this return getFontDescriptor().isSymbolic(); } return null; } @Override public String toUnicode(int code) throws IOException { return toUnicode(code, GlyphList.getAdobeGlyphList()); } @Override public String toUnicode(int code, GlyphList customGlyphList) throws IOException { // allow the glyph list to be overridden for the purpose of extracting Unicode // we only do this when the font's glyph list is the AGL, to avoid breaking Zapf Dingbats GlyphList unicodeGlyphList; if (this.glyphList == GlyphList.getAdobeGlyphList()) { unicodeGlyphList = customGlyphList; } else { unicodeGlyphList = this.glyphList; } // first try to use a ToUnicode CMap String unicode = super.toUnicode(code); if (unicode != null) { return unicode; } // if the font is a "simple font" and uses MacRoman/MacExpert/WinAnsi[Encoding] // or has Differences with names from only Adobe Standard and/or Symbol, then: // // a) Map the character codes to names // b) Look up the name in the Adobe Glyph List to obtain the Unicode value String name = null; if (encoding != null) { name = encoding.getName(code); unicode = unicodeGlyphList.toUnicode(name); if (unicode != null) { return unicode; } } // if no value has been produced, there is no way to obtain Unicode for the character. if (LOG.isWarnEnabled() && !noUnicode.contains(code)) { // we keep track of which warnings have been issued, so we don't log multiple times noUnicode.add(code); if (name != null) { LOG.warn("No Unicode mapping for " + name + " (" + code + ") in font " + getName()); } else { LOG.warn("No Unicode mapping for character code " + code + " in font " + getName()); } } return null; } @Override public boolean isVertical() { return false; } @Override protected final float getStandard14Width(int code) { if (getStandard14AFM() != null) { String nameInAFM = getEncoding().getName(code); // the Adobe AFMs don't include .notdef, but Acrobat uses 250, test with PDFBOX-2334 if (".notdef".equals(nameInAFM)) { return 250f; } return getStandard14AFM().getCharacterWidth(nameInAFM); } throw new IllegalStateException("No AFM"); } @Override public boolean isStandard14() { // this logic is based on Acrobat's behaviour, see see PDFBOX-2372 // the Encoding entry cannot have Differences if we want "standard 14" font handling if (getEncoding() instanceof DictionaryEncoding) { DictionaryEncoding dictionary = (DictionaryEncoding) getEncoding(); if (dictionary.getDifferences().size() > 0) { // we also require that the differences are actually different, see PDFBOX-1900 with // the file from PDFBOX-2192 on Windows Encoding baseEncoding = dictionary.getBaseEncoding(); if (isNull(baseEncoding)) { return false; } for (Map.Entry entry : dictionary.getDifferences().entrySet()) { if (!entry.getValue().equals(baseEncoding.getName(entry.getKey()))) { return false; } } } } return super.isStandard14(); } /** * Returns the path for the character with the given name. For some fonts, GIDs may be used instead of names when * calling this method. * * @return glyph path * @throws IOException if the path could not be read */ public abstract GeneralPath getPath(String name) throws IOException; /** * Returns true if the font contains the character with the given name. * * @throws IOException if the path could not be read */ public abstract boolean hasGlyph(String name) throws IOException; /** * Returns the embedded or system font used for rendering. This is never null. */ public abstract FontBoxFont getFontBoxFont(); @Override public void addToSubset(int codePoint) { throw new UnsupportedOperationException(); } @Override public void subset() throws IOException { // only TTF subsetting via PDType0Font is currently supported throw new UnsupportedOperationException(); } @Override public boolean willBeSubset() { return false; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDTrueTypeFont.java000066400000000000000000000472731320103431700267760ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.nonNull; import static org.sejda.sambox.pdmodel.font.UniUtil.getUniNameOfCodePoint; import java.awt.geom.GeneralPath; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.ttf.CmapSubtable; import org.apache.fontbox.ttf.CmapTable; import org.apache.fontbox.ttf.GlyphData; import org.apache.fontbox.ttf.PostScriptTable; import org.apache.fontbox.ttf.TTFParser; import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.font.encoding.BuiltInEncoding; import org.sejda.sambox.pdmodel.font.encoding.Encoding; import org.sejda.sambox.pdmodel.font.encoding.GlyphList; import org.sejda.sambox.pdmodel.font.encoding.MacOSRomanEncoding; import org.sejda.sambox.pdmodel.font.encoding.StandardEncoding; import org.sejda.sambox.pdmodel.font.encoding.Type1Encoding; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * TrueType font. * * @author Ben Litchfield */ public class PDTrueTypeFont extends PDSimpleFont implements PDVectorFont { private static final Logger LOG = LoggerFactory.getLogger(PDTrueTypeFont.class); private static final int START_RANGE_F000 = 0xF000; private static final int START_RANGE_F100 = 0xF100; private static final int START_RANGE_F200 = 0xF200; private static final Map INVERTED_MACOS_ROMAN = new HashMap<>(250); static { Map codeToName = MacOSRomanEncoding.INSTANCE.getCodeToNameMap(); for (Map.Entry entry : codeToName.entrySet()) { if (!INVERTED_MACOS_ROMAN.containsKey(entry.getValue())) { INVERTED_MACOS_ROMAN.put(entry.getValue(), entry.getKey()); } } } /** * Loads a TTF to be embedded into a document as a simple font. * *

* Note: Simple fonts only support 256 characters. For Unicode support, use * {@link PDType0Font#load(PDDocument, File)} instead. *

* * @param file A TTF file. * @param encoding The PostScript encoding vector to be used for embedding. * @return a PDTrueTypeFont instance. * @throws IOException If there is an error loading the data. */ public static PDTrueTypeFont load(File file, Encoding encoding) throws IOException { return new PDTrueTypeFont(new TTFParser().parse(file), encoding, true); } /** * Loads a TTF to be embedded into a document as a simple font. * *

* Note: Simple fonts only support 256 characters. For Unicode support, use * {@link PDType0Font#load(PDDocument, InputStream)} instead. *

* * @param input A TTF file stream * @param encoding The PostScript encoding vector to be used for embedding. * @return a PDTrueTypeFont instance. * @throws IOException If there is an error loading the data. */ public static PDTrueTypeFont load(InputStream input, Encoding encoding) throws IOException { return new PDTrueTypeFont(new TTFParser().parse(input), encoding, true); } /** * Loads a TTF to be embedded into a document as a simple font. * *

* Note: Simple fonts only support 256 characters. For Unicode support, use * {@link PDType0Font#load(InputStream)} instead. *

* * @param ttf A true type font * @param encoding The PostScript encoding vector to be used for embedding. * @return a PDTrueTypeFont instance. * @throws IOException If there is an error loading the data. */ public static PDTrueTypeFont load(PDDocument doc, TrueTypeFont ttf, Encoding encoding) throws IOException { return new PDTrueTypeFont(ttf, encoding, false); } private CmapSubtable cmapWinUnicode = null; private CmapSubtable cmapWinSymbol = null; private CmapSubtable cmapMacRoman = null; private boolean cmapInitialized = false; private Map gidToCode; // for embedding private final TrueTypeFont ttf; private final boolean isEmbedded; private final boolean isDamaged; private BoundingBox fontBBox; /** * Creates a new TrueType font from a Font dictionary. * * @param fontDictionary The font dictionary according to the PDF specification. */ public PDTrueTypeFont(COSDictionary fontDictionary) throws IOException { super(fontDictionary); TrueTypeFont ttfFont = null; boolean fontIsDamaged = false; if (getFontDescriptor() != null) { PDFontDescriptor fd = super.getFontDescriptor(); PDStream ff2Stream = fd.getFontFile2(); if (ff2Stream != null) { try { // embedded TTFParser ttfParser = new TTFParser(true); ttfFont = ttfParser.parse(ff2Stream.createInputStream()); } catch (NullPointerException e) // TTF parser is buggy { LOG.warn("Could not read embedded TTF for font " + getBaseFont(), e); fontIsDamaged = true; } catch (IOException e) { LOG.warn("Could not read embedded TTF for font " + getBaseFont(), e); fontIsDamaged = true; } } } isEmbedded = ttfFont != null; isDamaged = fontIsDamaged; // substitute if (ttfFont == null) { FontMapping mapping = FontMappers.instance() .getTrueTypeFont(getBaseFont(), getFontDescriptor()); ttfFont = mapping.getFont(); if (mapping.isFallback()) { LOG.warn("Using fallback font '" + ttfFont + "' for '" + getBaseFont() + "'"); } } ttf = ttfFont; readEncoding(); } /** * Returns the PostScript name of the font. */ public final String getBaseFont() { return dict.getNameAsString(COSName.BASE_FONT); } @Override protected Encoding readEncodingFromFont() throws IOException { if (!isEmbedded() && getStandard14AFM() != null) { // read from AFM return new Type1Encoding(getStandard14AFM()); } // non-symbolic fonts don't have a built-in encoding per se, but there encoding is // assumed to be StandardEncoding by the PDF spec unless an explicit Encoding is present // which will override this anyway if (getSymbolicFlag() != null && !getSymbolicFlag()) { return StandardEncoding.INSTANCE; } // normalise the standard 14 name, e.g "Symbol,Italic" -> "Symbol" String standard14Name = Standard14Fonts.getMappedFontName(getName()); // likewise, if the font is standard 14 then we know it's Standard Encoding if (isStandard14() && !standard14Name.equals("Symbol") && !standard14Name.equals("ZapfDingbats")) { return StandardEncoding.INSTANCE; } // synthesize an encoding, so that getEncoding() is always usable PostScriptTable post = ttf.getPostScript(); Map codeToName = new HashMap(); for (int code = 0; code <= 256; code++) { int gid = codeToGID(code); if (gid > 0) { String name = null; if (post != null) { name = post.getName(gid); } if (name == null) { // GID pseudo-name name = Integer.toString(gid); } codeToName.put(code, name); } } return new BuiltInEncoding(codeToName); } /** * Creates a new TrueType font for embedding. */ private PDTrueTypeFont(TrueTypeFont ttf, Encoding encoding, boolean closeTTF) throws IOException { PDTrueTypeFontEmbedder embedder = new PDTrueTypeFontEmbedder(dict, ttf, encoding); this.encoding = encoding; this.ttf = ttf; setFontDescriptor(embedder.getFontDescriptor()); isEmbedded = true; isDamaged = false; glyphList = GlyphList.getAdobeGlyphList(); if (closeTTF) { // the TTF is fully loaded and it is save to close the underlying data source ttf.close(); } } @Override public int readCode(InputStream in) throws IOException { return in.read(); } @Override public String getName() { return getBaseFont(); } @Override public BoundingBox getBoundingBox() throws IOException { if (fontBBox == null) { fontBBox = generateBoundingBox(); } return fontBBox; } private BoundingBox generateBoundingBox() throws IOException { if (getFontDescriptor() != null) { PDRectangle bbox = getFontDescriptor().getFontBoundingBox(); if (nonNull(bbox)) { return new BoundingBox(bbox.getLowerLeftX(), bbox.getLowerLeftY(), bbox.getUpperRightX(), bbox.getUpperRightY()); } } return ttf.getFontBBox(); } @Override public boolean isDamaged() { return isDamaged; } /** * Returns the embedded or substituted TrueType font. */ public TrueTypeFont getTrueTypeFont() { return ttf; } @Override public float getWidthFromFont(int code) throws IOException { int gid = codeToGID(code); float width = ttf.getAdvanceWidth(gid); float unitsPerEM = ttf.getUnitsPerEm(); if (unitsPerEM != 1000) { width *= 1000f / unitsPerEM; } return width; } @Override public float getHeight(int code) throws IOException { int gid = codeToGID(code); GlyphData glyph = ttf.getGlyph().getGlyph(gid); if (glyph != null) { return glyph.getBoundingBox().getHeight(); } return 0; } @Override protected byte[] encode(int unicode) throws IOException { if (nonNull(encoding)) { if (!encoding.contains(getGlyphList().codePointToName(unicode))) { throw new IllegalArgumentException( String.format("U+%04X is not available in this font's encoding: %s", unicode, encoding.getEncodingName())); } String name = getGlyphList().codePointToName(unicode); Map inverted = encoding.getNameToCodeMap(); if (!ttf.hasGlyph(name)) { // try unicode name String uniName = getUniNameOfCodePoint(unicode); if (!ttf.hasGlyph(uniName)) { throw new IllegalArgumentException( String.format("No glyph for U+%04X in font %s", unicode, getName())); } } Integer code = inverted.get(name); if (code == null) { throw new IllegalArgumentException( String.format("No glyph for U+%04X in font %s", unicode, getName())); } return new byte[] { (byte) code.intValue() }; } // use TTF font's built-in encoding String name = getGlyphList().codePointToName(unicode); if (!ttf.hasGlyph(name)) { throw new IllegalArgumentException( String.format("No glyph for U+%04X in font %s", unicode, getName())); } int gid = ttf.nameToGID(name); Integer code = getGIDToCode().get(gid); if (code == null) { throw new IllegalArgumentException( String.format("U+%04X is not available in this font's Encoding", unicode)); } return new byte[] { (byte) (int) code }; } /** * Inverts the font's code -> GID mapping. Any duplicate (GID -> code) mappings will be lost. */ protected Map getGIDToCode() throws IOException { if (gidToCode != null) { return gidToCode; } gidToCode = new HashMap(); for (int code = 0; code <= 255; code++) { int gid = codeToGID(code); if (!gidToCode.containsKey(gid)) { gidToCode.put(gid, code); } } return gidToCode; } @Override public boolean isEmbedded() { return isEmbedded; } @Override public GeneralPath getPath(int code) throws IOException { int gid = codeToGID(code); GlyphData glyph = ttf.getGlyph().getGlyph(gid); // some glyphs have no outlines (e.g. space, table, newline) if (glyph == null) { return new GeneralPath(); } return glyph.getPath(); } @Override public GeneralPath getPath(String name) throws IOException { // handle glyph names and uniXXXX names int gid = ttf.nameToGID(name); if (gid == 0) { try { // handle GID pseudo-names gid = Integer.parseInt(name); if (gid > ttf.getNumberOfGlyphs()) { gid = 0; } } catch (NumberFormatException e) { gid = 0; } } // I'm assuming .notdef paths are not drawn, as it PDFBOX-2421 if (gid == 0) { return new GeneralPath(); } GlyphData glyph = ttf.getGlyph().getGlyph(gid); if (glyph != null) { return glyph.getPath(); } return new GeneralPath(); } @Override public boolean hasGlyph(String name) throws IOException { int gid = ttf.nameToGID(name); return gid != 0; } @Override public FontBoxFont getFontBoxFont() { return ttf; } @Override public boolean hasGlyph(int code) throws IOException { return codeToGID(code) != 0; } /** * Returns the GID for the given character code. * * @param code character code * @return GID (glyph index) */ public int codeToGID(int code) throws IOException { extractCmapTable(); int gid = 0; if (!isSymbolic()) // non-symbolic { String name = encoding.getName(code); if (".notdef".equals(name)) { return 0; } // (3, 1) - (Windows, Unicode) if (cmapWinUnicode != null) { String unicode = GlyphList.getAdobeGlyphList().toUnicode(name); if (unicode != null) { int uni = unicode.codePointAt(0); gid = cmapWinUnicode.getGlyphId(uni); } } // (1, 0) - (Macintosh, Roman) if (gid == 0 && cmapMacRoman != null) { Integer macCode = INVERTED_MACOS_ROMAN.get(name); if (macCode != null) { gid = cmapMacRoman.getGlyphId(macCode); } } // 'post' table if (gid == 0) { gid = ttf.nameToGID(name); } } else // symbolic { // (3, 0) - (Windows, Symbol) if (cmapWinSymbol != null) { gid = cmapWinSymbol.getGlyphId(code); if (code >= 0 && code <= 0xFF) { // the CMap may use one of the following code ranges, // so that we have to add the high byte to get the // mapped value if (gid == 0) { // F000 - F0FF gid = cmapWinSymbol.getGlyphId(code + START_RANGE_F000); } if (gid == 0) { // F100 - F1FF gid = cmapWinSymbol.getGlyphId(code + START_RANGE_F100); } if (gid == 0) { // F200 - F2FF gid = cmapWinSymbol.getGlyphId(code + START_RANGE_F200); } } } // (1, 0) - (Mac, Roman) if (gid == 0 && cmapMacRoman != null) { gid = cmapMacRoman.getGlyphId(code); } // PDFBOX-3965: fallback for font has that the symbol flag but isn't if (gid == 0 && cmapWinUnicode != null && encoding != null) { String name = encoding.getName(code); if (".notdef".equals(name)) { return 0; } String unicode = GlyphList.getAdobeGlyphList().toUnicode(name); if (unicode != null) { int uni = unicode.codePointAt(0); gid = cmapWinUnicode.getGlyphId(uni); } } } return gid; } /** * extract all useful "cmap" subtables. */ private void extractCmapTable() throws IOException { if (cmapInitialized) { return; } CmapTable cmapTable = ttf.getCmap(); if (cmapTable != null) { // get all relevant "cmap" subtables CmapSubtable[] cmaps = cmapTable.getCmaps(); for (CmapSubtable cmap : cmaps) { if (CmapTable.PLATFORM_WINDOWS == cmap.getPlatformId()) { if (CmapTable.ENCODING_WIN_UNICODE_BMP == cmap.getPlatformEncodingId()) { cmapWinUnicode = cmap; } else if (CmapTable.ENCODING_WIN_SYMBOL == cmap.getPlatformEncodingId()) { cmapWinSymbol = cmap; } } else if (CmapTable.PLATFORM_MACINTOSH == cmap.getPlatformId() && CmapTable.ENCODING_MAC_ROMAN == cmap.getPlatformEncodingId()) { cmapMacRoman = cmap; } } } cmapInitialized = true; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDTrueTypeFontEmbedder.java000066400000000000000000000106631320103431700304170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.fontbox.ttf.HorizontalMetricsTable; import org.apache.fontbox.ttf.TrueTypeFont; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.font.encoding.Encoding; import org.sejda.sambox.pdmodel.font.encoding.GlyphList; /** * Embedded PDTrueTypeFont builder. Helper class to populate a PDTrueTypeFont from a TTF. * * @author John Hewson * @author Ben Litchfield */ final class PDTrueTypeFontEmbedder extends TrueTypeEmbedder { private final Encoding fontEncoding; /** * Creates a new TrueType font embedder for the given TTF as a PDTrueTypeFont. * * @param dict Font dictionary * @param ttf TTF font * @param encoding The PostScript encoding vector to be used for embedding. * @throws IOException if the TTF could not be read */ PDTrueTypeFontEmbedder(COSDictionary dict, TrueTypeFont ttf, Encoding encoding) throws IOException { super(dict, ttf, false); dict.setItem(COSName.SUBTYPE, COSName.TRUE_TYPE); GlyphList glyphList = GlyphList.getAdobeGlyphList(); this.fontEncoding = encoding; dict.setItem(COSName.ENCODING, encoding.getCOSObject()); fontDescriptor.setSymbolic(false); fontDescriptor.setNonSymbolic(true); // add the font descriptor dict.setItem(COSName.FONT_DESC, fontDescriptor); // set the glyph widths setWidths(dict, glyphList); } /** * Sets the glyph widths in the font dictionary. */ private void setWidths(COSDictionary font, GlyphList glyphList) throws IOException { float scaling = 1000f / ttf.getHeader().getUnitsPerEm(); HorizontalMetricsTable hmtx = ttf.getHorizontalMetrics(); Map codeToName = getFontEncoding().getCodeToNameMap(); int firstChar = Collections.min(codeToName.keySet()); int lastChar = Collections.max(codeToName.keySet()); List widths = new ArrayList(lastChar - firstChar + 1); for (int i = 0; i < lastChar - firstChar + 1; i++) { widths.add(0); } // a character code is mapped to a glyph name via the provided font encoding // afterwards, the glyph name is translated to a glyph ID. for (Map.Entry entry : codeToName.entrySet()) { int code = entry.getKey(); String name = entry.getValue(); if (code >= firstChar && code <= lastChar) { String uni = glyphList.toUnicode(name); int charCode = uni.codePointAt(0); int gid = cmap.getGlyphId(charCode); widths.set(entry.getKey() - firstChar, Math.round(hmtx.getAdvanceWidth(gid) * scaling)); } } font.setInt(COSName.FIRST_CHAR, firstChar); font.setInt(COSName.LAST_CHAR, lastChar); font.setItem(COSName.WIDTHS, COSArrayList.converterToCOSArray(widths)); } /** * Returns the font's encoding. */ public Encoding getFontEncoding() { return fontEncoding; } @Override protected void buildSubset(InputStream ttfSubset, String tag, Map gidToCid) throws IOException { // use PDType0Font instead throw new UnsupportedOperationException(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDType0Font.java000066400000000000000000000355621320103431700262140ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.nonNull; import java.awt.geom.GeneralPath; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.util.Set; import org.apache.fontbox.cmap.CMap; import org.apache.fontbox.ttf.TTFParser; import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A Composite (Type 0) font. * * @author Ben Litchfield */ public class PDType0Font extends PDFont implements PDVectorFont { private static final Logger LOG = LoggerFactory.getLogger(PDType0Font.class); private final PDCIDFont descendantFont; private CMap cMap, cMapUCS2; private boolean isCMapPredefined; private boolean isDescendantCJK; private PDCIDFontType2Embedder embedder; private final Set noUnicode = new HashSet<>(); private TrueTypeFont ttf; /** * Loads a TTF to be embedded into a document as a Type 0 font. * * @param doc The PDF document that will hold the embedded font. * @param file A TrueType font. * @return A Type0 font with a CIDFontType2 descendant. * @throws IOException If there is an error reading the font file. */ public static PDType0Font load(PDDocument doc, File file) throws IOException { return new PDType0Font(doc, new TTFParser().parse(file), true, true); } /** * Loads a TTF to be embedded into a document as a Type 0 font. * * @param doc The PDF document that will hold the embedded font. * @param input A TrueType font. * @return A Type0 font with a CIDFontType2 descendant. * @throws IOException If there is an error reading the font stream. */ public static PDType0Font load(PDDocument doc, InputStream input) throws IOException { return new PDType0Font(doc, new TTFParser().parse(input), true, true); } /** * Loads a TTF to be embedded into a document as a Type 0 font. * * @param doc The PDF document that will hold the embedded font. * @param input A TrueType font. * @param embedSubset True if the font will be subset before embedding * @return A Type0 font with a CIDFontType2 descendant. * @throws IOException If there is an error reading the font stream. */ public static PDType0Font load(PDDocument doc, InputStream input, boolean embedSubset) throws IOException { return new PDType0Font(doc, new TTFParser().parse(input), embedSubset, true); } /** * Loads a TTF to be embedded into a document as a Type 0 font. * * @param doc The PDF document that will hold the embedded font. * @param ttf A TrueType font. * @param embedSubset True if the font will be subset before embedding * @return A Type0 font with a CIDFontType2 descendant. * @throws IOException If there is an error reading the font stream. */ public static PDType0Font load(PDDocument doc, TrueTypeFont ttf, boolean embedSubset) throws IOException { return new PDType0Font(doc, ttf, embedSubset, false); } /** * Constructor for reading a Type0 font from a PDF file. * * @param fontDictionary The font dictionary according to the PDF specification. */ public PDType0Font(COSDictionary fontDictionary) throws IOException { super(fontDictionary); COSBase base = dict.getDictionaryObject(COSName.DESCENDANT_FONTS); if (!(base instanceof COSArray)) { throw new IOException("Missing descendant font array"); } COSArray descendantFonts = (COSArray) base; if (descendantFonts.size() == 0) { throw new IOException("Descendant font array is empty"); } COSBase descendantFontDictBase = descendantFonts.getObject(0); if (!(descendantFontDictBase instanceof COSDictionary)) { throw new IOException("Missing descendant font dictionary"); } descendantFont = PDFontFactory.createDescendantFont((COSDictionary) descendantFontDictBase, this); readEncoding(); fetchCMapUCS2(); } /** * Private. Creates a new TrueType font for embedding. */ private PDType0Font(PDDocument document, TrueTypeFont ttf, boolean embedSubset, boolean closeOnSubset) throws IOException { embedder = new PDCIDFontType2Embedder(document, dict, ttf, embedSubset, this); descendantFont = embedder.getCIDFont(); readEncoding(); fetchCMapUCS2(); if (closeOnSubset) { if (embedSubset) { this.ttf = ttf; } else { // the TTF is fully loaded and it is safe to close the underlying data source ttf.close(); } } } @Override public void addToSubset(int codePoint) { if (!willBeSubset()) { throw new IllegalStateException("This font was created with subsetting disabled"); } embedder.addToSubset(codePoint); } @Override public void subset() throws IOException { if (!willBeSubset()) { throw new IllegalStateException("This font was created with subsetting disabled"); } embedder.subset(); if (nonNull(ttf)) { ttf.close(); ttf = null; } } @Override public boolean willBeSubset() { return embedder != null && embedder.needsSubset(); } /** * Reads the font's Encoding entry, which should be a CMap name/stream. */ private void readEncoding() throws IOException { COSBase encoding = dict.getDictionaryObject(COSName.ENCODING); if (encoding instanceof COSName) { // predefined CMap COSName encodingName = (COSName) encoding; cMap = CMapManager.getPredefinedCMap(encodingName.getName()); if (cMap != null) { isCMapPredefined = true; } else { throw new IOException("Missing required CMap"); } } else if (encoding != null) { cMap = readCMap(encoding); if (cMap == null) { throw new IOException("Missing required CMap"); } else if (!cMap.hasCIDMappings()) { LOG.warn("Invalid Encoding CMap in font " + getName()); } } // check if the descendant font is CJK PDCIDSystemInfo ros = descendantFont.getCIDSystemInfo(); if (ros != null) { isDescendantCJK = "Adobe".equals(ros.getRegistry()) && ("GB1".equals(ros.getOrdering()) || "CNS1".equals(ros.getOrdering()) || "Japan1".equals(ros.getOrdering()) || "Korea1".equals(ros.getOrdering())); } } /** * Fetches the corresponding UCS2 CMap if the font's CMap is predefined. */ private void fetchCMapUCS2() throws IOException { // if the font is composite and uses a predefined cmap (excluding Identity-H/V) // or whose descendant CIDFont uses the Adobe-GB1, Adobe-CNS1, Adobe-Japan1, or // Adobe-Korea1 character collection: COSName name = dict.getCOSName(COSName.ENCODING); if (isCMapPredefined && !(name == COSName.IDENTITY_H || name == COSName.IDENTITY_V) || isDescendantCJK) { // a) Map the character code to a CID using the font's CMap // b) Obtain the ROS from the font's CIDSystemInfo // c) Construct a second CMap name by concatenating the ROS in the format "R-O-UCS2" // d) Obtain the CMap with the constructed name // e) Map the CID according to the CMap from step d), producing a Unicode value String strName = null; if (isDescendantCJK) { strName = descendantFont.getCIDSystemInfo().getRegistry() + "-" + descendantFont.getCIDSystemInfo().getOrdering() + "-" + descendantFont.getCIDSystemInfo().getSupplement(); } else if (name != null) { strName = name.getName(); } // try to find the corresponding Unicode (UC2) CMap if (strName != null) { CMap prdCMap = CMapManager.getPredefinedCMap(strName); String ucs2Name = prdCMap.getRegistry() + "-" + prdCMap.getOrdering() + "-UCS2"; cMapUCS2 = CMapManager.getPredefinedCMap(ucs2Name); } } } /** * Returns the PostScript name of the font. */ public String getBaseFont() { return dict.getNameAsString(COSName.BASE_FONT); } /** * Returns the descendant font. */ public PDCIDFont getDescendantFont() { return descendantFont; } /** * Returns the font's CMap. */ public CMap getCMap() { return cMap; } /** * Returns the font's UCS2 CMap, only present this font uses a predefined CMap. */ public CMap getCMapUCS2() { return cMapUCS2; } @Override public PDFontDescriptor getFontDescriptor() { return descendantFont.getFontDescriptor(); } @Override public Matrix getFontMatrix() { return descendantFont.getFontMatrix(); } @Override public boolean isVertical() { return cMap.getWMode() == 1; } @Override public float getHeight(int code) throws IOException { return descendantFont.getHeight(code); } @Override protected byte[] encode(int unicode) throws IOException { return descendantFont.encode(unicode); } @Override public float getAverageFontWidth() { return descendantFont.getAverageFontWidth(); } @Override public Vector getPositionVector(int code) { // units are always 1/1000 text space, font matrix is not used, see FOP-2252 return descendantFont.getPositionVector(code).scale(-1 / 1000f); } @Override public Vector getDisplacement(int code) throws IOException { if (isVertical()) { return new Vector(0, descendantFont.getVerticalDisplacementVectorY(code) / 1000f); } return super.getDisplacement(code); } @Override public float getWidth(int code) throws IOException { return descendantFont.getWidth(code); } @Override protected float getStandard14Width(int code) { throw new UnsupportedOperationException("not suppported"); } @Override public float getWidthFromFont(int code) throws IOException { return descendantFont.getWidthFromFont(code); } @Override public boolean isEmbedded() { return descendantFont.isEmbedded(); } @Override public String toUnicode(int code) throws IOException { // try to use a ToUnicode CMap String unicode = super.toUnicode(code); if (unicode != null) { return unicode; } if ((isCMapPredefined || isDescendantCJK) && cMapUCS2 != null) { // if the font is composite and uses a predefined cmap (excluding Identity-H/V) then // or if its decendant font uses Adobe-GB1/CNS1/Japan1/Korea1 // a) Map the character code to a character identifier (CID) according to the font?s CMap int cid = codeToCID(code); // e) Map the CID according to the CMap from step d), producing a Unicode value return cMapUCS2.toUnicode(cid); } if (LOG.isWarnEnabled() && !noUnicode.contains(code)) { // if no value has been produced, there is no way to obtain Unicode for the character. String cid = "CID+" + codeToCID(code); LOG.warn("No Unicode mapping for " + cid + " (" + code + ") in font " + getName()); // we keep track of which warnings have been issued, so we don't log multiple times noUnicode.add(code); } return null; } @Override public String getName() { return getBaseFont(); } @Override public BoundingBox getBoundingBox() throws IOException { // Will be cached by underlying font return descendantFont.getBoundingBox(); } @Override public int readCode(InputStream in) throws IOException { return cMap.readCode(in); } /** * Returns the CID for the given character code. If not found then CID 0 is returned. * * @param code character code * @return CID */ public int codeToCID(int code) { return descendantFont.codeToCID(code); } /** * Returns the GID for the given character code. * * @param code character code * @return GID */ public int codeToGID(int code) throws IOException { return descendantFont.codeToGID(code); } @Override public boolean isStandard14() { return false; } @Override public boolean isDamaged() { return descendantFont.isDamaged(); } @Override public String toString() { String descendant = null; if (getDescendantFont() != null) { descendant = getDescendantFont().getClass().getSimpleName(); } return getClass().getSimpleName() + "/" + descendant + " " + getBaseFont(); } @Override public GeneralPath getPath(int code) throws IOException { return descendantFont.getPath(code); } @Override public boolean hasGlyph(int code) throws IOException { return descendantFont.hasGlyph(code); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDType1CFont.java000066400000000000000000000265341320103431700263170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.nonNull; import static org.sejda.sambox.pdmodel.font.UniUtil.getUniNameOfCodePoint; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.fontbox.EncodedFont; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.cff.CFFParser; import org.apache.fontbox.cff.CFFType1Font; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.font.encoding.Encoding; import org.sejda.sambox.pdmodel.font.encoding.StandardEncoding; import org.sejda.sambox.pdmodel.font.encoding.Type1Encoding; import org.sejda.sambox.util.Matrix; import org.sejda.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Type 1-equivalent CFF font. * * @author Villu Ruusmann * @author John Hewson */ public class PDType1CFont extends PDSimpleFont { private static final Logger LOG = LoggerFactory.getLogger(PDType1CFont.class); private final Map glyphHeights = new HashMap<>(); private Float avgWidth = null; private Matrix fontMatrix; private final AffineTransform fontMatrixTransform; private final CFFType1Font cffFont; // embedded font private final FontBoxFont genericFont; // embedded or system font for rendering private final boolean isEmbedded; private final boolean isDamaged; private BoundingBox fontBBox; /** * Constructor. * * @param fontDictionary the corresponding dictionary * @throws IOException it something went wrong */ public PDType1CFont(COSDictionary fontDictionary) throws IOException { super(fontDictionary); PDFontDescriptor fd = getFontDescriptor(); byte[] bytes = null; if (fd != null) { PDStream ff3Stream = fd.getFontFile3(); if (ff3Stream != null) { bytes = IOUtils.toByteArray(ff3Stream.createInputStream()); if (bytes.length == 0) { LOG.error("Invalid data for embedded Type1C font " + getName()); bytes = null; } } } boolean fontIsDamaged = false; CFFType1Font cffEmbedded = null; try { if (bytes != null) { // note: this could be an OpenType file, fortunately CFFParser can handle that CFFParser cffParser = new CFFParser(); cffEmbedded = (CFFType1Font) cffParser.parse(bytes, new ByteSource()).get(0); } } catch (IOException e) { LOG.error("Can't read the embedded Type1C font " + getName(), e); fontIsDamaged = true; } isDamaged = fontIsDamaged; cffFont = cffEmbedded; if (cffFont != null) { genericFont = cffFont; isEmbedded = true; } else { FontMapping mapping = FontMappers.instance().getFontBoxFont(getBaseFont(), fd); genericFont = mapping.getFont(); if (mapping.isFallback()) { LOG.warn("Using fallback font " + genericFont.getName() + " for " + getBaseFont()); } isEmbedded = false; } readEncoding(); fontMatrixTransform = getFontMatrix().createAffineTransform(); fontMatrixTransform.scale(1000, 1000); } private class ByteSource implements CFFParser.ByteSource { @Override public byte[] getBytes() throws IOException { PDStream ff3Stream = getFontDescriptor().getFontFile3(); return IOUtils.toByteArray(ff3Stream.createInputStream()); } } @Override public FontBoxFont getFontBoxFont() { return genericFont; } /** * Returns the PostScript name of the font. */ public final String getBaseFont() { return dict.getNameAsString(COSName.BASE_FONT); } @Override public GeneralPath getPath(String name) throws IOException { // Acrobat only draws .notdef for embedded or "Standard 14" fonts, see PDFBOX-2372 if (name.equals(".notdef") && !isEmbedded() && !isStandard14()) { return new GeneralPath(); } return genericFont.getPath(name); } @Override public boolean hasGlyph(String name) throws IOException { return genericFont.hasGlyph(name); } @Override public final String getName() { return getBaseFont(); } @Override public BoundingBox getBoundingBox() throws IOException { if (fontBBox == null) { fontBBox = generateBoundingBox(); } return fontBBox; } private BoundingBox generateBoundingBox() throws IOException { if (getFontDescriptor() != null) { PDRectangle bbox = getFontDescriptor().getFontBoundingBox(); if (nonNull(bbox) && bbox.getLowerLeftX() != 0 || bbox.getLowerLeftY() != 0 || bbox.getUpperRightX() != 0 || bbox.getUpperRightY() != 0) { return new BoundingBox(bbox.getLowerLeftX(), bbox.getLowerLeftY(), bbox.getUpperRightX(), bbox.getUpperRightY()); } } return genericFont.getFontBBox(); } // @Override public String codeToName(int code) { return getEncoding().getName(code); } @Override protected Encoding readEncodingFromFont() throws IOException { if (!isEmbedded() && getStandard14AFM() != null) { // read from AFM return new Type1Encoding(getStandard14AFM()); } // extract from Type1 font/substitute if (genericFont instanceof EncodedFont) { return Type1Encoding.fromFontBox(((EncodedFont) genericFont).getEncoding()); } // default (only happens with TTFs) return StandardEncoding.INSTANCE; } @Override public int readCode(InputStream in) throws IOException { return in.read(); } @Override public final Matrix getFontMatrix() { if (fontMatrix == null) { List numbers = null; try { numbers = genericFont.getFontMatrix(); } catch (IOException e) { fontMatrix = DEFAULT_FONT_MATRIX; } if (numbers != null && numbers.size() == 6) { fontMatrix = new Matrix(numbers.get(0).floatValue(), numbers.get(1).floatValue(), numbers.get(2).floatValue(), numbers.get(3).floatValue(), numbers.get(4).floatValue(), numbers.get(5).floatValue()); } else { return super.getFontMatrix(); } } return fontMatrix; } @Override public boolean isDamaged() { return isDamaged; } @Override public float getWidthFromFont(int code) throws IOException { String name = codeToName(code); float width = genericFont.getWidth(name); Point2D p = new Point2D.Float(width, 0); fontMatrixTransform.transform(p, p); return (float) p.getX(); } @Override public boolean isEmbedded() { return isEmbedded; } @Override public float getHeight(int code) throws IOException { String name = codeToName(code); float height = 0; if (!glyphHeights.containsKey(name)) { height = (float) cffFont.getType1CharString(name).getBounds().getHeight(); // todo: cffFont could be null glyphHeights.put(name, height); } return height; } @Override protected byte[] encode(int unicode) throws IOException { String name = getGlyphList().codePointToName(unicode); if (!encoding.contains(name)) { throw new IllegalArgumentException( String.format("U+%04X ('%s') is not available in this font's encoding: %s", unicode, name, encoding.getEncodingName())); } String nameInFont = getNameInFont(name); Map inverted = encoding.getNameToCodeMap(); if (nameInFont.equals(".notdef") || !genericFont.hasGlyph(nameInFont)) { throw new IllegalArgumentException( String.format("No glyph for U+%04X in font %s", unicode, getName())); } int code = inverted.get(name); return new byte[] { (byte) code }; } @Override public float getStringWidth(String string) throws IOException { float width = 0; for (int i = 0; i < string.length(); i++) { int codePoint = string.codePointAt(i); String name = getGlyphList().codePointToName(codePoint); width += cffFont.getType1CharString(name).getWidth(); } return width; } @Override public float getAverageFontWidth() { if (avgWidth == null) { avgWidth = getAverageCharacterWidth(); } return avgWidth; } /** * Returns the embedded Type 1-equivalent CFF font. * * @return the cffFont */ public CFFType1Font getCFFType1Font() { return cffFont; } // todo: this is a replacement for FontMetrics method private float getAverageCharacterWidth() { // todo: not implemented, highly suspect return 500; } /** * Maps a PostScript glyph name to the name in the underlying font, for example when using a TTF font we might map * "W" to "uni0057". */ private String getNameInFont(String name) throws IOException { if (isEmbedded() || genericFont.hasGlyph(name)) { return name; } // try unicode name String unicodes = getGlyphList().toUnicode(name); if (unicodes != null && unicodes.length() == 1) { String uniName = getUniNameOfCodePoint(unicodes.codePointAt(0)); if (genericFont.hasGlyph(uniName)) { return uniName; } } return ".notdef"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDType1Font.java000066400000000000000000000526571320103431700262210ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.nonNull; import static org.sejda.sambox.pdmodel.font.UniUtil.getUniNameOfCodePoint; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.fontbox.EncodedFont; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.type1.DamagedFontException; import org.apache.fontbox.type1.Type1Font; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.font.encoding.Encoding; import org.sejda.sambox.pdmodel.font.encoding.StandardEncoding; import org.sejda.sambox.pdmodel.font.encoding.SymbolEncoding; import org.sejda.sambox.pdmodel.font.encoding.Type1Encoding; import org.sejda.sambox.pdmodel.font.encoding.WinAnsiEncoding; import org.sejda.sambox.pdmodel.font.encoding.ZapfDingbatsEncoding; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A PostScript Type 1 Font. * * @author Ben Litchfield */ public class PDType1Font extends PDSimpleFont { private static final Logger LOG = LoggerFactory.getLogger(PDType1Font.class); // alternative names for glyphs which are commonly encountered private static final Map ALT_NAMES = new HashMap<>(); static { ALT_NAMES.put("ff", "f_f"); ALT_NAMES.put("ffi", "f_f_i"); ALT_NAMES.put("ffl", "f_f_l"); ALT_NAMES.put("fi", "f_i"); ALT_NAMES.put("fl", "f_l"); ALT_NAMES.put("st", "s_t"); ALT_NAMES.put("IJ", "I_J"); ALT_NAMES.put("ij", "i_j"); ALT_NAMES.put("ellipsis", "elipsis"); // misspelled in ArialMT } private static final int PFB_START_MARKER = 0x80; // todo: replace with enum? or getters? public static final PDType1Font TIMES_ROMAN = new PDType1Font("Times-Roman"); public static final PDType1Font TIMES_BOLD = new PDType1Font("Times-Bold"); public static final PDType1Font TIMES_ITALIC = new PDType1Font("Times-Italic"); public static final PDType1Font TIMES_BOLD_ITALIC = new PDType1Font("Times-BoldItalic"); public static final PDType1Font HELVETICA = new PDType1Font("Helvetica"); public static final PDType1Font HELVETICA_BOLD = new PDType1Font("Helvetica-Bold"); public static final PDType1Font HELVETICA_OBLIQUE = new PDType1Font("Helvetica-Oblique"); public static final PDType1Font HELVETICA_BOLD_OBLIQUE = new PDType1Font( "Helvetica-BoldOblique"); public static final PDType1Font COURIER = new PDType1Font("Courier"); public static final PDType1Font COURIER_BOLD = new PDType1Font("Courier-Bold"); public static final PDType1Font COURIER_OBLIQUE = new PDType1Font("Courier-Oblique"); public static final PDType1Font COURIER_BOLD_OBLIQUE = new PDType1Font("Courier-BoldOblique"); public static final PDType1Font SYMBOL = new PDType1Font("Symbol"); public static final PDType1Font ZAPF_DINGBATS = new PDType1Font("ZapfDingbats"); private final Type1Font type1font; // embedded font private final FontBoxFont genericFont; // embedded or system font for rendering private final boolean isEmbedded; private final boolean isDamaged; private Matrix fontMatrix; private final AffineTransform fontMatrixTransform; private BoundingBox fontBBox; /** * to improve encoding speed. */ private final Map codeToBytesMap; /** * Creates a Type 1 standard 14 font for embedding. * * @param baseFont One of the standard 14 PostScript names */ private PDType1Font(String baseFont) { super(baseFont); dict.setItem(COSName.SUBTYPE, COSName.TYPE1); dict.setName(COSName.BASE_FONT, baseFont); if ("ZapfDingbats".equals(baseFont)) { encoding = ZapfDingbatsEncoding.INSTANCE; } else if ("Symbol".equals(baseFont)) { encoding = SymbolEncoding.INSTANCE; } else { encoding = WinAnsiEncoding.INSTANCE; dict.setItem(COSName.ENCODING, COSName.WIN_ANSI_ENCODING); } // standard 14 fonts may be accessed concurrently, as they are singletons codeToBytesMap = new ConcurrentHashMap<>(); // todo: could load the PFB font here if we wanted to support Standard 14 embedding type1font = null; FontMapping mapping = FontMappers.instance().getFontBoxFont(getBaseFont(), getFontDescriptor()); genericFont = mapping.getFont(); if (mapping.isFallback()) { String fontName; try { fontName = genericFont.getName(); } catch (IOException e) { fontName = "?"; } LOG.warn("Using fallback font " + fontName + " for base font " + getBaseFont()); } isEmbedded = false; isDamaged = false; fontMatrixTransform = new AffineTransform(); } /** * Creates a new Type 1 font for embedding. * * @param doc PDF document to write to * @param pfbIn PFB file stream * @throws IOException */ public PDType1Font(PDDocument doc, InputStream pfbIn) throws IOException { PDType1FontEmbedder embedder = new PDType1FontEmbedder(doc, dict, pfbIn, null); encoding = embedder.getFontEncoding(); glyphList = embedder.getGlyphList(); type1font = embedder.getType1Font(); genericFont = embedder.getType1Font(); isEmbedded = true; isDamaged = false; fontMatrixTransform = new AffineTransform(); codeToBytesMap = new HashMap<>(); } /** * Creates a new Type 1 font for embedding. * * @param doc PDF document to write to * @param pfbIn PFB file stream * @param encoding * @throws IOException */ public PDType1Font(PDDocument doc, InputStream pfbIn, Encoding encoding) throws IOException { PDType1FontEmbedder embedder = new PDType1FontEmbedder(doc, dict, pfbIn, encoding); this.encoding = encoding; glyphList = embedder.getGlyphList(); type1font = embedder.getType1Font(); genericFont = embedder.getType1Font(); isEmbedded = true; isDamaged = false; fontMatrixTransform = new AffineTransform(); codeToBytesMap = new HashMap<>(); } /** * Creates a Type 1 font from a Font dictionary in a PDF. * * @param fontDictionary font dictionary. * @throws IOException if there was an error initializing the font. * @throws IllegalArgumentException if /FontFile3 was used. */ public PDType1Font(COSDictionary fontDictionary) throws IOException { super(fontDictionary); codeToBytesMap = new HashMap<>(); PDFontDescriptor fd = getFontDescriptor(); Type1Font t1 = null; boolean fontIsDamaged = false; if (fd != null) { // a Type1 font may contain a Type1C font PDStream fontFile3 = fd.getFontFile3(); if (fontFile3 != null) { throw new IllegalArgumentException("Use PDType1CFont for FontFile3"); } // or it may contain a PFB PDStream fontFile = fd.getFontFile(); if (fontFile != null) { try { COSStream stream = fontFile.getCOSObject(); int length1 = stream.getInt(COSName.LENGTH1); int length2 = stream.getInt(COSName.LENGTH2); // repair Length1 and Length2 if necessary byte[] bytes = fontFile.toByteArray(); length1 = repairLength1(bytes, length1); length2 = repairLength2(bytes, length1, length2); if (bytes.length > 0 && (bytes[0] & 0xff) == PFB_START_MARKER) { // some bad files embed the entire PFB, see PDFBOX-2607 t1 = Type1Font.createWithPFB(bytes); } else { // the PFB embedded as two segments back-to-back byte[] segment1 = Arrays.copyOfRange(bytes, 0, length1); byte[] segment2 = Arrays.copyOfRange(bytes, length1, length1 + length2); // empty streams are simply ignored if (length1 > 0 && length2 > 0) { t1 = Type1Font.createWithSegments(segment1, segment2); } } } catch (DamagedFontException e) { LOG.warn("Can't read damaged embedded Type1 font " + fd.getFontName()); fontIsDamaged = true; } catch (IOException e) { LOG.error("Can't read the embedded Type1 font " + fd.getFontName(), e); fontIsDamaged = true; } } } isEmbedded = t1 != null; isDamaged = fontIsDamaged; type1font = t1; // find a generic font to use for rendering, could be a .pfb, but might be a .ttf if (type1font != null) { genericFont = type1font; } else { FontMapping mapping = FontMappers.instance().getFontBoxFont(getBaseFont(), fd); genericFont = mapping.getFont(); if (mapping.isFallback()) { LOG.warn("Using fallback font " + genericFont.getName() + " for " + getBaseFont()); } } readEncoding(); fontMatrixTransform = getFontMatrix().createAffineTransform(); fontMatrixTransform.scale(1000, 1000); } /** * Some Type 1 fonts have an invalid Length1, which causes the binary segment of the font to be truncated, see * PDFBOX-2350. * * @param bytes Type 1 stream bytes * @param length1 Length1 from the Type 1 stream * @return repaired Length1 value */ private int repairLength1(byte[] bytes, int length1) { // scan backwards from the end of the first segment to find 'exec' int offset = Math.max(0, length1 - 4); if (offset <= 0 || offset > bytes.length - 4) { offset = bytes.length - 4; } offset = findBinaryOffsetAfterExec(bytes, offset); if (offset == 0 && length1 > 0) { // 2nd try with brute force offset = findBinaryOffsetAfterExec(bytes, bytes.length - 4); } if (length1 - offset != 0 && offset > 0) { if (LOG.isWarnEnabled()) { LOG.warn("Ignored invalid Length1 " + length1 + " for Type 1 font " + getName()); } return offset; } return length1; } private static int findBinaryOffsetAfterExec(byte[] bytes, int startOffset) { int offset = startOffset; while (offset > 0) { if (bytes[offset + 0] == 'e' && bytes[offset + 1] == 'x' && bytes[offset + 2] == 'e' && bytes[offset + 3] == 'c') { offset += 4; // skip additional CR LF space characters while (offset < bytes.length && (bytes[offset] == '\r' || bytes[offset] == '\n' || bytes[offset] == ' ' || bytes[offset] == '\t')) { offset++; } break; } offset--; } return offset; } /** * Some Type 1 fonts have an invalid Length2, see PDFBOX-3475. A negative /Length2 brings an * IllegalArgumentException in Arrays.copyOfRange(), a huge value eats up memory because of padding. * * @param bytes Type 1 stream bytes * @param length1 Length1 from the Type 1 stream * @param length2 Length2 from the Type 1 stream * @return repaired Length2 value */ private int repairLength2(byte[] bytes, int length1, int length2) { // repair Length2 if necessary if (length2 < 0 || length2 > bytes.length - length1) { LOG.warn("Ignored invalid Length2 " + length2 + " for Type 1 font " + getName()); return bytes.length - length1; } return length2; } /** * Returns the PostScript name of the font. */ public final String getBaseFont() { return dict.getNameAsString(COSName.BASE_FONT); } @Override public float getHeight(int code) throws IOException { String name = codeToName(code); if (getStandard14AFM() != null) { String afmName = getEncoding().getName(code); return getStandard14AFM().getCharacterHeight(afmName); // todo: isn't this the y-advance, not the height? } // todo: should be scaled by font matrix return (float) genericFont.getPath(name).getBounds().getHeight(); } @Override protected byte[] encode(int unicode) throws IOException { byte[] bytes = codeToBytesMap.get(unicode); if (bytes != null) { return bytes; } String name = getGlyphList().codePointToName(unicode); if (isStandard14()) { // genericFont not needed, thus simplified code // this is important on systems with no installed fonts if (!encoding.contains(name)) { throw new IllegalArgumentException( String.format("U+%04X ('%s') is not available in this font %s encoding: %s", unicode, name, getName(), encoding.getEncodingName())); } if (".notdef".equals(name)) { throw new IllegalArgumentException( String.format("No glyph for U+%04X in font %s", unicode, getName())); } } else { if (!encoding.contains(name)) { throw new IllegalArgumentException(String.format( "U+%04X ('%s') is not available in this font %s (generic: %s) encoding: %s", unicode, name, getName(), genericFont.getName(), encoding.getEncodingName())); } String nameInFont = getNameInFont(name); if (nameInFont.equals(".notdef") || !genericFont.hasGlyph(nameInFont)) { throw new IllegalArgumentException( String.format("No glyph for U+%04X in font %s (generic: %s)", unicode, getName(), genericFont.getName())); } } Map inverted = encoding.getNameToCodeMap(); int code = inverted.get(name); bytes = new byte[] { (byte) code }; codeToBytesMap.put(code, bytes); return bytes; } @Override public float getWidthFromFont(int code) throws IOException { String name = codeToName(code); // width of .notdef is ignored for substitutes, see PDFBOX-1900 if (!isEmbedded && ".notdef".equals(name)) { return 250; } float width = genericFont.getWidth(name); Point2D p = new Point2D.Float(width, 0); fontMatrixTransform.transform(p, p); return (float) p.getX(); } @Override public boolean isEmbedded() { return isEmbedded; } @Override public float getAverageFontWidth() { if (getStandard14AFM() != null) { return getStandard14AFM().getAverageCharacterWidth(); } return super.getAverageFontWidth(); } @Override public int readCode(InputStream in) throws IOException { return in.read(); } @Override protected Encoding readEncodingFromFont() throws IOException { if (!isEmbedded() && getStandard14AFM() != null) { // read from AFM return new Type1Encoding(getStandard14AFM()); } // extract from Type1 font/substitute if (genericFont instanceof EncodedFont) { return Type1Encoding.fromFontBox(((EncodedFont) genericFont).getEncoding()); } // default (only happens with TTFs) return StandardEncoding.INSTANCE; } /** * Returns the embedded or substituted Type 1 font, or null if there is none. */ public Type1Font getType1Font() { return type1font; } @Override public FontBoxFont getFontBoxFont() { return genericFont; } @Override public String getName() { return getBaseFont(); } @Override public BoundingBox getBoundingBox() throws IOException { if (fontBBox == null) { fontBBox = generateBoundingBox(); } return fontBBox; } private BoundingBox generateBoundingBox() throws IOException { if (getFontDescriptor() != null) { PDRectangle bbox = getFontDescriptor().getFontBoundingBox(); if (nonNull(bbox) && bbox.getLowerLeftX() != 0 || bbox.getLowerLeftY() != 0 || bbox.getUpperRightX() != 0 || bbox.getUpperRightY() != 0) { return new BoundingBox(bbox.getLowerLeftX(), bbox.getLowerLeftY(), bbox.getUpperRightX(), bbox.getUpperRightY()); } } return genericFont.getFontBBox(); } // @Override public String codeToName(int code) throws IOException { String name = getEncoding().getName(code); return getNameInFont(name); } /** * Maps a PostScript glyph name to the name in the underlying font, for example when using a TTF font we might map * "W" to "uni0057". */ private String getNameInFont(String name) throws IOException { if (isEmbedded() || genericFont.hasGlyph(name)) { return name; } // try alternative name String altName = ALT_NAMES.get(name); if (altName != null && !name.equals(".notdef") && genericFont.hasGlyph(altName)) { return altName; } // try unicode name String unicodes = getGlyphList().toUnicode(name); if (unicodes != null && unicodes.length() == 1) { String uniName = getUniNameOfCodePoint(unicodes.codePointAt(0)); if (genericFont.hasGlyph(uniName)) { return uniName; } } return ".notdef"; } @Override public GeneralPath getPath(String name) throws IOException { // Acrobat does not draw .notdef for Type 1 fonts, see PDFBOX-2421 // I suspect that it does do this for embedded fonts though, but this is untested if (name.equals(".notdef") && !isEmbedded) { return new GeneralPath(); } else { return genericFont.getPath(getNameInFont(name)); } } @Override public boolean hasGlyph(String name) throws IOException { return genericFont.hasGlyph(getNameInFont(name)); } @Override public final Matrix getFontMatrix() { if (fontMatrix == null) { // PDF specified that Type 1 fonts use a 1000upem matrix, but some fonts specify // their own custom matrix anyway, for example PDFBOX-2298 List numbers = null; try { numbers = genericFont.getFontMatrix(); } catch (IOException e) { fontMatrix = DEFAULT_FONT_MATRIX; } if (numbers != null && numbers.size() == 6) { fontMatrix = new Matrix(numbers.get(0).floatValue(), numbers.get(1).floatValue(), numbers.get(2).floatValue(), numbers.get(3).floatValue(), numbers.get(4).floatValue(), numbers.get(5).floatValue()); } else { return super.getFontMatrix(); } } return fontMatrix; } @Override public boolean isDamaged() { return isDamaged; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDType1FontEmbedder.java000066400000000000000000000135631320103431700276420ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.apache.fontbox.afm.FontMetrics; import org.apache.fontbox.pfb.PfbParser; import org.apache.fontbox.type1.Type1Font; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.font.encoding.Encoding; import org.sejda.sambox.pdmodel.font.encoding.GlyphList; import org.sejda.sambox.pdmodel.font.encoding.Type1Encoding; import org.sejda.util.IOUtils; /** * Embedded PDType1Font builder. Helper class to populate a PDType1Font from a PFB and AFM. * * @author Michael Niedermair */ class PDType1FontEmbedder { private final Encoding fontEncoding; private final Type1Font type1; /** * This will load a PFB to be embedded into a document. * * @param doc The PDF document that will hold the embedded font. * @param dict The Font dictionary to write to. * @param pfbStream The pfb input. * @throws IOException If there is an error loading the data. */ PDType1FontEmbedder(PDDocument doc, COSDictionary dict, InputStream pfbStream, Encoding encoding) throws IOException { dict.setItem(COSName.SUBTYPE, COSName.TYPE1); // read the pfb byte[] pfbBytes = IOUtils.toByteArray(pfbStream); PfbParser pfbParser = new PfbParser(pfbBytes); type1 = Type1Font.createWithPFB(pfbBytes); if (encoding == null) { fontEncoding = Type1Encoding.fromFontBox(type1.getEncoding()); } else { fontEncoding = encoding; } // build font descriptor PDFontDescriptor fd = buildFontDescriptor(type1); PDStream fontStream = new PDStream(pfbParser.getInputStream(), COSName.FLATE_DECODE); fontStream.getCOSObject().setInt("Length", pfbParser.size()); for (int i = 0; i < pfbParser.getLengths().length; i++) { fontStream.getCOSObject().setInt("Length" + (i + 1), pfbParser.getLengths()[i]); } fd.setFontFile(fontStream); // set the values dict.setItem(COSName.FONT_DESC, fd); dict.setName(COSName.BASE_FONT, type1.getName()); // widths List widths = new ArrayList<>(256); for (int code = 0; code <= 255; code++) { String name = fontEncoding.getName(code); int width = Math.round(type1.getWidth(name)); widths.add(width); } dict.setInt(COSName.FIRST_CHAR, 0); dict.setInt(COSName.LAST_CHAR, 255); dict.setItem(COSName.WIDTHS, COSArrayList.converterToCOSArray(widths)); } /** * Returns a PDFontDescriptor for the given PFB. */ static PDFontDescriptor buildFontDescriptor(Type1Font type1) { boolean isSymbolic = type1 .getEncoding() instanceof org.apache.fontbox.encoding.BuiltInEncoding; PDFontDescriptor fd = new PDFontDescriptor(); fd.setFontName(type1.getName()); fd.setFontFamily(type1.getFamilyName()); fd.setNonSymbolic(!isSymbolic); fd.setSymbolic(isSymbolic); fd.setFontBoundingBox(new PDRectangle(type1.getFontBBox())); fd.setItalicAngle(type1.getItalicAngle()); fd.setAscent(type1.getFontBBox().getUpperRightY()); fd.setDescent(type1.getFontBBox().getLowerLeftY()); fd.setCapHeight(type1.getBlueValues().get(2).floatValue()); fd.setStemV(0); // for PDF/A return fd; } /** * Returns a PDFontDescriptor for the given AFM. Used only for Standard 14 fonts. * * @param metrics AFM */ static PDFontDescriptor buildFontDescriptor(FontMetrics metrics) { boolean isSymbolic = metrics.getEncodingScheme().equals("FontSpecific"); PDFontDescriptor fd = new PDFontDescriptor(); fd.setFontName(metrics.getFontName()); fd.setFontFamily(metrics.getFamilyName()); fd.setNonSymbolic(!isSymbolic); fd.setSymbolic(isSymbolic); fd.setFontBoundingBox(new PDRectangle(metrics.getFontBBox())); fd.setItalicAngle(metrics.getItalicAngle()); fd.setAscent(metrics.getAscender()); fd.setDescent(metrics.getDescender()); fd.setCapHeight(metrics.getCapHeight()); fd.setXHeight(metrics.getXHeight()); fd.setAverageWidth(metrics.getAverageCharacterWidth()); fd.setCharacterSet(metrics.getCharacterSet()); fd.setStemV(0); // for PDF/A return fd; } /** * Returns the font's encoding. */ public Encoding getFontEncoding() { return fontEncoding; } /** * Returns the font's glyph list. */ public GlyphList getGlyphList() { return GlyphList.getAdobeGlyphList(); } /** * Returns the Type 1 font. */ public Type1Font getType1Font() { return type1; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDType3CharProc.java000066400000000000000000000135161320103431700270050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.contentstream.PDContentStream; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.input.ContentStreamParser; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A Type 3 character procedure. This is a standalone PDF content stream. * * @author John Hewson */ public final class PDType3CharProc implements COSObjectable, PDContentStream { private static final Logger LOG = LoggerFactory.getLogger(PDType3CharProc.class); private final PDType3Font font; private final COSStream charStream; public PDType3CharProc(PDType3Font font, COSStream charStream) { this.font = font; this.charStream = charStream; } @Override public COSStream getCOSObject() { return charStream; } public PDType3Font getFont() { return font; } public PDStream getContentStream() { return new PDStream(charStream); } @Override public InputStream getContents() throws IOException { return charStream.getUnfilteredStream(); } @Override public PDResources getResources() { return font.getResources(); } @Override public PDRectangle getBBox() { return font.getFontBBox(); } /** * Calculate the bounding box of this glyph. This will work only if the first operator in the stream is d1. * * @return the bounding box of this glyph, or null if the first operator is not d1. */ public PDRectangle getGlyphBBox() { List arguments = new ArrayList<>(); try { ContentStreamParser parser = new ContentStreamParser(this); Object token = null; while ((token = parser.nextParsedToken()) != null) { if (token instanceof Operator) { if (((Operator) token).getName().equals("d1") && arguments.size() == 6) { for (int i = 0; i < 6; ++i) { if (!(arguments.get(i) instanceof COSNumber)) { return null; } } return new PDRectangle(((COSNumber) arguments.get(2)).floatValue(), ((COSNumber) arguments.get(3)).floatValue(), ((COSNumber) arguments.get(4)).floatValue() - ((COSNumber) arguments.get(2)).floatValue(), ((COSNumber) arguments.get(5)).floatValue() - ((COSNumber) arguments.get(3)).floatValue()); } return null; } arguments.add((COSBase) token); } } catch (IOException e) { LOG.warn("An error occured while calculating the glyph bbox", e); } return null; } @Override public Matrix getMatrix() { return font.getFontMatrix(); } /** * Get the width from a type3 charproc stream. * * @return the glyph width. * @throws IOException if the stream could not be read, or did not have d0 or d1 as first operator, or if their * first argument was not a number. */ public float getWidth() throws IOException { List arguments = new ArrayList<>(); ContentStreamParser parser = new ContentStreamParser(this); Object token = null; while ((token = parser.nextParsedToken()) != null) { if (token instanceof Operator) { return parseWidth((Operator) token, arguments); } arguments.add(((COSBase) token).getCOSObject()); } throw new IOException("Unexpected end of stream"); } private float parseWidth(Operator operator, List arguments) throws IOException { if (operator.getName().equals("d0") || operator.getName().equals("d1")) { Object obj = arguments.get(0); if (obj instanceof Number) { return ((Number) obj).floatValue(); } else if (obj instanceof COSNumber) { return ((COSNumber) obj).floatValue(); } else { throw new IOException("Unexpected argument type: " + obj.getClass().getName()); } } throw new IOException("First operator must be d0 or d1"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDType3Font.java000066400000000000000000000231371320103431700262120ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import java.awt.geom.GeneralPath; import java.io.IOException; import java.io.InputStream; import org.apache.fontbox.FontBoxFont; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.font.encoding.DictionaryEncoding; import org.sejda.sambox.pdmodel.font.encoding.Encoding; import org.sejda.sambox.pdmodel.font.encoding.GlyphList; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.Vector; /** * A PostScript Type 3 Font. * * @author Ben Litchfield */ public class PDType3Font extends PDSimpleFont { private PDResources resources; private COSDictionary charProcs; private Matrix fontMatrix; private BoundingBox fontBBox; public PDType3Font(COSDictionary fontDictionary) throws IOException { super(fontDictionary); readEncoding(); } @Override public String getName() { return dict.getNameAsString(COSName.NAME); } @Override protected final void readEncoding() { encoding = new DictionaryEncoding( dict.getDictionaryObject(COSName.ENCODING, COSDictionary.class)); glyphList = GlyphList.getAdobeGlyphList(); } @Override protected Encoding readEncodingFromFont() { // Type 3 fonts do not have a built-in encoding throw new UnsupportedOperationException("not supported for Type 3 fonts"); } @Override protected Boolean isFontSymbolic() { return false; } @Override public GeneralPath getPath(String name) { // Type 3 fonts do not use vector paths throw new UnsupportedOperationException("not supported for Type 3 fonts"); } @Override public boolean hasGlyph(String name) { return nonNull( getCharProcs().getDictionaryObject(COSName.getPDFName(name), COSStream.class)); } @Override public FontBoxFont getFontBoxFont() { // Type 3 fonts do not use FontBox fonts throw new UnsupportedOperationException("not supported for Type 3 fonts"); } @Override public Vector getDisplacement(int code) throws IOException { return getFontMatrix().transform(new Vector(getWidth(code), 0)); } @Override public float getWidth(int code) throws IOException { int firstChar = dict.getInt(COSName.FIRST_CHAR, -1); int lastChar = dict.getInt(COSName.LAST_CHAR, -1); if (!getWidths().isEmpty() && code >= firstChar && code <= lastChar) { return ofNullable(getWidths().get(code - firstChar)).orElse(0f); } PDFontDescriptor fd = getFontDescriptor(); if (nonNull(fd)) { return fd.getMissingWidth(); } return getWidthFromFont(code); } @Override public float getWidthFromFont(int code) throws IOException { PDType3CharProc charProc = getCharProc(code); if (nonNull(charProc)) { return charProc.getWidth(); } return 0; } @Override public boolean isEmbedded() { return true; } @Override public float getHeight(int code) { PDFontDescriptor desc = getFontDescriptor(); if (desc != null) { // the following values are all more or less accurate at least all are average // values. Maybe we'll find another way to get those value for every single glyph // in the future if needed PDRectangle bbox = desc.getFontBoundingBox(); float retval = 0; if (bbox != null) { retval = bbox.getHeight() / 2; } if (retval == 0) { retval = desc.getCapHeight(); } if (retval == 0) { retval = desc.getAscent(); } if (retval == 0) { retval = desc.getXHeight(); if (retval > 0) { retval -= desc.getDescent(); } } return retval; } return 0; } @Override protected byte[] encode(int unicode) { throw new UnsupportedOperationException("Not implemented: Type3"); } @Override public int readCode(InputStream in) throws IOException { return in.read(); } @Override public Matrix getFontMatrix() { if (fontMatrix == null) { COSArray array = dict.getDictionaryObject(COSName.FONT_MATRIX, COSArray.class); if (nonNull(array)) { fontMatrix = new Matrix(array); } else { return super.getFontMatrix(); } } return fontMatrix; } @Override public boolean isDamaged() { // there's no font file to load return false; } /** * Returns the optional resources of the type3 stream. * * @return the resources bound to be used when parsing the type3 stream */ public PDResources getResources() { if (resources == null) { COSDictionary resourcesDict = dict.getDictionaryObject(COSName.RESOURCES, COSDictionary.class); if (resourcesDict != null) { this.resources = new PDResources(resourcesDict); } } return resources; } /** * This will get the fonts bounding box from its dictionary. * * @return The fonts bounding box. */ public PDRectangle getFontBBox() { COSArray rect = (COSArray) dict.getDictionaryObject(COSName.FONT_BBOX); if (nonNull(rect)) { return new PDRectangle(rect); } return null; } @Override public BoundingBox getBoundingBox() { if (fontBBox == null) { fontBBox = generateBoundingBox(); } return fontBBox; } private BoundingBox generateBoundingBox() { PDRectangle rect = getFontBBox(); if (rect.getLowerLeftX() == 0 && rect.getLowerLeftY() == 0 && rect.getUpperRightX() == 0 && rect.getUpperRightY() == 0) { // Plan B: get the max bounding box of the glyphs COSDictionary cp = getCharProcs(); for (COSName name : cp.keySet()) { COSBase base = cp.getDictionaryObject(name); if (base instanceof COSStream) { PDType3CharProc charProc = new PDType3CharProc(this, (COSStream) base); PDRectangle glyphBBox = charProc.getGlyphBBox(); if (nonNull(glyphBBox)) { rect.setLowerLeftX( Math.min(rect.getLowerLeftX(), glyphBBox.getLowerLeftX())); rect.setLowerLeftY( Math.min(rect.getLowerLeftY(), glyphBBox.getLowerLeftY())); rect.setUpperRightX( Math.max(rect.getUpperRightX(), glyphBBox.getUpperRightX())); rect.setUpperRightY( Math.max(rect.getUpperRightY(), glyphBBox.getUpperRightY())); } } } } return new BoundingBox(rect.getLowerLeftX(), rect.getLowerLeftY(), rect.getUpperRightX(), rect.getUpperRightY()); } /** * Returns the dictionary containing all streams to be used to render the glyphs. * * @return the dictionary containing all glyph streams. */ public COSDictionary getCharProcs() { if (charProcs == null) { charProcs = dict.getDictionaryObject(COSName.CHAR_PROCS, COSDictionary.class); } return charProcs; } /** * Returns the stream of the glyph for the given character code * * @param code character code * @return the stream to be used to render the glyph */ public PDType3CharProc getCharProc(int code) { String name = getEncoding().getName(code); if (!".notdef".equals(name)) { COSStream stream = getCharProcs().getDictionaryObject(COSName.getPDFName(name), COSStream.class); if (nonNull(stream)) { return new PDType3CharProc(this, stream); } } return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/PDVectorFont.java000066400000000000000000000027751320103431700264550ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.awt.geom.GeneralPath; import java.io.IOException; /** * A vector outline font, e.g. not Type 3. * * @author John Hewson */ public interface PDVectorFont { /** * Returns the glyph path for the given character code. * * @param code character code in a PDF. Not to be confused with unicode. * @throws java.io.IOException if the font could not be read */ GeneralPath getPath(int code) throws IOException; /** * Returns true if this font contains a glyph for the given character code. * * @param code character code in a PDF. Not to be confused with unicode. */ boolean hasGlyph(int code) throws IOException; } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/Standard14Fonts.java000066400000000000000000000127611320103431700270530ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.fontbox.afm.AFMParser; import org.apache.fontbox.afm.FontMetrics; /** * The "Standard 14" PDF fonts, also known as the "base 14" fonts. There are 14 font files, but Acrobat uses additional * names for compatibility, e.g. Arial. * * @author John Hewson */ final class Standard14Fonts { private Standard14Fonts() { } private static final Set STANDARD_14_NAMES = new HashSet(); private static final Map STANDARD_14_MAPPING = new HashMap(); private static final Map STANDARD14_AFM_MAP; static { try { STANDARD14_AFM_MAP = new HashMap(); addAFM("Courier-Bold"); addAFM("Courier-BoldOblique"); addAFM("Courier"); addAFM("Courier-Oblique"); addAFM("Helvetica"); addAFM("Helvetica-Bold"); addAFM("Helvetica-BoldOblique"); addAFM("Helvetica-Oblique"); addAFM("Symbol"); addAFM("Times-Bold"); addAFM("Times-BoldItalic"); addAFM("Times-Italic"); addAFM("Times-Roman"); addAFM("ZapfDingbats"); // alternative names from Adobe Supplement to the ISO 32000 addAFM("CourierCourierNew", "Courier"); addAFM("CourierNew", "Courier"); addAFM("CourierNew,Italic", "Courier-Oblique"); addAFM("CourierNew,Bold", "Courier-Bold"); addAFM("CourierNew,BoldItalic", "Courier-BoldOblique"); addAFM("Arial", "Helvetica"); addAFM("Arial,Italic", "Helvetica-Oblique"); addAFM("Arial,Bold", "Helvetica-Bold"); addAFM("Arial,BoldItalic", "Helvetica-BoldOblique"); addAFM("TimesNewRoman", "Times-Roman"); addAFM("TimesNewRoman,Italic", "Times-Italic"); addAFM("TimesNewRoman,Bold", "Times-Bold"); addAFM("TimesNewRoman,BoldItalic", "Times-BoldItalic"); // Acrobat treats these fonts as "standard 14" too (at least Acrobat preflight says so) addAFM("Symbol,Italic", "Symbol"); addAFM("Symbol,Bold", "Symbol"); addAFM("Symbol,BoldItalic", "Symbol"); addAFM("Times", "Times-Roman"); addAFM("Times,Italic", "Times-Italic"); addAFM("Times,Bold", "Times-Bold"); addAFM("Times,BoldItalic", "Times-BoldItalic"); } catch (IOException e) { throw new RuntimeException(e); } } private static void addAFM(String fontName) throws IOException { addAFM(fontName, fontName); } private static void addAFM(String fontName, String afmName) throws IOException { STANDARD_14_NAMES.add(fontName); STANDARD_14_MAPPING.put(fontName, afmName); if (STANDARD14_AFM_MAP.containsKey(afmName)) { STANDARD14_AFM_MAP.put(fontName, STANDARD14_AFM_MAP.get(afmName)); } String resourceName = "org/sejda/sambox/resources/afm/" + afmName + ".afm"; URL url = PDType1Font.class.getClassLoader().getResource(resourceName); if (url != null) { try (InputStream afmStream = url.openStream()) { AFMParser parser = new AFMParser(afmStream); FontMetrics metric = parser.parse(true); STANDARD14_AFM_MAP.put(fontName, metric); } } else { throw new IOException(resourceName + " not found"); } } /** * Returns the AFM for the given font. * * @param baseName base name of font */ public static FontMetrics getAFM(String baseName) { return STANDARD14_AFM_MAP.get(baseName); } /** * Returns true if the given font name a Standard 14 font. * * @param baseName base name of font */ public static boolean containsName(String baseName) { return STANDARD_14_NAMES.contains(baseName); } /** * Returns the set of Standard 14 font names, including additional names. */ public static Set getNames() { return Collections.unmodifiableSet(STANDARD_14_NAMES); } /** * Returns the name of the actual font which the given font name maps to. * * @param baseName base name of font */ public static String getMappedFontName(String baseName) { return STANDARD_14_MAPPING.get(baseName); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/Subsetter.java000066400000000000000000000023421320103431700261060ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.IOException; /** * Interface for a font subsetter. */ interface Subsetter { /** * Adds the given Unicode code point to this subset. * * @param codePoint Unicode code point */ void addToSubset(int codePoint); /** * Subset this font now. * * @throws IOException if the font could not be read */ void subset() throws IOException; } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/ToUnicodeWriter.java000066400000000000000000000135061320103431700272200ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.sejda.sambox.util.Hex; /** * Writes ToUnicode Mapping Files. * * @author John Hewson */ final class ToUnicodeWriter { private final Map cidToUnicode = new TreeMap(); private int wMode; /** * Creates a new ToUnicode CMap writer. */ ToUnicodeWriter() { this.wMode = 0; } /** * Sets the WMode (writing mode) of this CMap. * * @param wMode 1 for vertical, 0 for horizontal (default) */ public void setWMode(int wMode) { this.wMode = wMode; } /** * Adds the given CID to Unicode mapping. * * @param cid CID * @param text Unicode text, up to 512 bytes. */ public void add(int cid, String text) { if (cid < 0 || cid > 0xFFFF) { throw new IllegalArgumentException("CID is not valid"); } if (text == null || text.isEmpty()) { throw new IllegalArgumentException("Text is null or empty"); } cidToUnicode.put(cid, text); } /** * Writes the CMap as ASCII to the given output stream. * * @param out ASCII output stream * @throws IOException if the stream could not be written */ public void writeTo(OutputStream out) throws IOException { BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(out, StandardCharsets.US_ASCII)); writeLine(writer, "/CIDInit /ProcSet findresource begin"); writeLine(writer, "12 dict begin\n"); writeLine(writer, "begincmap"); writeLine(writer, "/CIDSystemInfo"); writeLine(writer, "<< /Registry (Adobe)"); writeLine(writer, "/Ordering (UCS)"); writeLine(writer, "/Supplement 0"); writeLine(writer, ">> def\n"); writeLine(writer, "/CMapName /Adobe-Identity-UCS" + " def"); writeLine(writer, "/CMapType 2 def\n"); // 2 = ToUnicode if (wMode != 0) { writeLine(writer, "/WMode /" + wMode + " def"); } // ToUnicode always uses 16-bit CIDs writeLine(writer, "1 begincodespacerange"); writeLine(writer, "<0000> "); writeLine(writer, "endcodespacerange\n"); // CID -> Unicode mappings, we use ranges to generate a smaller CMap List srcFrom = new ArrayList(); List srcTo = new ArrayList(); List dstString = new ArrayList(); int srcPrev = -1; String dstPrev = null; int srcCode1 = -1; for (Map.Entry entry : cidToUnicode.entrySet()) { int cid = entry.getKey(); String text = entry.getValue(); if (cid == srcPrev + 1 && // CID must be last CID + 1 dstPrev.codePointCount(0, dstPrev.length()) == 1 && // no UTF-16 surrogates text.codePointAt(0) == dstPrev.codePointAt(0) + 1 && // dstString must be prev + 1 dstPrev.codePointAt(0) + 1 <= 255 - (cid - srcCode1)) // increment last byte only { // extend range srcTo.set(srcTo.size() - 1, cid); } else { // begin range srcCode1 = cid; srcFrom.add(cid); srcTo.add(cid); dstString.add(text); } srcPrev = cid; dstPrev = text; } // limit of 100 entries per operator int batchCount = (int)Math.ceil(srcFrom.size() / 100.0); for (int batch = 0; batch < batchCount; batch++) { int count = batch == batchCount - 1 ? srcFrom.size() % 100 : 100; writer.write(count + " beginbfrange\n"); for (int j = 0; j < count; j++) { int index = batch * 100 + j; writer.write('<'); writer.write(Hex.getChars(srcFrom.get(index).shortValue())); writer.write("> "); writer.write('<'); writer.write(Hex.getChars(srcTo.get(index).shortValue())); writer.write("> "); writer.write('<'); writer.write(Hex.getCharsUTF16BE(dstString.get(index))); writer.write(">\n"); } writeLine(writer, "endbfrange\n"); } // footer writeLine(writer, "endcmap"); writeLine(writer, "CMapName currentdict /CMap defineresource pop"); writeLine(writer, "end"); writeLine(writer, "end"); writer.flush(); } private void writeLine(BufferedWriter writer, String text) throws IOException { writer.write(text); writer.write('\n'); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/TrueTypeEmbedder.java000066400000000000000000000263021320103431700273410ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font; import java.awt.geom.GeneralPath; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.fontbox.ttf.CmapSubtable; import org.apache.fontbox.ttf.HeaderTable; import org.apache.fontbox.ttf.HorizontalHeaderTable; import org.apache.fontbox.ttf.OS2WindowsMetricsTable; import org.apache.fontbox.ttf.PostScriptTable; import org.apache.fontbox.ttf.TTFParser; import org.apache.fontbox.ttf.TTFSubsetter; import org.apache.fontbox.ttf.TrueTypeFont; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.util.IOUtils; /** * Common functionality for embedding TrueType fonts. * * @author Ben Litchfield * @author John Hewson */ abstract class TrueTypeEmbedder implements Subsetter { private static final int ITALIC = 1; private static final int OBLIQUE = 512; private static final String BASE25 = "BCDEFGHIJKLMNOPQRSTUVWXYZ"; protected TrueTypeFont ttf; protected PDFontDescriptor fontDescriptor; protected final CmapSubtable cmap; private final Set subsetCodePoints = new HashSet<>(); private final boolean embedSubset; /** * Creates a new TrueType font for embedding. */ TrueTypeEmbedder(COSDictionary dict, TrueTypeFont ttf, boolean embedSubset) throws IOException { this.embedSubset = embedSubset; this.ttf = ttf; fontDescriptor = createFontDescriptor(ttf); if (!isEmbeddingPermitted(ttf)) { throw new IOException("This font does not permit embedding"); } if (!embedSubset) { // full embedding PDStream stream = new PDStream(ttf.getOriginalData(), COSName.FLATE_DECODE); stream.getCOSObject().setLong(COSName.LENGTH1, ttf.getOriginalDataSize()); fontDescriptor.setFontFile2(stream); } dict.setName(COSName.BASE_FONT, ttf.getName()); // choose a Unicode "cmap" cmap = ttf.getUnicodeCmap(); } public void buildFontFile2(InputStream ttfStream) throws IOException { PDStream stream = new PDStream(ttfStream, COSName.FLATE_DECODE); // as the stream was closed within the PDStream constructor, we have to recreate it InputStream input = null; try { input = stream.createInputStream(); ttf = new TTFParser().parseEmbedded(input); if (!isEmbeddingPermitted(ttf)) { throw new IOException("This font does not permit embedding"); } if (fontDescriptor == null) { fontDescriptor = createFontDescriptor(ttf); } } finally { IOUtils.closeQuietly(input); } stream.getCOSObject().setLong(COSName.LENGTH1, ttf.getOriginalDataSize()); fontDescriptor.setFontFile2(stream); } /** * Returns true if the fsType in the OS/2 table permits embedding. */ private boolean isEmbeddingPermitted(TrueTypeFont ttf) throws IOException { if (ttf.getOS2Windows() != null) { int fsType = ttf.getOS2Windows().getFsType(); int exclusive = fsType & 0x8; // bits 0-3 are a set of exclusive bits if ((exclusive & OS2WindowsMetricsTable.FSTYPE_RESTRICTED) == OS2WindowsMetricsTable.FSTYPE_RESTRICTED) { // restricted License embedding return false; } else if ((exclusive & OS2WindowsMetricsTable.FSTYPE_BITMAP_ONLY) == OS2WindowsMetricsTable.FSTYPE_BITMAP_ONLY) { // bitmap embedding only return false; } } return true; } /** * Returns true if the fsType in the OS/2 table permits subsetting. */ private boolean isSubsettingPermitted(TrueTypeFont ttf) throws IOException { if (ttf.getOS2Windows() != null) { int fsType = ttf.getOS2Windows().getFsType(); if ((fsType & OS2WindowsMetricsTable.FSTYPE_NO_SUBSETTING) == OS2WindowsMetricsTable.FSTYPE_NO_SUBSETTING) { return false; } } return true; } /** * Creates a new font descriptor dictionary for the given TTF. */ private PDFontDescriptor createFontDescriptor(TrueTypeFont ttf) throws IOException { PDFontDescriptor fd = new PDFontDescriptor(); fd.setFontName(ttf.getName()); OS2WindowsMetricsTable os2 = ttf.getOS2Windows(); PostScriptTable post = ttf.getPostScript(); // Flags fd.setFixedPitch( post.getIsFixedPitch() > 0 || ttf.getHorizontalHeader().getNumberOfHMetrics() == 1); int fsSelection = os2.getFsSelection(); fd.setItalic(((fsSelection & (ITALIC | OBLIQUE)) != 0)); switch (os2.getFamilyClass()) { case OS2WindowsMetricsTable.FAMILY_CLASS_CLAREDON_SERIFS: case OS2WindowsMetricsTable.FAMILY_CLASS_FREEFORM_SERIFS: case OS2WindowsMetricsTable.FAMILY_CLASS_MODERN_SERIFS: case OS2WindowsMetricsTable.FAMILY_CLASS_OLDSTYLE_SERIFS: case OS2WindowsMetricsTable.FAMILY_CLASS_SLAB_SERIFS: fd.setSerif(true); break; case OS2WindowsMetricsTable.FAMILY_CLASS_SCRIPTS: fd.setScript(true); break; default: break; } fd.setFontWeight(os2.getWeightClass()); fd.setSymbolic(true); fd.setNonSymbolic(false); // ItalicAngle fd.setItalicAngle(post.getItalicAngle()); // FontBBox HeaderTable header = ttf.getHeader(); PDRectangle rect = new PDRectangle(); float scaling = 1000f / header.getUnitsPerEm(); rect.setLowerLeftX(header.getXMin() * scaling); rect.setLowerLeftY(header.getYMin() * scaling); rect.setUpperRightX(header.getXMax() * scaling); rect.setUpperRightY(header.getYMax() * scaling); fd.setFontBoundingBox(rect); // Ascent, Descent HorizontalHeaderTable hHeader = ttf.getHorizontalHeader(); fd.setAscent(hHeader.getAscender() * scaling); fd.setDescent(hHeader.getDescender() * scaling); // CapHeight, XHeight if (os2.getVersion() >= 1.2) { fd.setCapHeight(os2.getCapHeight() * scaling); fd.setXHeight(os2.getHeight() * scaling); } else { GeneralPath capHPath = ttf.getPath("H"); if (capHPath != null) { fd.setCapHeight(Math.round(capHPath.getBounds2D().getMaxY()) * scaling); } else { // estimate by summing the typographical +ve ascender and -ve descender fd.setCapHeight((os2.getTypoAscender() + os2.getTypoDescender()) * scaling); } GeneralPath xPath = ttf.getPath("x"); if (xPath != null) { fd.setXHeight(Math.round(xPath.getBounds2D().getMaxY()) * scaling); } else { // estimate by halving the typographical ascender fd.setXHeight(os2.getTypoAscender() / 2.0f * scaling); } } // StemV - there's no true TTF equivalent of this, so we estimate it fd.setStemV(fd.getFontBoundingBox().getWidth() * .13f); return fd; } /** * Returns the FontBox font. */ @Deprecated public TrueTypeFont getTrueTypeFont() { return ttf; } /** * Returns the font descriptor. */ public PDFontDescriptor getFontDescriptor() { return fontDescriptor; } @Override public void addToSubset(int codePoint) { subsetCodePoints.add(codePoint); } @Override public void subset() throws IOException { if (!isSubsettingPermitted(ttf)) { throw new IOException("This font does not permit subsetting"); } if (!embedSubset) { throw new IllegalStateException("Subsetting is disabled"); } // PDF spec required tables (if present), all others will be removed List tables = new ArrayList(); tables.add("head"); tables.add("hhea"); tables.add("loca"); tables.add("maxp"); tables.add("cvt "); tables.add("prep"); tables.add("glyf"); tables.add("hmtx"); tables.add("fpgm"); // Windows ClearType tables.add("gasp"); // set the GIDs to subset TTFSubsetter subsetter = new TTFSubsetter(ttf, tables); subsetter.addAll(subsetCodePoints); // calculate deterministic tag based on the chosen subset Map gidToCid = subsetter.getGIDMap(); String tag = getTag(gidToCid); subsetter.setPrefix(tag); // save the subset font ByteArrayOutputStream out = new ByteArrayOutputStream(); subsetter.writeToStream(out); // re-build the embedded font buildSubset(new ByteArrayInputStream(out.toByteArray()), tag, gidToCid); ttf.close(); } /** * Returns true if the font needs to be subset. */ public boolean needsSubset() { return embedSubset; } /** * Rebuild a font subset. */ protected abstract void buildSubset(InputStream ttfSubset, String tag, Map gidToCid) throws IOException; /** * Returns an uppercase 6-character unique tag for the given subset. */ public String getTag(Map gidToCid) { // deterministic long num = gidToCid.hashCode(); // base25 encode StringBuilder sb = new StringBuilder(); do { long div = num / 25; int mod = (int) (num % 25); sb.append(BASE25.charAt(mod)); num = div; } while (num != 0 && sb.length() < 6); // pad while (sb.length() < 6) { sb.insert(0, 'A'); } sb.append('+'); return sb.toString(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/UniUtil.java000066400000000000000000000024101320103431700255130ustar00rootroot00000000000000/* * Copyright 2015 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.font; import java.util.Locale; /** * Utility class for Unicode fallback. * * @author Philip Helger */ final class UniUtil { private UniUtil() { } // faster than String.format("uni%04X", codePoint) static String getUniNameOfCodePoint(int codePoint) { String hex = Integer.toString(codePoint, 16).toUpperCase(Locale.US); switch (hex.length()) { case 1: return "uni000" + hex; case 2: return "uni00" + hex; case 3: return "uni0" + hex; default: return "uni" + hex; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/000077500000000000000000000000001320103431700250505ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/BuiltInEncoding.java000066400000000000000000000030671320103431700307360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import java.util.Map; import org.sejda.sambox.cos.COSBase; /** * A font's built-in encoding. * * @author John Hewson */ public class BuiltInEncoding extends Encoding { /** * Constructor. * * @param codeToName the given code to name mapping */ public BuiltInEncoding(Map codeToName) { for (Map.Entry entry : codeToName.entrySet()) { add(entry.getKey(), entry.getValue()); } } @Override public COSBase getCOSObject() { throw new UnsupportedOperationException("Built-in encodings cannot be serialized"); } @Override public String getEncodingName() { return "built-in (TTF)"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/DictionaryEncoding.java000066400000000000000000000133431320103431700314730ustar00rootroot00000000000000/* /* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; /** * This will perform the encoding from a dictionary. * * @author Ben Litchfield */ public class DictionaryEncoding extends Encoding { private final COSDictionary encoding; private final Encoding baseEncoding; private final Map differences = new HashMap<>(); /** * Creates a new DictionaryEncoding for embedding. */ public DictionaryEncoding(COSName baseEncoding, COSArray differences) { encoding = new COSDictionary(); encoding.setItem(COSName.NAME, COSName.ENCODING); encoding.setItem(COSName.DIFFERENCES, differences); if (baseEncoding != COSName.STANDARD_ENCODING) { encoding.setItem(COSName.BASE_ENCODING, baseEncoding); this.baseEncoding = Encoding.getInstance(baseEncoding); } else { this.baseEncoding = Encoding.getInstance(baseEncoding); } if (this.baseEncoding == null) { throw new IllegalArgumentException("Invalid encoding: " + baseEncoding); } codeToName.putAll(this.baseEncoding.codeToName); inverted.putAll(this.baseEncoding.inverted); applyDifferences(); } /** * Creates a new DictionaryEncoding for a Type 3 font from a PDF. * * @param fontEncoding The Type 3 encoding dictionary. */ public DictionaryEncoding(COSDictionary fontEncoding) { encoding = fontEncoding; baseEncoding = null; applyDifferences(); } /** * Creates a new DictionaryEncoding from a PDF. * * @param fontEncoding The encoding dictionary. * @param isNonSymbolic True if the font is non-symbolic. False for Type 3 fonts. * @param builtIn The font's built-in encoding. Null for Type 3 fonts. */ public DictionaryEncoding(COSDictionary fontEncoding, boolean isNonSymbolic, Encoding builtIn) { encoding = fontEncoding; Encoding base = null; boolean hasBaseEncoding = encoding.containsKey(COSName.BASE_ENCODING); if (hasBaseEncoding) { COSName name = encoding.getCOSName(COSName.BASE_ENCODING); base = Encoding.getInstance(name); // null when the name is invalid } if (base == null) { if (isNonSymbolic) { // Otherwise, for a nonsymbolic font, it is StandardEncoding base = StandardEncoding.INSTANCE; } else { // and for a symbolic font, it is the font's built-in encoding. if (builtIn != null) { base = builtIn; } else { // triggering this error indicates a bug in PDFBox. Every font should always have // a built-in encoding, if not, we parsed it incorrectly. throw new IllegalArgumentException( "Symbolic fonts must have a built-in " + "encoding"); } } } baseEncoding = base; codeToName.putAll(baseEncoding.codeToName); inverted.putAll(baseEncoding.inverted); applyDifferences(); } private void applyDifferences() { COSArray differences = ofNullable(encoding) .map(e -> e.getDictionaryObject(COSName.DIFFERENCES, COSArray.class)).orElse(null); if (nonNull(differences)) { int currentIndex = -1; for (int i = 0; i < differences.size(); i++) { COSBase next = differences.getObject(i); if (next instanceof COSNumber) { currentIndex = ((COSNumber) next).intValue(); } else if (next instanceof COSName) { COSName name = (COSName) next; overwrite(currentIndex, name.getName()); this.differences.put(currentIndex, name.getName()); currentIndex++; } } } } /** * Returns the base encoding. Will be null for Type 3 fonts. */ public Encoding getBaseEncoding() { return baseEncoding; } /** * Returns the Differences array. */ public Map getDifferences() { return differences; } @Override public COSBase getCOSObject() { return encoding; } @Override public String getEncodingName() { return baseEncoding.getEncodingName() + " with differences"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/Encoding.java000066400000000000000000000126241320103431700274460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * A PostScript encoding vector, maps character codes to glyph names. * * @author Ben Litchfield */ public abstract class Encoding implements COSObjectable { /** * This will get an encoding by name. May return null. * * @param name The name of the encoding to get. * @return The encoding that matches the name. */ public static Encoding getInstance(COSName name) { if (COSName.STANDARD_ENCODING.equals(name)) { return StandardEncoding.INSTANCE; } else if (COSName.WIN_ANSI_ENCODING.equals(name)) { return WinAnsiEncoding.INSTANCE; } else if (COSName.MAC_ROMAN_ENCODING.equals(name)) { return MacRomanEncoding.INSTANCE; } else if (COSName.MAC_EXPERT_ENCODING.equals(name)) { return MacExpertEncoding.INSTANCE; } return null; } protected final Map codeToName = new HashMap<>(250); protected final Map inverted = new HashMap<>(250); private Set names; /** * Returns an unmodifiable view of the code -> name mapping. * * @return the code -> name map */ public Map getCodeToNameMap() { return Collections.unmodifiableMap(codeToName); } /** * Returns an unmodifiable view of the name -> code mapping. More than one name may map to the same code. * * @return the name -> code map */ public Map getNameToCodeMap() { return Collections.unmodifiableMap(inverted); } /** * This will add a character encoding. An already existing mapping is preservered when creating the reverse mapping. * * @see #overwrite(int, String) * * @param code character code * @param name PostScript glyph name */ protected void add(int code, String name) { codeToName.put(code, name); if (!inverted.containsKey(name)) { inverted.put(name, code); } } /** * This will add a character encoding. An already existing mapping is overwritten when creating the reverse mapping. * * @see Encoding#add(int, String) * * @param code character code * @param name PostScript glyph name */ protected void overwrite(int code, String name) { // remove existing reverse mapping first String oldName = codeToName.get(code); if (oldName != null) { Integer oldCode = inverted.get(oldName); if (oldCode != null && oldCode == code) { inverted.remove(oldName); } } inverted.put(name, code); codeToName.put(code, name); } /** * Determines if the encoding has a mapping for the given name value. * * @param name PostScript glyph name */ public boolean contains(String name) { // we have to wait until all add() calls are done before building the name cache // otherwise /Differences won't be accounted for if (names == null) { synchronized (this) { // PDFBOX-3404: avoid possibility that one thread ends up with newly created empty map from other thread Set tmpSet = new HashSet<>(codeToName.values()); // make sure that assignment is done after initialisation is complete names = tmpSet; // note that it might still happen that 'names' is initialized twice, but this is harmless } // at this point, names will never be null. } return names.contains(name); } /** * Determines if the encoding has a mapping for the given code value. * * @param code character code */ public boolean contains(int code) { return codeToName.containsKey(code); } /** * This will take a character code and get the name from the code. * * @param code character code * @return PostScript glyph name */ public String getName(int code) { String name = codeToName.get(code); if (name != null) { return name; } return ".notdef"; } /** * Returns the name of this encoding. */ public abstract String getEncodingName(); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/GlyphList.java000066400000000000000000000234111320103431700276330ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * PostScript glyph list, maps glyph names to sequences of Unicode characters. Instances of GlyphList are immutable. */ public final class GlyphList { private static final Logger LOG = LoggerFactory.getLogger(GlyphList.class); // Adobe Glyph List (AGL) private static final GlyphList DEFAULT = load("glyphlist.txt", 4281); // Zapf Dingbats has its own glyph list private static final GlyphList ZAPF_DINGBATS = load("zapfdingbats.txt", 201); /** * Loads a glyph list from disk. */ private static GlyphList load(String filename, int numberOfEntries) { ClassLoader loader = GlyphList.class.getClassLoader(); String path = "org/sejda/sambox/resources/glyphlist/"; try { return new GlyphList(loader.getResourceAsStream(path + filename), numberOfEntries); } catch (IOException e) { throw new RuntimeException(e); } } static { // not supported in PDFBox 2.0, but we issue a warning, see PDFBOX-2379 try { String location = System.getProperty("glyphlist_ext"); if (location != null) { throw new UnsupportedOperationException("glyphlist_ext is no longer supported, " + "use GlyphList.DEFAULT.addGlyphs(Properties) instead"); } } catch (SecurityException e) // can occur on System.getProperty { // PDFBOX-1946 ignore and continue } } /** * Returns the Adobe Glyph List (AGL). */ public static GlyphList getAdobeGlyphList() { return DEFAULT; } /** * Returns the Zapf Dingbats glyph list. */ public static GlyphList getZapfDingbats() { return ZAPF_DINGBATS; } // read-only mappings, never modified outside GlyphList's constructor private final Map nameToUnicode; private final Map unicodeToName; // additional read/write cache for uniXXXX names private final Map uniNameToUnicodeCache = new HashMap<>(); /** * Creates a new GlyphList from a glyph list file. * * @param input glyph list in Adobe format * @throws IOException if the glyph list could not be read */ public GlyphList(InputStream input, int numberOfEntries) throws IOException { nameToUnicode = new HashMap<>(numberOfEntries); unicodeToName = new HashMap<>(numberOfEntries); loadList(input); } /** * Creates a new GlyphList from multiple glyph list files. * * @param glyphList an existing glyph list to be copied * @param input glyph list in Adobe format * @throws IOException if the glyph list could not be read */ public GlyphList(GlyphList glyphList, InputStream input) throws IOException { nameToUnicode = new HashMap<>(glyphList.nameToUnicode); unicodeToName = new HashMap<>(glyphList.unicodeToName); loadList(input); } private void loadList(InputStream input) throws IOException { try (BufferedReader in = new BufferedReader(new InputStreamReader(input, "ISO-8859-1"))) { while (in.ready()) { String line = in.readLine(); if (line != null && !line.startsWith("#")) { String[] parts = line.split(";"); if (parts.length < 2) { throw new IOException("Invalid glyph list entry: " + line); } String name = parts[0]; String[] unicodeList = parts[1].split(" "); if (nameToUnicode.containsKey(name)) { LOG.warn("duplicate value for " + name + " -> " + parts[1] + " " + nameToUnicode.get(name)); } int[] codePoints = new int[unicodeList.length]; int index = 0; for (String hex : unicodeList) { codePoints[index++] = Integer.parseInt(hex, 16); } String string = new String(codePoints, 0, codePoints.length); // forward mapping nameToUnicode.put(name, string); // reverse mapping // PDFBOX-3884: take the various standard encodings as canonical, // e.g. tilde over ilde final boolean forceOverride = WinAnsiEncoding.INSTANCE.contains(name) || MacRomanEncoding.INSTANCE.contains(name) || MacExpertEncoding.INSTANCE.contains(name) || SymbolEncoding.INSTANCE.contains(name) || ZapfDingbatsEncoding.INSTANCE.contains(name); if (!unicodeToName.containsKey(string) || forceOverride) { unicodeToName.put(string, name); } } } } } /** * Returns the name for the given Unicode code point. * * @param codePoint Unicode code point * @return PostScript glyph name, or ".notdef" */ public String codePointToName(int codePoint) { String name = unicodeToName.get(new String(new int[] { codePoint }, 0, 1)); if (name == null) { return ".notdef"; } return name; } /** * Returns the name for a given sequence of Unicode characters. * * @param unicodeSequence sequence of Unicode characters * @return PostScript glyph name, or ".notdef" */ public String sequenceToName(String unicodeSequence) { String name = unicodeToName.get(unicodeSequence); if (name == null) { return ".notdef"; } return name; } /** * Returns the Unicode character sequence for the given glyph name, or null if there isn't any. * * @param name PostScript glyph name * @return Unicode character(s), or null. */ public String toUnicode(String name) { if (name == null) { return null; } String unicode = nameToUnicode.get(name); if (unicode != null) { return unicode; } // separate read/write cache for thread safety unicode = uniNameToUnicodeCache.get(name); if (unicode == null) { // test if we have a suffix and if so remove it if (name.indexOf('.') > 0) { unicode = toUnicode(name.substring(0, name.indexOf('.'))); } else if (name.startsWith("uni") && name.length() == 7) { // test for Unicode name in the format uniXXXX where X is hex int nameLength = name.length(); StringBuilder uniStr = new StringBuilder(); try { for (int chPos = 3; chPos + 4 <= nameLength; chPos += 4) { int codePoint = Integer.parseInt(name.substring(chPos, chPos + 4), 16); if (codePoint > 0xD7FF && codePoint < 0xE000) { LOG.warn("Unicode character name with disallowed code area: {} ", name); } else { uniStr.append((char) codePoint); } } unicode = uniStr.toString(); } catch (NumberFormatException nfe) { LOG.warn("Not a number in Unicode character name: {}", name); } } else if (name.startsWith("u") && name.length() == 5) { // test for an alternate Unicode name representation uXXXX try { int codePoint = Integer.parseInt(name.substring(1), 16); if (codePoint > 0xD7FF && codePoint < 0xE000) { LOG.warn("Unicode character name with disallowed code area: {}", name); } else { unicode = String.valueOf((char) codePoint); } } catch (NumberFormatException nfe) { LOG.warn("Not a number in Unicode character name: {}", name); } } uniNameToUnicodeCache.put(name, unicode); } return unicode; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/MacExpertEncoding.java000066400000000000000000000153371320103431700312630ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; /** * This is an interface to a text encoder. */ public class MacExpertEncoding extends Encoding { private static final int CHAR_CODE = 0; private static final int CHAR_NAME = 1; /** * Table of octal character codes and their corresponding names. */ private static final Object[][] MAC_EXPERT_ENCODING_TABLE = { {0276, "AEsmall"}, {0207, "Aacutesmall"}, {0211, "Acircumflexsmall"}, {047, "Acutesmall"}, {0212, "Adieresissmall"}, {0210, "Agravesmall"}, {0214, "Aringsmall"}, {0141, "Asmall"}, {0213, "Atildesmall"}, {0363, "Brevesmall"}, {0142, "Bsmall"}, {0256, "Caronsmall"}, {0215, "Ccedillasmall"}, {0311, "Cedillasmall"}, {0136, "Circumflexsmall"}, {0143, "Csmall"}, {0254, "Dieresissmall"}, {0372, "Dotaccentsmall"}, {0144, "Dsmall"}, {0216, "Eacutesmall"}, {0220, "Ecircumflexsmall"}, {0221, "Edieresissmall"}, {0217, "Egravesmall"}, {0145, "Esmall"}, {0104, "Ethsmall"}, {0146, "Fsmall"}, {0140, "Gravesmall"}, {0147, "Gsmall"}, {0150, "Hsmall"}, {042, "Hungarumlautsmall"}, {0222, "Iacutesmall"}, {0224, "Icircumflexsmall"}, {0225, "Idieresissmall"}, {0223, "Igravesmall"}, {0151, "Ismall"}, {0152, "Jsmall"}, {0153, "Ksmall"}, {0302, "Lslashsmall"}, {0154, "Lsmall"}, {0364, "Macronsmall"}, {0155, "Msmall"}, {0156, "Nsmall"}, {0226, "Ntildesmall"}, {0317, "OEsmall"}, {0227, "Oacutesmall"}, {0231, "Ocircumflexsmall"}, {0232, "Odieresissmall"}, {0362, "Ogoneksmall"}, {0230, "Ogravesmall"}, {0277, "Oslashsmall"}, {0157, "Osmall"}, {0233, "Otildesmall"}, {0160, "Psmall"}, {0161, "Qsmall"}, {0373, "Ringsmall"}, {0162, "Rsmall"}, {0247, "Scaronsmall"}, {0163, "Ssmall"}, {0271, "Thornsmall"}, {0176, "Tildesmall"}, {0164, "Tsmall"}, {0234, "Uacutesmall"}, {0236, "Ucircumflexsmall"}, {0237, "Udieresissmall"}, {0235, "Ugravesmall"}, {0165, "Usmall"}, {0166, "Vsmall"}, {0167, "Wsmall"}, {0170, "Xsmall"}, {0264, "Yacutesmall"}, {0330, "Ydieresissmall"}, {0171, "Ysmall"}, {0275, "Zcaronsmall"}, {0172, "Zsmall"}, {046, "ampersandsmall"}, {0201, "asuperior"}, {0365, "bsuperior"}, {0251, "centinferior"}, {043, "centoldstyle"}, {0202, "centsuperior"}, {072, "colon"}, {0173, "colonmonetary"}, {054, "comma"}, {0262, "commainferior"}, {0370, "commasuperior"}, {0266, "dollarinferior"}, {044, "dollaroldstyle"}, {045, "dollarsuperior"}, {0353, "dsuperior"}, {0245, "eightinferior"}, {070, "eightoldstyle"}, {0241, "eightsuperior"}, {0344, "esuperior"}, {0326, "exclamdownsmall"}, {041, "exclamsmall"}, {0126, "ff"}, {0131, "ffi"}, {0132, "ffl"}, {0127, "fi"}, {0320, "figuredash"}, {0114, "fiveeighths"}, {0260, "fiveinferior"}, {065, "fiveoldstyle"}, {0336, "fivesuperior"}, {0130, "fl"}, {0242, "fourinferior"}, {064, "fouroldstyle"}, {0335, "foursuperior"}, {057, "fraction"}, {055, "hyphen"}, {0137, "hypheninferior"}, {0321, "hyphensuperior"}, {0351, "isuperior"}, {0361, "lsuperior"}, {0367, "msuperior"}, {0273, "nineinferior"}, {071, "nineoldstyle"}, {0341, "ninesuperior"}, {0366, "nsuperior"}, {053, "onedotenleader"}, {0112, "oneeighth"}, {0174, "onefitted"}, {0110, "onehalf"}, {0301, "oneinferior"}, {061, "oneoldstyle"}, {0107, "onequarter"}, {0332, "onesuperior"}, {0116, "onethird"}, {0257, "osuperior"}, {0133, "parenleftinferior"}, {050, "parenleftsuperior"}, {0135, "parenrightinferior"}, {051, "parenrightsuperior"}, {056, "period"}, {0263, "periodinferior"}, {0371, "periodsuperior"}, {0300, "questiondownsmall"}, {077, "questionsmall"}, {0345, "rsuperior"}, {0175, "rupiah"}, {073, "semicolon"}, {0115, "seveneighths"}, {0246, "seveninferior"}, {067, "sevenoldstyle"}, {0340, "sevensuperior"}, {0244, "sixinferior"}, {066, "sixoldstyle"}, {0337, "sixsuperior"}, {040, "space"}, {0352, "ssuperior"}, {0113, "threeeighths"}, {0243, "threeinferior"}, {063, "threeoldstyle"}, {0111, "threequarters"}, {075, "threequartersemdash"}, {0334, "threesuperior"}, {0346, "tsuperior"}, {052, "twodotenleader"}, {0252, "twoinferior"}, {062, "twooldstyle"}, {0333, "twosuperior"}, {0117, "twothirds"}, {0274, "zeroinferior"}, {060, "zerooldstyle"}, {0342, "zerosuperior"} }; /** * Singleton instance of this class. */ public static final MacExpertEncoding INSTANCE = new MacExpertEncoding(); /** * Constructor. */ public MacExpertEncoding() { for (Object[] encodingEntry : MAC_EXPERT_ENCODING_TABLE) { add((Integer) encodingEntry[CHAR_CODE], encodingEntry[CHAR_NAME].toString()); } } @Override public COSBase getCOSObject() { return COSName.MAC_EXPERT_ENCODING; } @Override public String getEncodingName() { return "MacExpertEncoding"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/MacOSRomanEncoding.java000066400000000000000000000046351320103431700313310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import org.sejda.sambox.cos.COSName; /** * This is the Mac OS Roman encoding, which is similar to the MacRomanEncoding with the addition of 15 entries */ public class MacOSRomanEncoding extends MacRomanEncoding { private static final int CHAR_CODE = 0; private static final int CHAR_NAME = 1; /** * Table of octal character codes and their corresponding names on top of {@link MacRomanEncoding}. */ private static final Object[][] MAC_OS_ROMAN_ENCODING_TABLE = { { 255, "notequal" }, { 260, "infinity" }, { 262, "lessequal" }, { 263, "greaterequal" }, { 266, "partialdiff" }, { 267, "summation" }, { 270, "product" }, { 271, "pi" }, { 272, "integral" }, { 275, "Omega" }, { 303, "radical" }, { 305, "approxequal" }, { 306, "Delta" }, { 327, "lozenge" }, { 333, "Euro" }, { 360, "apple" } }; /** * Singleton instance of this class. * * @since Apache PDFBox 2.0.0 */ public static final MacOSRomanEncoding INSTANCE = new MacOSRomanEncoding(); /** * Constructor. */ public MacOSRomanEncoding() { super(); // differences and additions to MacRomanEncoding for (Object[] encodingEntry : MAC_OS_ROMAN_ENCODING_TABLE) { add((Integer) encodingEntry[CHAR_CODE], encodingEntry[CHAR_NAME].toString()); } } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSName getCOSObject() { return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/MacRomanEncoding.java000066400000000000000000000153031320103431700310610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import org.sejda.sambox.cos.COSName; /** * This is an interface to a text encoder. * * @author Ben Litchfield */ public class MacRomanEncoding extends Encoding { private static final int CHAR_CODE = 0; private static final int CHAR_NAME = 1; /** * Table of octal character codes and their corresponding names. */ private static final Object[][] MAC_ROMAN_ENCODING_TABLE = { { 0101, "A" }, { 0256, "AE" }, { 0347, "Aacute" }, { 0345, "Acircumflex" }, { 0200, "Adieresis" }, { 0313, "Agrave" }, { 0201, "Aring" }, { 0314, "Atilde" }, { 0102, "B" }, { 0103, "C" }, { 0202, "Ccedilla" }, { 0104, "D" }, { 0105, "E" }, { 0203, "Eacute" }, { 0346, "Ecircumflex" }, { 0350, "Edieresis" }, { 0351, "Egrave" }, { 0106, "F" }, { 0107, "G" }, { 0110, "H" }, { 0111, "I" }, { 0352, "Iacute" }, { 0353, "Icircumflex" }, { 0354, "Idieresis" }, { 0355, "Igrave" }, { 0112, "J" }, { 0113, "K" }, { 0114, "L" }, { 0115, "M" }, { 0116, "N" }, { 0204, "Ntilde" }, { 0117, "O" }, { 0316, "OE" }, { 0356, "Oacute" }, { 0357, "Ocircumflex" }, { 0205, "Odieresis" }, { 0361, "Ograve" }, { 0257, "Oslash" }, { 0315, "Otilde" }, { 0120, "P" }, { 0121, "Q" }, { 0122, "R" }, { 0123, "S" }, { 0124, "T" }, { 0125, "U" }, { 0362, "Uacute" }, { 0363, "Ucircumflex" }, { 0206, "Udieresis" }, { 0364, "Ugrave" }, { 0126, "V" }, { 0127, "W" }, { 0130, "X" }, { 0131, "Y" }, { 0331, "Ydieresis" }, { 0132, "Z" }, { 0141, "a" }, { 0207, "aacute" }, { 0211, "acircumflex" }, { 0253, "acute" }, { 0212, "adieresis" }, { 0276, "ae" }, { 0210, "agrave" }, { 046, "ampersand" }, { 0214, "aring" }, { 0136, "asciicircum" }, { 0176, "asciitilde" }, { 052, "asterisk" }, { 0100, "at" }, { 0213, "atilde" }, { 0142, "b" }, { 0134, "backslash" }, { 0174, "bar" }, { 0173, "braceleft" }, { 0175, "braceright" }, { 0133, "bracketleft" }, { 0135, "bracketright" }, { 0371, "breve" }, { 0245, "bullet" }, { 0143, "c" }, { 0377, "caron" }, { 0215, "ccedilla" }, { 0374, "cedilla" }, { 0242, "cent" }, { 0366, "circumflex" }, { 072, "colon" }, { 054, "comma" }, { 0251, "copyright" }, { 0333, "currency" }, { 0144, "d" }, { 0240, "dagger" }, { 0340, "daggerdbl" }, { 0241, "degree" }, { 0254, "dieresis" }, { 0326, "divide" }, { 044, "dollar" }, { 0372, "dotaccent" }, { 0365, "dotlessi" }, { 0145, "e" }, { 0216, "eacute" }, { 0220, "ecircumflex" }, { 0221, "edieresis" }, { 0217, "egrave" }, { 070, "eight" }, { 0311, "ellipsis" }, { 0321, "emdash" }, { 0320, "endash" }, { 075, "equal" }, { 041, "exclam" }, { 0301, "exclamdown" }, { 0146, "f" }, { 0336, "fi" }, { 065, "five" }, { 0337, "fl" }, { 0304, "florin" }, { 064, "four" }, { 0332, "fraction" }, { 0147, "g" }, { 0247, "germandbls" }, { 0140, "grave" }, { 076, "greater" }, { 0307, "guillemotleft" }, { 0310, "guillemotright" }, { 0334, "guilsinglleft" }, { 0335, "guilsinglright" }, { 0150, "h" }, { 0375, "hungarumlaut" }, { 055, "hyphen" }, { 0151, "i" }, { 0222, "iacute" }, { 0224, "icircumflex" }, { 0225, "idieresis" }, { 0223, "igrave" }, { 0152, "j" }, { 0153, "k" }, { 0154, "l" }, { 074, "less" }, { 0302, "logicalnot" }, { 0155, "m" }, { 0370, "macron" }, { 0265, "mu" }, { 0156, "n" }, { 071, "nine" }, { 0226, "ntilde" }, { 043, "numbersign" }, { 0157, "o" }, { 0227, "oacute" }, { 0231, "ocircumflex" }, { 0232, "odieresis" }, { 0317, "oe" }, { 0376, "ogonek" }, { 0230, "ograve" }, { 061, "one" }, { 0273, "ordfeminine" }, { 0274, "ordmasculine" }, { 0277, "oslash" }, { 0233, "otilde" }, { 0160, "p" }, { 0246, "paragraph" }, { 050, "parenleft" }, { 051, "parenright" }, { 045, "percent" }, { 056, "period" }, { 0341, "periodcentered" }, { 0344, "perthousand" }, { 053, "plus" }, { 0261, "plusminus" }, { 0161, "q" }, { 077, "question" }, { 0300, "questiondown" }, { 042, "quotedbl" }, { 0343, "quotedblbase" }, { 0322, "quotedblleft" }, { 0323, "quotedblright" }, { 0324, "quoteleft" }, { 0325, "quoteright" }, { 0342, "quotesinglbase" }, { 047, "quotesingle" }, { 0162, "r" }, { 0250, "registered" }, { 0373, "ring" }, { 0163, "s" }, { 0244, "section" }, { 073, "semicolon" }, { 067, "seven" }, { 066, "six" }, { 057, "slash" }, { 040, "space" }, { 0243, "sterling" }, { 0164, "t" }, { 063, "three" }, { 0367, "tilde" }, { 0252, "trademark" }, { 062, "two" }, { 0165, "u" }, { 0234, "uacute" }, { 0236, "ucircumflex" }, { 0237, "udieresis" }, { 0235, "ugrave" }, { 0137, "underscore" }, { 0166, "v" }, { 0167, "w" }, { 0170, "x" }, { 0171, "y" }, { 0330, "ydieresis" }, { 0264, "yen" }, { 0172, "z" }, { 060, "zero" }, // adding an additional mapping as defined in Appendix D of the pdf spec { 0312, "space" } }; /** * Singleton instance of this class. * * @since Apache PDFBox 1.3.0 */ public static final MacRomanEncoding INSTANCE = new MacRomanEncoding(); /** * Constructor. */ public MacRomanEncoding() { for (Object[] encodingEntry : MAC_ROMAN_ENCODING_TABLE) { add((Integer) encodingEntry[CHAR_CODE], encodingEntry[CHAR_NAME].toString()); } } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSName getCOSObject() { return COSName.MAC_ROMAN_ENCODING; } @Override public String getEncodingName() { return "MacRomanEncoding"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/StandardEncoding.java000066400000000000000000000123131320103431700311220ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import org.sejda.sambox.cos.COSName; /** * This is an interface to a text encoder. * * @author Ben Litchfield */ public class StandardEncoding extends Encoding { private static final int CHAR_CODE = 0; private static final int CHAR_NAME = 1; /** * Table of octal character codes and their corresponding names. */ private static final Object[][] STANDARD_ENCODING_TABLE = { { 0101, "A" }, { 0341, "AE" }, { 0102, "B" }, { 0103, "C" }, { 0104, "D" }, { 0105, "E" }, { 0106, "F" }, { 0107, "G" }, { 0110, "H" }, { 0111, "I" }, { 0112, "J" }, { 0113, "K" }, { 0114, "L" }, { 0350, "Lslash" }, { 0115, "M" }, { 0116, "N" }, { 0117, "O" }, { 0352, "OE" }, { 0351, "Oslash" }, { 0120, "P" }, { 0121, "Q" }, { 0122, "R" }, { 0123, "S" }, { 0124, "T" }, { 0125, "U" }, { 0126, "V" }, { 0127, "W" }, { 0130, "X" }, { 0131, "Y" }, { 0132, "Z" }, { 0141, "a" }, { 0302, "acute" }, { 0361, "ae" }, { 0046, "ampersand" }, { 0136, "asciicircum" }, { 0176, "asciitilde" }, { 0052, "asterisk" }, { 0100, "at" }, { 0142, "b" }, { 0134, "backslash" }, { 0174, "bar" }, { 0173, "braceleft" }, { 0175, "braceright" }, { 0133, "bracketleft" }, { 0135, "bracketright" }, { 0306, "breve" }, { 0267, "bullet" }, { 0143, "c" }, { 0317, "caron" }, { 0313, "cedilla" }, { 0242, "cent" }, { 0303, "circumflex" }, { 0072, "colon" }, { 0054, "comma" }, { 0250, "currency" }, { 0144, "d" }, { 0262, "dagger" }, { 0263, "daggerdbl" }, { 0310, "dieresis" }, { 0044, "dollar" }, { 0307, "dotaccent" }, { 0365, "dotlessi" }, { 0145, "e" }, { 0070, "eight" }, { 0274, "ellipsis" }, { 0320, "emdash" }, { 0261, "endash" }, { 0075, "equal" }, { 0041, "exclam" }, { 0241, "exclamdown" }, { 0146, "f" }, { 0256, "fi" }, { 0065, "five" }, { 0257, "fl" }, { 0246, "florin" }, { 0064, "four" }, { 0244, "fraction" }, { 0147, "g" }, { 0373, "germandbls" }, { 0301, "grave" }, { 0076, "greater" }, { 0253, "guillemotleft" }, { 0273, "guillemotright" }, { 0254, "guilsinglleft" }, { 0255, "guilsinglright" }, { 0150, "h" }, { 0315, "hungarumlaut" }, { 0055, "hyphen" }, { 0151, "i" }, { 0152, "j" }, { 0153, "k" }, { 0154, "l" }, { 0074, "less" }, { 0370, "lslash" }, { 0155, "m" }, { 0305, "macron" }, { 0156, "n" }, { 0071, "nine" }, { 0043, "numbersign" }, { 0157, "o" }, { 0372, "oe" }, { 0316, "ogonek" }, { 0061, "one" }, { 0343, "ordfeminine" }, { 0353, "ordmasculine" }, { 0371, "oslash" }, { 0160, "p" }, { 0266, "paragraph" }, { 0050, "parenleft" }, { 0051, "parenright" }, { 0045, "percent" }, { 0056, "period" }, { 0264, "periodcentered" }, { 0275, "perthousand" }, { 0053, "plus" }, { 0161, "q" }, { 0077, "question" }, { 0277, "questiondown" }, { 0042, "quotedbl" }, { 0271, "quotedblbase" }, { 0252, "quotedblleft" }, { 0272, "quotedblright" }, { 0140, "quoteleft" }, { 0047, "quoteright" }, { 0270, "quotesinglbase" }, { 0251, "quotesingle" }, { 0162, "r" }, { 0312, "ring" }, { 0163, "s" }, { 0247, "section" }, { 0073, "semicolon" }, { 0067, "seven" }, { 0066, "six" }, { 0057, "slash" }, { 0040, "space" }, { 0243, "sterling" }, { 0164, "t" }, { 0063, "three" }, { 0304, "tilde" }, { 0062, "two" }, { 0165, "u" }, { 0137, "underscore" }, { 0166, "v" }, { 0167, "w" }, { 0170, "x" }, { 0171, "y" }, { 0245, "yen" }, { 0172, "z" }, { 0060, "zero" } }; /** * Singleton instance of this class. * * @since Apache PDFBox 1.3.0 */ public static final StandardEncoding INSTANCE = new StandardEncoding(); /** * Constructor. */ public StandardEncoding() { for (Object[] encodingEntry : STANDARD_ENCODING_TABLE) { add((Integer) encodingEntry[CHAR_CODE], encodingEntry[CHAR_NAME].toString()); } } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSName getCOSObject() { return COSName.STANDARD_ENCODING; } @Override public String getEncodingName() { return "StandardEncoding"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/SymbolEncoding.java000066400000000000000000000157021320103431700306340ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; /** * This is an interface to a text encoder. */ public class SymbolEncoding extends Encoding { private static final int CHAR_CODE = 0; private static final int CHAR_NAME = 1; /** * Table of octal character codes and their corresponding names. */ private static final Object[][] SYMBOL_ENCODING_TABLE = { {0101, "Alpha"}, {0102, "Beta"}, {0103, "Chi"}, {0104, "Delta"}, {0105, "Epsilon"}, {0110, "Eta"}, {0240, "Euro"}, {0107, "Gamma"}, {0301, "Ifraktur"}, {0111, "Iota"}, {0113, "Kappa"}, {0114, "Lambda"}, {0115, "Mu"}, {0116, "Nu"}, {0127, "Omega"}, {0117, "Omicron"}, {0106, "Phi"}, {0120, "Pi"}, {0131, "Psi"}, {0302, "Rfraktur"}, {0122, "Rho"}, {0123, "Sigma"}, {0124, "Tau"}, {0121, "Theta"}, {0125, "Upsilon"}, {0241, "Upsilon1"}, {0130, "Xi"}, {0132, "Zeta"}, {0300, "aleph"}, {0141, "alpha"}, {0046, "ampersand"}, {0320, "angle"}, {0341, "angleleft"}, {0361, "angleright"}, {0273, "approxequal"}, {0253, "arrowboth"}, {0333, "arrowdblboth"}, {0337, "arrowdbldown"}, {0334, "arrowdblleft"}, {0336, "arrowdblright"}, {0335, "arrowdblup"}, {0257, "arrowdown"}, {0276, "arrowhorizex"}, {0254, "arrowleft"}, {0256, "arrowright"}, {0255, "arrowup"}, {0275, "arrowvertex"}, {0052, "asteriskmath"}, {0174, "bar"}, {0142, "beta"}, {0173, "braceleft"}, {0175, "braceright"}, {0354, "bracelefttp"}, {0355, "braceleftmid"}, {0356, "braceleftbt"}, {0374, "bracerighttp"}, {0375, "bracerightmid"}, {0376, "bracerightbt"}, {0357, "braceex"}, {0133, "bracketleft"}, {0135, "bracketright"}, {0351, "bracketlefttp"}, {0352, "bracketleftex"}, {0353, "bracketleftbt"}, {0371, "bracketrighttp"}, {0372, "bracketrightex"}, {0373, "bracketrightbt"}, {0267, "bullet"}, {0277, "carriagereturn"}, {0143, "chi"}, {0304, "circlemultiply"}, {0305, "circleplus"}, {0247, "club"}, {0072, "colon"}, {0054, "comma"}, {0100, "congruent"}, {0343, "copyrightsans"}, {0323, "copyrightserif"}, {0260, "degree"}, {0144, "delta"}, {0250, "diamond"}, {0270, "divide"}, {0327, "dotmath"}, {0070, "eight"}, {0316, "element"}, {0274, "ellipsis"}, {0306, "emptyset"}, {0145, "epsilon"}, {0075, "equal"}, {0272, "equivalence"}, {0150, "eta"}, {0041, "exclam"}, {0044, "existential"}, {0065, "five"}, {0246, "florin"}, {0064, "four"}, {0244, "fraction"}, {0147, "gamma"}, {0321, "gradient"}, {0076, "greater"}, {0263, "greaterequal"}, {0251, "heart"}, {0245, "infinity"}, {0362, "integral"}, {0363, "integraltp"}, {0364, "integralex"}, {0365, "integralbt"}, {0307, "intersection"}, {0151, "iota"}, {0153, "kappa"}, {0154, "lambda"}, {0074, "less"}, {0243, "lessequal"}, {0331, "logicaland"}, {0330, "logicalnot"}, {0332, "logicalor"}, {0340, "lozenge"}, {0055, "minus"}, {0242, "minute"}, {0155, "mu"}, {0264, "multiply"}, {0071, "nine"}, {0317, "notelement"}, {0271, "notequal"}, {0313, "notsubset"}, {0156, "nu"}, {0043, "numbersign"}, {0167, "omega"}, {0166, "omega1"}, {0157, "omicron"}, {0061, "one"}, {0050, "parenleft"}, {0051, "parenright"}, {0346, "parenlefttp"}, {0347, "parenleftex"}, {0350, "parenleftbt"}, {0366, "parenrighttp"}, {0367, "parenrightex"}, {0370, "parenrightbt"}, {0266, "partialdiff"}, {0045, "percent"}, {0056, "period"}, {0136, "perpendicular"}, {0146, "phi"}, {0152, "phi1"}, {0160, "pi"}, {0053, "plus"}, {0261, "plusminus"}, {0325, "product"}, {0314, "propersubset"}, {0311, "propersuperset"}, {0265, "proportional"}, {0171, "psi"}, {0077, "question"}, {0326, "radical"}, {0140, "radicalex"}, {0315, "reflexsubset"}, {0312, "reflexsuperset"}, {0342, "registersans"}, {0322, "registerserif"}, {0162, "rho"}, {0262, "second"}, {0073, "semicolon"}, {0067, "seven"}, {0163, "sigma"}, {0126, "sigma1"}, {0176, "similar"}, {0066, "six"}, {0057, "slash"}, {0040, "space"}, {0252, "spade"}, {0047, "suchthat"}, {0345, "summation"}, {0164, "tau"}, {0134, "therefore"}, {0161, "theta"}, {0112, "theta1"}, {0063, "three"}, {0344, "trademarksans"}, {0324, "trademarkserif"}, {0062, "two"}, {0137, "underscore"}, {0310, "union"}, {0042, "universal"}, {0165, "upsilon"}, {0303, "weierstrass"}, {0170, "xi"}, {0060, "zero"}, {0172, "zeta"} }; /** * Singleton instance of this class. */ public static final SymbolEncoding INSTANCE = new SymbolEncoding(); /** * Constructor. */ public SymbolEncoding() { for (Object[] encodingEntry : SYMBOL_ENCODING_TABLE) { add((Integer) encodingEntry[CHAR_CODE], encodingEntry[CHAR_NAME].toString()); } } @Override public COSBase getCOSObject() { return COSName.getPDFName("SymbolEncoding"); } @Override public String getEncodingName() { return "SymbolEncoding"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/Type1Encoding.java000066400000000000000000000043641320103431700303730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import java.util.Map; import org.apache.fontbox.afm.CharMetric; import org.apache.fontbox.afm.FontMetrics; import org.sejda.sambox.cos.COSBase; /** * An encoding for a Type 1 font. */ public class Type1Encoding extends Encoding { /** * Creates an encoding from the given FontBox encoding. * * @param encoding FontBox encoding */ public static Type1Encoding fromFontBox(org.apache.fontbox.encoding.Encoding encoding) { // todo: could optimise this by looking for specific subclasses Map codeToName = encoding.getCodeToNameMap(); Type1Encoding enc = new Type1Encoding(); for (Map.Entry entry : codeToName.entrySet()) { enc.add(entry.getKey(), entry.getValue()); } return enc; } /** * Creates an empty encoding. */ public Type1Encoding() { } /** * Creates an encoding from the given AFM font metrics. * * @param fontMetrics AFM font metrics. */ public Type1Encoding(FontMetrics fontMetrics) { for (CharMetric nextMetric : fontMetrics.getCharMetrics()) { add(nextMetric.getCharacterCode(), nextMetric.getName()); } } @Override public COSBase getCOSObject() { return null; } @Override public String getEncodingName() { return "built-in (Type 1)"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/WinAnsiEncoding.java000066400000000000000000000163301320103431700307350ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import org.sejda.sambox.cos.COSName; /** * This the win ansi encoding. * * @author Ben Litchfield */ public class WinAnsiEncoding extends Encoding { private static final int CHAR_CODE = 0; private static final int CHAR_NAME = 1; /** * Table of octal character codes and their corresponding names. */ private static final Object[][] WIN_ANSI_ENCODING_TABLE = { { 0101, "A" }, { 0306, "AE" }, { 0301, "Aacute" }, { 0302, "Acircumflex" }, { 0304, "Adieresis" }, { 0300, "Agrave" }, { 0305, "Aring" }, { 0303, "Atilde" }, { 0102, "B" }, { 0103, "C" }, { 0307, "Ccedilla" }, { 0104, "D" }, { 0105, "E" }, { 0311, "Eacute" }, { 0312, "Ecircumflex" }, { 0313, "Edieresis" }, { 0310, "Egrave" }, { 0320, "Eth" }, { 0200, "Euro" }, { 0106, "F" }, { 0107, "G" }, { 0110, "H" }, { 0111, "I" }, { 0315, "Iacute" }, { 0316, "Icircumflex" }, { 0317, "Idieresis" }, { 0314, "Igrave" }, { 0112, "J" }, { 0113, "K" }, { 0114, "L" }, { 0115, "M" }, { 0116, "N" }, { 0321, "Ntilde" }, { 0117, "O" }, { 0214, "OE" }, { 0323, "Oacute" }, { 0324, "Ocircumflex" }, { 0326, "Odieresis" }, { 0322, "Ograve" }, { 0330, "Oslash" }, { 0325, "Otilde" }, { 0120, "P" }, { 0121, "Q" }, { 0122, "R" }, { 0123, "S" }, { 0212, "Scaron" }, { 0124, "T" }, { 0336, "Thorn" }, { 0125, "U" }, { 0332, "Uacute" }, { 0333, "Ucircumflex" }, { 0334, "Udieresis" }, { 0331, "Ugrave" }, { 0126, "V" }, { 0127, "W" }, { 0130, "X" }, { 0131, "Y" }, { 0335, "Yacute" }, { 0237, "Ydieresis" }, { 0132, "Z" }, { 0216, "Zcaron" }, { 0141, "a" }, { 0341, "aacute" }, { 0342, "acircumflex" }, { 0264, "acute" }, { 0344, "adieresis" }, { 0346, "ae" }, { 0340, "agrave" }, { 046, "ampersand" }, { 0345, "aring" }, { 0136, "asciicircum" }, { 0176, "asciitilde" }, { 052, "asterisk" }, { 0100, "at" }, { 0343, "atilde" }, { 0142, "b" }, { 0134, "backslash" }, { 0174, "bar" }, { 0173, "braceleft" }, { 0175, "braceright" }, { 0133, "bracketleft" }, { 0135, "bracketright" }, { 0246, "brokenbar" }, { 0225, "bullet" }, { 0143, "c" }, { 0347, "ccedilla" }, { 0270, "cedilla" }, { 0242, "cent" }, { 0210, "circumflex" }, { 072, "colon" }, { 054, "comma" }, { 0251, "copyright" }, { 0244, "currency" }, { 0144, "d" }, { 0206, "dagger" }, { 0207, "daggerdbl" }, { 0260, "degree" }, { 0250, "dieresis" }, { 0367, "divide" }, { 044, "dollar" }, { 0145, "e" }, { 0351, "eacute" }, { 0352, "ecircumflex" }, { 0353, "edieresis" }, { 0350, "egrave" }, { 070, "eight" }, { 0205, "ellipsis" }, { 0227, "emdash" }, { 0226, "endash" }, { 075, "equal" }, { 0360, "eth" }, { 041, "exclam" }, { 0241, "exclamdown" }, { 0146, "f" }, { 065, "five" }, { 0203, "florin" }, { 064, "four" }, { 0147, "g" }, { 0337, "germandbls" }, { 0140, "grave" }, { 076, "greater" }, { 0253, "guillemotleft" }, { 0273, "guillemotright" }, { 0213, "guilsinglleft" }, { 0233, "guilsinglright" }, { 0150, "h" }, { 055, "hyphen" }, { 0151, "i" }, { 0355, "iacute" }, { 0356, "icircumflex" }, { 0357, "idieresis" }, { 0354, "igrave" }, { 0152, "j" }, { 0153, "k" }, { 0154, "l" }, { 074, "less" }, { 0254, "logicalnot" }, { 0155, "m" }, { 0257, "macron" }, { 0265, "mu" }, { 0327, "multiply" }, { 0156, "n" }, { 071, "nine" }, { 0361, "ntilde" }, { 043, "numbersign" }, { 0157, "o" }, { 0363, "oacute" }, { 0364, "ocircumflex" }, { 0366, "odieresis" }, { 0234, "oe" }, { 0362, "ograve" }, { 061, "one" }, { 0275, "onehalf" }, { 0274, "onequarter" }, { 0271, "onesuperior" }, { 0252, "ordfeminine" }, { 0272, "ordmasculine" }, { 0370, "oslash" }, { 0365, "otilde" }, { 0160, "p" }, { 0266, "paragraph" }, { 050, "parenleft" }, { 051, "parenright" }, { 045, "percent" }, { 056, "period" }, { 0267, "periodcentered" }, { 0211, "perthousand" }, { 053, "plus" }, { 0261, "plusminus" }, { 0161, "q" }, { 077, "question" }, { 0277, "questiondown" }, { 042, "quotedbl" }, { 0204, "quotedblbase" }, { 0223, "quotedblleft" }, { 0224, "quotedblright" }, { 0221, "quoteleft" }, { 0222, "quoteright" }, { 0202, "quotesinglbase" }, { 047, "quotesingle" }, { 0162, "r" }, { 0256, "registered" }, { 0163, "s" }, { 0232, "scaron" }, { 0247, "section" }, { 073, "semicolon" }, { 067, "seven" }, { 066, "six" }, { 057, "slash" }, { 040, "space" }, { 0243, "sterling" }, { 0164, "t" }, { 0376, "thorn" }, { 063, "three" }, { 0276, "threequarters" }, { 0263, "threesuperior" }, { 0230, "tilde" }, { 0231, "trademark" }, { 062, "two" }, { 0262, "twosuperior" }, { 0165, "u" }, { 0372, "uacute" }, { 0373, "ucircumflex" }, { 0374, "udieresis" }, { 0371, "ugrave" }, { 0137, "underscore" }, { 0166, "v" }, { 0167, "w" }, { 0170, "x" }, { 0171, "y" }, { 0375, "yacute" }, { 0377, "ydieresis" }, { 0245, "yen" }, { 0172, "z" }, { 0236, "zcaron" }, { 060, "zero" }, // adding some additional mappings as defined in Appendix D of the pdf spec { 0240, "space" }, { 0255, "hyphen" } }; /** * Singleton instance of this class. * * @since Apache PDFBox 1.3.0 */ public static final WinAnsiEncoding INSTANCE = new WinAnsiEncoding(); /** * Constructor. */ public WinAnsiEncoding() { for (Object[] encodingEntry : WIN_ANSI_ENCODING_TABLE) { add((Integer) encodingEntry[CHAR_CODE], encodingEntry[CHAR_NAME].toString()); } // From the PDF specification: // In WinAnsiEncoding, all unused codes greater than 40 map to the bullet character. for (int i = 041; i <= 255; i++) { if (!codeToName.containsKey(i)) { add(i, "bullet"); } } } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSName getCOSObject() { return COSName.WIN_ANSI_ENCODING; } @Override public String getEncodingName() { return "WinAnsiEncoding"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/encoding/ZapfDingbatsEncoding.java000066400000000000000000000142401320103431700317370ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.font.encoding; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; /** * This is an interface to a text encoder. */ public class ZapfDingbatsEncoding extends Encoding { private static final int CHAR_CODE = 0; private static final int CHAR_NAME = 1; /** * Table of octal character codes and their corresponding names. */ private static final Object[][] ZAPFDINGBATS_ENCODING_TABLE = { {040, "space"}, {041, "a1"}, {042, "a2"}, {043, "a202"}, {044, "a3"}, {045, "a4"}, {046, "a5"}, {047, "a119"}, {050, "a118"}, {051, "a117"}, {052, "a11"}, {053, "a12"}, {054, "a13"}, {055, "a14"}, {056, "a15"}, {057, "a16"}, {060, "a105"}, {061, "a17"}, {062, "a18"}, {063, "a19"}, {064, "a20"}, {065, "a21"}, {066, "a22"}, {067, "a23"}, {070, "a24"}, {071, "a25"}, {072, "a26"}, {073, "a27"}, {074, "a28"}, {075, "a6"}, {076, "a7"}, {077, "a8"}, {0100, "a9"}, {0101, "a10"}, {0102, "a29"}, {0103, "a30"}, {0104, "a31"}, {0105, "a32"}, {0106, "a33"}, {0107, "a34"}, {0110, "a35"}, {0111, "a36"}, {0112, "a37"}, {0113, "a38"}, {0114, "a39"}, {0115, "a40"}, {0116, "a41"}, {0117, "a42"}, {0120, "a43"}, {0121, "a44"}, {0122, "a45"}, {0123, "a46"}, {0124, "a47"}, {0125, "a48"}, {0126, "a49"}, {0127, "a50"}, {0130, "a51"}, {0131, "a52"}, {0132, "a53"}, {0133, "a54"}, {0134, "a55"}, {0135, "a56"}, {0136, "a57"}, {0137, "a58"}, {0140, "a59"}, {0141, "a60"}, {0142, "a61"}, {0143, "a62"}, {0144, "a63"}, {0145, "a64"}, {0146, "a65"}, {0147, "a66"}, {0150, "a67"}, {0151, "a68"}, {0152, "a69"}, {0153, "a70"}, {0154, "a71"}, {0155, "a72"}, {0156, "a73"}, {0157, "a74"}, {0160, "a203"}, {0161, "a75"}, {0162, "a204"}, {0163, "a76"}, {0164, "a77"}, {0165, "a78"}, {0166, "a79"}, {0167, "a81"}, {0170, "a82"}, {0171, "a83"}, {0172, "a84"}, {0173, "a97"}, {0174, "a98"}, {0175, "a99"}, {0176, "a100"}, {0241, "a101"}, {0242, "a102"}, {0243, "a103"}, {0244, "a104"}, {0245, "a106"}, {0246, "a107"}, {0247, "a108"}, {0250, "a112"}, {0251, "a111"}, {0252, "a110"}, {0253, "a109"}, {0254, "a120"}, {0255, "a121"}, {0256, "a122"}, {0257, "a123"}, {0260, "a124"}, {0261, "a125"}, {0262, "a126"}, {0263, "a127"}, {0264, "a128"}, {0265, "a129"}, {0266, "a130"}, {0267, "a131"}, {0270, "a132"}, {0271, "a133"}, {0272, "a134"}, {0273, "a135"}, {0274, "a136"}, {0275, "a137"}, {0276, "a138"}, {0277, "a139"}, {0300, "a140"}, {0301, "a141"}, {0302, "a142"}, {0303, "a143"}, {0304, "a144"}, {0305, "a145"}, {0306, "a146"}, {0307, "a147"}, {0310, "a148"}, {0311, "a149"}, {0312, "a150"}, {0313, "a151"}, {0314, "a152"}, {0315, "a153"}, {0316, "a154"}, {0317, "a155"}, {0320, "a156"}, {0321, "a157"}, {0322, "a158"}, {0323, "a159"}, {0324, "a160"}, {0325, "a161"}, {0326, "a163"}, {0327, "a164"}, {0330, "a196"}, {0331, "a165"}, {0332, "a192"}, {0333, "a166"}, {0334, "a167"}, {0335, "a168"}, {0336, "a169"}, {0337, "a170"}, {0340, "a171"}, {0341, "a172"}, {0342, "a173"}, {0343, "a162"}, {0344, "a174"}, {0345, "a175"}, {0346, "a176"}, {0347, "a177"}, {0350, "a178"}, {0351, "a179"}, {0352, "a193"}, {0353, "a180"}, {0354, "a199"}, {0355, "a181"}, {0356, "a200"}, {0357, "a182"}, {0361, "a201"}, {0362, "a183"}, {0363, "a184"}, {0364, "a197"}, {0365, "a185"}, {0366, "a194"}, {0367, "a198"}, {0370, "a186"}, {0371, "a195"}, {0372, "a187"}, {0373, "a188"}, {0374, "a189"}, {0375, "a190"}, {0376, "a191"} }; /** * Singleton instance of this class. */ public static final ZapfDingbatsEncoding INSTANCE = new ZapfDingbatsEncoding(); /** * Constructor. */ public ZapfDingbatsEncoding() { for (Object[] encodingEntry : ZAPFDINGBATS_ENCODING_TABLE) { add((Integer) encodingEntry[CHAR_CODE], encodingEntry[CHAR_NAME].toString()); } } @Override public COSBase getCOSObject() { return COSName.getPDFName("ZapfDingbatsEncoding"); } @Override public String getEncodingName() { return "ZapfDingbatsEncoding"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/font/package.html000066400000000000000000000017101320103431700255420ustar00rootroot00000000000000 Classes to deal with font functionality in a PDF Document. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/000077500000000000000000000000001320103431700241145ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/PDFontSetting.java000066400000000000000000000062561320103431700274600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.font.PDFontFactory; /** * This class represents a font setting used for the graphics state. A font setting is a font and a * font size. Maybe there is a better name for this? * * @author Ben Litchfield */ public class PDFontSetting implements COSObjectable { private COSArray fontSetting = null; /** * Creates a blank font setting, font will be null, size will be 1. */ public PDFontSetting() { fontSetting = new COSArray(); fontSetting.add( null ); fontSetting.add( new COSFloat( 1 ) ); } /** * Constructs a font setting from an existing array. * * @param fs The new font setting value. */ public PDFontSetting( COSArray fs ) { fontSetting = fs; } /** * {@inheritDoc} */ @Override public COSBase getCOSObject() { return fontSetting; } /** * This will get the font for this font setting. * * @return The font for this setting of null if one was not found. * * @throws IOException If there is an error getting the font. */ public PDFont getFont() throws IOException { PDFont retval = null; COSBase font = fontSetting.getObject(0); if( font instanceof COSDictionary ) { retval = PDFontFactory.createFont( (COSDictionary)font ); } return retval; } /** * This will set the font for this font setting. * * @param font The new font. */ public void setFont( PDFont font ) { fontSetting.set( 0, font ); } /** * This will get the size of the font. * * @return The size of the font. */ public float getFontSize() { COSNumber size = (COSNumber)fontSetting.get( 1 ); return size.floatValue(); } /** * This will set the size of the font. * * @param size The new size of the font. */ public void setFontSize( float size ) { fontSetting.set( 1, new COSFloat( size ) ); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/PDLineDashPattern.java000066400000000000000000000046031320103431700302330ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics; import java.util.Arrays; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSObjectable; /** * A line dash pattern for stroking paths. * Instances of PDLineDashPattern are immutable. * @author Ben Litchfield * @author John Hewson */ public final class PDLineDashPattern implements COSObjectable { private final int phase; private final float[] array; /** * Creates a new line dash pattern, with no dashes and a phase of 0. */ public PDLineDashPattern() { array = new float[] { }; phase = 0; } /** * Creates a new line dash pattern from a dash array and phase. * @param array the dash array * @param phase the phase */ public PDLineDashPattern(COSArray array, int phase) { this.array = array.toFloatArray(); this.phase = phase; } @Override public COSBase getCOSObject() { COSArray cos = new COSArray(); cos.add(COSArrayList.converterToCOSArray(Arrays.asList(array))); cos.add(COSInteger.get(phase)); return cos; } /** * Returns the dash phase. * This specifies the distance into the dash pattern at which to start the dash. * @return the dash phase */ public int getPhase() { return phase; } /** * Returns the dash array. * @return the dash array */ public float[] getDashArray() { return array.clone(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/PDPostScriptXObject.java000066400000000000000000000024301320103431700305730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; /** * A PostScript XObject. * Conforming readers may not be able to interpret the PostScript fragments. * * @author John Hewson */ public class PDPostScriptXObject extends PDXObject { /** * Creates a PostScript XObject. * @param stream The XObject stream */ public PDPostScriptXObject(COSStream stream) { super(stream, COSName.PS); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/PDXObject.java000066400000000000000000000117241320103431700265460ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.ResourceCache; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; import org.sejda.sambox.pdmodel.graphics.form.PDTransparencyGroup; import org.sejda.sambox.pdmodel.graphics.image.PDImageXObject; /** * An external object, or "XObject". * * @author Ben Litchfield * @author John Hewson */ public class PDXObject implements COSObjectable { private PDStream stream; /** * Creates a new XObject instance of the appropriate type for the COS stream. * * @param base The stream which is wrapped by this XObject. * @return A new XObject instance. * @throws java.io.IOException if there is an error creating the XObject. */ public static PDXObject createXObject(COSBase base, PDResources resources) throws IOException { if (base == null) { // TODO throw an exception? return null; } if (!(base instanceof COSStream)) { throw new IOException("Unexpected object type: " + base.getClass().getName()); } COSStream stream = (COSStream) base; String subtype = stream.getNameAsString(COSName.SUBTYPE); if (COSName.IMAGE.getName().equals(subtype)) { return new PDImageXObject(new PDStream(stream), resources); } else if (COSName.FORM.getName().equals(subtype)) { ResourceCache cache = resources != null ? resources.getResourceCache() : null; COSDictionary group = stream.getDictionaryObject(COSName.GROUP, COSDictionary.class); if (group != null && COSName.TRANSPARENCY.equals(group.getCOSName(COSName.S))) { return new PDTransparencyGroup(stream, cache); } return new PDFormXObject(stream, cache); } else if (COSName.PS.getName().equals(subtype)) { return new PDPostScriptXObject(stream); } else { throw new IOException("Invalid XObject Subtype: " + subtype); } } /** * Creates a new XObject from the given stream and subtype. * * @param stream The stream to read. * @param subtype */ protected PDXObject(COSStream stream, COSName subtype) { this.stream = new PDStream(stream); // could be used for writing: stream.setName(COSName.TYPE, COSName.XOBJECT.getName()); stream.setName(COSName.SUBTYPE, subtype.getName()); } /** * Creates a new XObject from the given stream and subtype. * * @param stream The stream to read. * @param subtype */ protected PDXObject(PDStream stream, COSName subtype) { this.stream = stream; stream.getCOSObject().setName(COSName.TYPE, COSName.XOBJECT.getName()); stream.getCOSObject().setName(COSName.SUBTYPE, subtype.getName()); } /** * Creates a new XObject from the given stream and subtype. * * @param stream The stream to read. */ protected PDXObject(COSName subtype) { this.stream = new PDStream(); stream.getCOSObject().setName(COSName.TYPE, COSName.XOBJECT.getName()); stream.getCOSObject().setName(COSName.SUBTYPE, subtype.getName()); } /** * Returns the stream. {@inheritDoc} */ @Override public final COSStream getCOSObject() { return stream.getCOSObject(); } /** * Returns the stream. * * @return The stream for this object. */ public final PDStream getStream() { return stream; } public final void setStream(PDStream stream) { stream.getCOSObject().setName(COSName.TYPE, COSName.XOBJECT.getName()); stream.getCOSObject().setName(COSName.SUBTYPE, this.stream.getCOSObject().getCOSName(COSName.SUBTYPE).getName()); this.stream = stream; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/blend/000077500000000000000000000000001320103431700252005ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/blend/BlendComposite.java000066400000000000000000000207551320103431700307630ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.blend; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.CompositeContext; import java.awt.RenderingHints; import java.awt.color.ColorSpace; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT composite for blend modes. * * @author Kühn & Weyh Software, GmbH */ public final class BlendComposite implements Composite { /** * Log instance. */ private static final Logger LOG = LoggerFactory.getLogger(BlendComposite.class); /** * Creates a blend composite * * @param blendMode Desired blend mode * @param constantAlpha Constant alpha, must be in the inclusive range [0.0...1.0] or it will be clipped. */ public static Composite getInstance(BlendMode blendMode, float constantAlpha) { if (constantAlpha < 0) { LOG.warn("using 0 instead of incorrect Alpha " + constantAlpha); constantAlpha = 0; } else if (constantAlpha > 1) { LOG.warn("using 1 instead of incorrect Alpha " + constantAlpha); constantAlpha = 1; } if (blendMode == BlendMode.NORMAL) { return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, constantAlpha); } return new BlendComposite(blendMode, constantAlpha); } // TODO - non-separable blending modes private final BlendMode blendMode; private final float constantAlpha; private BlendComposite(BlendMode blendMode, float constantAlpha) { super(); this.blendMode = blendMode; this.constantAlpha = constantAlpha; } @Override public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints) { return new BlendCompositeContext(srcColorModel, dstColorModel, hints); } class BlendCompositeContext implements CompositeContext { private final ColorModel srcColorModel; private final ColorModel dstColorModel; private final RenderingHints hints; BlendCompositeContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints) { this.srcColorModel = srcColorModel; this.dstColorModel = dstColorModel; this.hints = hints; } @Override public void dispose() { // nothing needed } @Override public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { int x0 = src.getMinX(); int y0 = src.getMinY(); int width = Math.min(Math.min(src.getWidth(), dstIn.getWidth()), dstOut.getWidth()); int height = Math.min(Math.min(src.getHeight(), dstIn.getHeight()), dstOut.getHeight()); int x1 = x0 + width; int y1 = y0 + height; int dstInXShift = dstIn.getMinX() - x0; int dstInYShift = dstIn.getMinY() - y0; int dstOutXShift = dstOut.getMinX() - x0; int dstOutYShift = dstOut.getMinY() - y0; ColorSpace srcColorSpace = srcColorModel.getColorSpace(); int numSrcColorComponents = srcColorModel.getNumColorComponents(); int numSrcComponents = src.getNumBands(); boolean srcHasAlpha = (numSrcComponents > numSrcColorComponents); ColorSpace dstColorSpace = dstColorModel.getColorSpace(); int numDstColorComponents = dstColorModel.getNumColorComponents(); int numDstComponents = dstIn.getNumBands(); boolean dstHasAlpha = (numDstComponents > numDstColorComponents); int colorSpaceType = dstColorSpace.getType(); boolean subtractive = (colorSpaceType != ColorSpace.TYPE_RGB) && (colorSpaceType != ColorSpace.TYPE_GRAY); boolean blendModeIsSeparable = blendMode instanceof SeparableBlendMode; SeparableBlendMode separableBlendMode = blendModeIsSeparable ? (SeparableBlendMode) blendMode : null; boolean needsColorConversion = !srcColorSpace.equals(dstColorSpace); Object srcPixel = null; Object dstPixel = null; float[] srcComponents = new float[numSrcComponents]; // PDFBOX-3501 let getNormalizedComponents allocate to avoid // ArrayIndexOutOfBoundsException for bitonal target float[] dstComponents = null; float[] srcColor = new float[numSrcColorComponents]; float[] srcConverted; for (int y = y0; y < y1; y++) { for (int x = x0; x < x1; x++) { srcPixel = src.getDataElements(x, y, srcPixel); dstPixel = dstIn.getDataElements(dstInXShift + x, dstInYShift + y, dstPixel); srcComponents = srcColorModel.getNormalizedComponents(srcPixel, srcComponents, 0); dstComponents = dstColorModel.getNormalizedComponents(dstPixel, dstComponents, 0); float srcAlpha = srcHasAlpha ? srcComponents[numSrcColorComponents] : 1.0f; float dstAlpha = dstHasAlpha ? dstComponents[numDstColorComponents] : 1.0f; srcAlpha = srcAlpha * constantAlpha; float resultAlpha = dstAlpha + srcAlpha - srcAlpha * dstAlpha; float srcAlphaRatio = (resultAlpha > 0) ? srcAlpha / resultAlpha : 0; // convert color System.arraycopy(srcComponents, 0, srcColor, 0, numSrcColorComponents); if (needsColorConversion) { // TODO - very very slow - Hash results??? float[] cieXYZ = srcColorSpace.toCIEXYZ(srcColor); srcConverted = dstColorSpace.fromCIEXYZ(cieXYZ); } else { srcConverted = srcColor; } if (separableBlendMode != null) { for (int k = 0; k < numDstColorComponents; k++) { float srcValue = srcConverted[k]; float dstValue = dstComponents[k]; if (subtractive) { srcValue = 1 - srcValue; dstValue = 1 - dstValue; } float value = separableBlendMode.blendChannel(srcValue, dstValue); value = srcValue + dstAlpha * (value - srcValue); value = dstValue + srcAlphaRatio * (value - dstValue); if (subtractive) { value = 1 - value; } dstComponents[k] = value; } } else { // TODO - nonseparable modes } if (dstHasAlpha) { dstComponents[numDstColorComponents] = resultAlpha; } dstPixel = dstColorModel.getDataElements(dstComponents, 0, dstPixel); dstOut.setDataElements(dstOutXShift + x, dstOutYShift + y, dstPixel); } } } public RenderingHints getHints() { return hints; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/blend/BlendMode.java000066400000000000000000000151051320103431700276760ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.blend; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; /** * Blend mode. * * @author Kühn & Weyh Software, GmbH */ public abstract class BlendMode { BlendMode() { } /** * Determines the blend mode from the BM entry in the COS ExtGState. * * @param cosBlendMode name or array * @return blending mode */ public static BlendMode getInstance(COSBase cosBlendMode) { BlendMode result = null; if (cosBlendMode instanceof COSName) { result = BLEND_MODES.get(cosBlendMode); } else if (cosBlendMode instanceof COSArray) { COSArray cosBlendModeArray = (COSArray) cosBlendMode; for (int i = 0; i < cosBlendModeArray.size(); i++) { result = BLEND_MODES.get(cosBlendModeArray.getObject(i)); if (result != null) { break; } } } if (result != null) { return result; } return BlendMode.COMPATIBLE; } public static final SeparableBlendMode NORMAL = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return srcValue; } }; public static final SeparableBlendMode COMPATIBLE = NORMAL; public static final SeparableBlendMode MULTIPLY = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return srcValue * dstValue; } }; public static final SeparableBlendMode SCREEN = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return srcValue + dstValue - srcValue * dstValue; } }; public static final SeparableBlendMode OVERLAY = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return (dstValue <= 0.5) ? 2 * dstValue * srcValue : 2 * (srcValue + dstValue - srcValue * dstValue) - 1; } }; public static final SeparableBlendMode DARKEN = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return Math.min(srcValue, dstValue); } }; public static final SeparableBlendMode LIGHTEN = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return Math.max(srcValue, dstValue); } }; public static final SeparableBlendMode COLOR_DODGE = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return (srcValue < 1) ? Math.min(1, dstValue / (1 - srcValue)) : 1; } }; public static final SeparableBlendMode COLOR_BURN = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return (srcValue > 0) ? 1 - Math.min(1, (1 - dstValue) / srcValue) : 0; } }; public static final SeparableBlendMode HARD_LIGHT = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return (srcValue <= 0.5) ? 2 * dstValue * srcValue : 2 * (srcValue + dstValue - srcValue * dstValue) - 1; } }; public static final SeparableBlendMode SOFT_LIGHT = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { if (srcValue <= 0.5) { return dstValue - (1 - 2 * srcValue) * dstValue * (1 - dstValue); } float d = (dstValue <= 0.25) ? ((16 * dstValue - 12) * dstValue + 4) * dstValue : (float) Math.sqrt(dstValue); return dstValue + (2 * srcValue - 1) * (d - dstValue); } }; public static final SeparableBlendMode DIFFERENCE = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return Math.abs(dstValue - srcValue); } }; public static final SeparableBlendMode EXCLUSION = new SeparableBlendMode() { @Override public float blendChannel(float srcValue, float dstValue) { return dstValue + srcValue - 2 * dstValue * srcValue; } }; // this map *must* come after the declarations above, otherwise its values will be null private static final Map BLEND_MODES = createBlendModeMap(); private static Map createBlendModeMap() { Map map = new HashMap<>(13); map.put(COSName.NORMAL, BlendMode.NORMAL); map.put(COSName.COMPATIBLE, BlendMode.COMPATIBLE); map.put(COSName.MULTIPLY, BlendMode.MULTIPLY); map.put(COSName.SCREEN, BlendMode.SCREEN); map.put(COSName.OVERLAY, BlendMode.OVERLAY); map.put(COSName.DARKEN, BlendMode.DARKEN); map.put(COSName.LIGHTEN, BlendMode.LIGHTEN); map.put(COSName.COLOR_DODGE, BlendMode.COLOR_DODGE); map.put(COSName.COLOR_BURN, BlendMode.COLOR_BURN); map.put(COSName.HARD_LIGHT, BlendMode.HARD_LIGHT); map.put(COSName.SOFT_LIGHT, BlendMode.SOFT_LIGHT); map.put(COSName.DIFFERENCE, BlendMode.DIFFERENCE); map.put(COSName.EXCLUSION, BlendMode.EXCLUSION); // TODO - non-separable blending modes return map; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/blend/NonSeparableBlendMode.java000066400000000000000000000021771320103431700321750ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.blend; /** * Non-separable blend mode (supports blend function). * * @author Kühn & Weyh Software, GmbH */ public abstract class NonSeparableBlendMode extends BlendMode { NonSeparableBlendMode() { } public abstract void blend(float[] srcValues, float[] dstValues, float[] result); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/blend/SeparableBlendMode.java000066400000000000000000000021431320103431700315130ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.blend; /** * Separable blend mode (support blendChannel) * * @author Kühn & Weyh Software, GmbH */ public abstract class SeparableBlendMode extends BlendMode { SeparableBlendMode() { } public abstract float blendChannel(float srcValue, float dstValue); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/000077500000000000000000000000001320103431700252325ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDCIEBasedColorSpace.java000066400000000000000000000052671320103431700316450ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.IOException; /** * CIE-based colour spaces specify colours in a way that is independent of the characteristics * of any particular output device. They are based on an international standard for colour * specification created by the Commission Internationale de l'Éclairage (CIE). * * @author John Hewson */ public abstract class PDCIEBasedColorSpace extends PDColorSpace { // // WARNING: this method is performance sensitive, modify with care! // @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { // This method calls toRGB to convert images one pixel at a time. For matrix-based // CIE color spaces this is fast enough. However, it should not be used with any // color space which uses an ICC Profile as it will be far too slow. int width = raster.getWidth(); int height = raster.getHeight(); BufferedImage rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); WritableRaster rgbRaster = rgbImage.getRaster(); // always three components: ABC float[] abc = new float[3]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.getPixel(x, y, abc); // 0..255 -> 0..1 abc[0] /= 255; abc[1] /= 255; abc[2] /= 255; float[] rgb = toRGB(abc); // 0..1 -> 0..255 rgb[0] *= 255; rgb[1] *= 255; rgb[2] *= 255; rgbRaster.setPixel(x, y, rgb); } } return rgbImage; } @Override public String toString() { return getName(); // TODO return more info } } PDCIEDictionaryBasedColorSpace.java000066400000000000000000000117221320103431700336050ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.color.ColorSpace; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; /** * CIE-based colour spaces that use a dictionary. * * @author Ben Litchfield * @author John Hewson */ public abstract class PDCIEDictionaryBasedColorSpace extends PDCIEBasedColorSpace { protected COSDictionary dictionary; private static final ColorSpace CIEXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); // we need to cache whitepoint values, because using getWhitePoint() // would create a new default object for each pixel conversion if the original // PDF didn't have a whitepoint array protected float wpX = 1; protected float wpY = 1; protected float wpZ = 1; protected PDCIEDictionaryBasedColorSpace(COSName cosName) { array = new COSArray(); dictionary = new COSDictionary(); array.add(cosName); array.add(dictionary); fillWhitepointCache(getWhitepoint()); } /** * Creates a new CalRGB color space using the given COS array. * * @param rgb the cos array which represents this color space */ protected PDCIEDictionaryBasedColorSpace(COSArray rgb) { array = rgb; dictionary = (COSDictionary) array.getObject(1); fillWhitepointCache(getWhitepoint()); } private void fillWhitepointCache(PDTristimulus whitepoint) { wpX = whitepoint.getX(); wpY = whitepoint.getY(); wpZ = whitepoint.getZ(); } protected float[] convXYZtoRGB(float x, float y, float z) { // toRGB() malfunctions with negative values // XYZ must be non-negative anyway: // http://ninedegreesbelow.com/photography/icc-profile-negative-tristimulus.html if (x < 0) { x = 0; } if (y < 0) { y = 0; } if (z < 0) { z = 0; } return CIEXYZ.toRGB(new float[] { x, y, z }); } /** * This will return the whitepoint tristimulus. As this is a required field * this will never return null. A default of 1,1,1 will be returned if the * pdf does not have any values yet. * * @return the whitepoint tristimulus */ public final PDTristimulus getWhitepoint() { COSArray wp = (COSArray) dictionary.getDictionaryObject(COSName.WHITE_POINT); if (wp == null) { wp = new COSArray(); wp.add(new COSFloat(1.0f)); wp.add(new COSFloat(1.0f)); wp.add(new COSFloat(1.0f)); } return new PDTristimulus(wp); } /** * This will return the BlackPoint tristimulus. This is an optional field * but has defaults so this will never return null. A default of 0,0,0 will * be returned if the pdf does not have any values yet. * * @return the blackpoint tristimulus */ public final PDTristimulus getBlackPoint() { COSArray bp = (COSArray) dictionary.getDictionaryObject(COSName.BLACK_POINT); if (bp == null) { bp = new COSArray(); bp.add(new COSFloat(0.0f)); bp.add(new COSFloat(0.0f)); bp.add(new COSFloat(0.0f)); } return new PDTristimulus(bp); } /** * This will set the whitepoint tristimulus. As this is a required field * this null should not be passed into this function. * * @param whitepoint the whitepoint tristimulus */ public void setWhitePoint(PDTristimulus whitepoint) { COSBase wpArray = whitepoint.getCOSObject(); if (wpArray != null) { dictionary.setItem(COSName.WHITE_POINT, wpArray); } fillWhitepointCache(whitepoint); } /** * This will set the BlackPoint tristimulus. As this is a required field * this null should not be passed into this function. * * @param blackpoint the BlackPoint tristimulus */ public void setBlackPoint(PDTristimulus blackpoint) { COSBase bpArray = null; if (blackpoint != null) { bpArray = blackpoint.getCOSObject(); } dictionary.setItem(COSName.BLACK_POINT, bpArray); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDCalGray.java000066400000000000000000000061431320103431700276470ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; /** * A CalGray colour space is a special case of a single-component CIE-based * colour space. * * @author John Hewson * @author Ben Litchfield */ public final class PDCalGray extends PDCIEDictionaryBasedColorSpace { private final PDColor initialColor = new PDColor(new float[] { 0 }, this); /** * Create a new CalGray color space. */ public PDCalGray() { super(COSName.CALGRAY); } /** * Creates a new CalGray color space using the given COS array. * * @param array the COS array which represents this color space */ public PDCalGray(COSArray array) { super(array); } @Override public String getName() { return COSName.CALGRAY.getName(); } @Override public int getNumberOfComponents() { return 1; } @Override public float[] getDefaultDecode(int bitsPerComponent) { return new float[] { 0, 1 }; } @Override public PDColor getInitialColor() { return initialColor; } @Override public float[] toRGB(float[] value) { // see implementation of toRGB in PDCabRGB, and PDFBOX-2971 if (wpX == 1 && wpY == 1 && wpZ == 1) { float a = value[0]; float gamma = getGamma(); float powAG = (float) Math.pow(a, gamma); return convXYZtoRGB(powAG, powAG, powAG); } else { return new float[] { value[0], value[0], value[0] }; } } /** * This will get the gamma value. If none is present then the default of 1 * will be returned. * * @return The gamma value. */ public float getGamma() { float retval = 1.0f; COSNumber gamma = (COSNumber) dictionary.getDictionaryObject(COSName.GAMMA); if (gamma != null) { retval = gamma.floatValue(); } return retval; } /** * Set the gamma value. * * @param value The new gamma value. */ public void setGamma(float value) { dictionary.setItem(COSName.GAMMA, new COSFloat(value)); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDCalRGB.java000066400000000000000000000124341320103431700273570ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.util.Matrix; /** * A CalRGB colour space is a CIE-based colour space with one transformation stage instead of two. * In this type of space, A, B, and C represent calibrated red, green, and blue colour values. * * @author Ben Litchfield * @author John Hewson */ public class PDCalRGB extends PDCIEDictionaryBasedColorSpace { private final PDColor initialColor = new PDColor(new float[] { 0, 0, 0 }, this); /** * Creates a new CalRGB color space. */ public PDCalRGB() { super(COSName.CALRGB); } /** * Creates a new CalRGB color space using the given COS array. * @param rgb the cos array which represents this color space */ public PDCalRGB(COSArray rgb) { super(rgb); } @Override public String getName() { return COSName.CALRGB.getName(); } @Override public int getNumberOfComponents() { return 3; } @Override public float[] getDefaultDecode(int bitsPerComponent) { return new float[] { 0, 1, 0, 1, 0, 1 }; } @Override public PDColor getInitialColor() { return initialColor; } @Override public float[] toRGB(float[] value) { if (wpX == 1 && wpY == 1 && wpZ == 1) { float a = value[0]; float b = value[1]; float c = value[2]; PDGamma gamma = getGamma(); float powAR = (float)Math.pow(a, gamma.getR()); float powBG = (float)Math.pow(b, gamma.getG()); float powCB = (float)Math.pow(c, gamma.getB()); float[] matrix = getMatrix(); float mXA = matrix[0]; float mYA = matrix[1]; float mZA = matrix[2]; float mXB = matrix[3]; float mYB = matrix[4]; float mZB = matrix[5]; float mXC = matrix[6]; float mYC = matrix[7]; float mZC = matrix[8]; float x = mXA * powAR + mXB * powBG + mXC * powCB; float y = mYA * powAR + mYB * powBG + mYC * powCB; float z = mZA * powAR + mZB * powBG + mZC * powCB; return convXYZtoRGB(x, y, z); } else { // this is a hack, we simply skip CIE calibration of the RGB value // this works only with whitepoint D65 (0.9505 1.0 1.089) // see PDFBOX-2553 return new float[] { value[0], value[1], value[2] }; } } /** * Returns the gamma value. * If none is present then the default of 1,1,1 will be returned. * @return the gamma value */ public final PDGamma getGamma() { COSArray gammaArray = (COSArray) dictionary.getDictionaryObject(COSName.GAMMA); if (gammaArray == null) { gammaArray = new COSArray(); gammaArray.add(new COSFloat(1.0f)); gammaArray.add(new COSFloat(1.0f)); gammaArray.add(new COSFloat(1.0f)); dictionary.setItem(COSName.GAMMA, gammaArray); } return new PDGamma(gammaArray); } /** * Returns the linear interpretation matrix, which is an array of nine numbers. * If the underlying dictionary contains null then the identity matrix will be returned. * @return the linear interpretation matrix */ public final float[] getMatrix() { COSArray matrix = (COSArray)dictionary.getDictionaryObject(COSName.MATRIX); if (matrix == null) { return new float[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 }; } else { return matrix.toFloatArray(); } } /** * Sets the gamma value. * @param gamma the new gamma value */ public final void setGamma(PDGamma gamma) { COSArray gammaArray = null; if(gamma != null) { gammaArray = gamma.getCOSArray(); } dictionary.setItem(COSName.GAMMA, gammaArray); } /** * Sets the linear interpretation matrix. * Passing in null will clear the matrix. * @param matrix the new linear interpretation matrix, or null */ public final void setMatrix(Matrix matrix) { COSArray matrixArray = null; if(matrix != null) { matrixArray = matrix.toCOSArray(); } dictionary.setItem(COSName.MATRIX, matrixArray); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDColor.java000066400000000000000000000140731320103431700274040ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.io.IOException; import java.util.Arrays; import org.sejda.sambox.cos.*; /** * A color value, consisting of one or more color components, or for pattern color spaces, a name and optional color * components. Color values are not associated with any given color space. * * Instances of PDColor are immutable. * * @author John Hewson */ public final class PDColor { private final float[] components; private final COSName patternName; private final PDColorSpace colorSpace; /** * Creates a PDColor containing the given color value. * * @param array a COS array containing the color value * @param colorSpace color space in which the color value is defined */ public PDColor(COSArray array, PDColorSpace colorSpace) { if (array.size() > 0 && array.get(array.size() - 1) instanceof COSName) { // color components (optional) components = new float[array.size() - 1]; for (int i = 0; i < array.size() - 1; i++) { components[i] = ((COSNumber) array.get(i)).floatValue(); } // pattern name (required) patternName = (COSName) array.get(array.size() - 1); } else { // color components only components = new float[array.size()]; for (int i = 0; i < array.size(); i++) { COSBase component = array.get(i); if(component instanceof COSNumber) { components[i] = ((COSNumber) array.get(i)).floatValue(); } else { components[i] = 0f; } } patternName = null; } this.colorSpace = colorSpace; } /** * Creates a PDColor containing the given color component values. * * @param components array of color component values * @param colorSpace color space in which the components are defined */ public PDColor(float[] components, PDColorSpace colorSpace) { this.components = components.clone(); this.patternName = null; this.colorSpace = colorSpace; } /** * Creates a PDColor containing the given pattern name. * * @param patternName the name of a pattern in a pattern dictionary * @param colorSpace color space in which the pattern is defined */ public PDColor(COSName patternName, PDColorSpace colorSpace) { this.components = new float[0]; this.patternName = patternName; this.colorSpace = colorSpace; } /** * Creates a PDColor containing the given color component values and pattern name. * * @param components array of color component values * @param patternName the name of a pattern in a pattern dictionary * @param colorSpace color space in which the pattern/components are defined */ public PDColor(float[] components, COSName patternName, PDColorSpace colorSpace) { this.components = components.clone(); this.patternName = patternName; this.colorSpace = colorSpace; } /** * Returns the components of this color value. * * @return the components of this color value */ public float[] getComponents() { return components.clone(); } /** * Returns the pattern name from this color value. * * @return the pattern name from this color value */ public COSName getPatternName() { return patternName; } /** * Returns true if this color value is a pattern. * * @return true if this color value is a pattern */ public boolean isPattern() { return patternName != null; } /** * Returns the packed RGB value for this color, if any. * * @return RGB * @throws IOException if the color conversion fails * @throws IllegalStateException if this color value is a pattern. */ public int toRGB() throws IOException { float[] floats = colorSpace.toRGB(components); int r = Math.round(floats[0] * 255); int g = Math.round(floats[1] * 255); int b = Math.round(floats[2] * 255); int rgb = r; rgb = (rgb << 8) + g; rgb = (rgb << 8) + b; return rgb; } /** * Returns this color value as a COS array * * @return the color value as a COS array */ public COSArray toCOSArray() { COSArray array = new COSArray(); array.setFloatArray(components); if (patternName != null) { array.add(patternName); } return array; } /** * @return the color value as a COSarray containing onlye the components values, no pattern name. */ public COSArray toComponentsCOSArray() { COSArray array = new COSArray(); array.setFloatArray(components); return array; } /** * Returns the color space in which this color value is defined. */ public PDColorSpace getColorSpace() { return colorSpace; } @Override public String toString() { return "PDColor{components=" + Arrays.toString(components) + ", patternName=" + patternName + "}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDColorSpace.java000066400000000000000000000253231320103431700303600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorConvertOp; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.MissingResourceException; import org.sejda.sambox.pdmodel.PDResources; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A color space specifies how the colours of graphics objects will be painted on the page. * * @author John Hewson * @author Ben Litchfield */ public abstract class PDColorSpace implements COSObjectable { private static final Logger LOG = LoggerFactory.getLogger(PDColorSpace.class); /** * Creates a color space given a name or array. * * @param colorSpace the color space COS object * @return a new color space * @throws IOException if the color space is unknown or cannot be created */ public static PDColorSpace create(COSBase colorSpace) throws IOException { return create(colorSpace, null); } /** * Creates a color space given a name or array. Abbreviated device color names are not supported here, please * replace them first. * * @param colorSpace the color space COS object * @param resources the current resources. * @return a new color space * @throws MissingResourceException if the color space is missing in the resources dictionary * @throws IOException if the color space is unknown or cannot be created */ public static PDColorSpace create(COSBase colorSpace, PDResources resources) throws IOException { return create(colorSpace, resources, false); } /** * Creates a color space given a name or array. Abbreviated device color names are not supported here, please * replace them first. This method is for PDFBox internal use only, others should use {@link create(COSBase, * PDResources)}. * * @param colorSpace the color space COS object * @param resources the current resources. * @param wasDefault if current color space was used by a default color space. * @return a new color space. * @throws MissingResourceException if the color space is missing in the resources dictionary * @throws IOException if the color space is unknown or cannot be created. */ public static PDColorSpace create(COSBase colorSpace, PDResources resources, boolean wasDefault) throws IOException { colorSpace = colorSpace.getCOSObject(); if (colorSpace instanceof COSName) { COSName name = (COSName) colorSpace; // default color spaces if (resources != null) { COSName defaultName = null; if (name.equals(COSName.DEVICECMYK) && resources.hasColorSpace(COSName.DEFAULT_CMYK)) { defaultName = COSName.DEFAULT_CMYK; } else if (name.equals(COSName.DEVICERGB) && resources.hasColorSpace(COSName.DEFAULT_RGB)) { defaultName = COSName.DEFAULT_RGB; } else if (name.equals(COSName.DEVICEGRAY) && resources.hasColorSpace(COSName.DEFAULT_GRAY)) { defaultName = COSName.DEFAULT_GRAY; } if (resources.hasColorSpace(defaultName) && !wasDefault) { return resources.getColorSpace(defaultName, true); } } // built-in color spaces if (name == COSName.DEVICECMYK) { return PDDeviceCMYK.INSTANCE; } else if (name == COSName.DEVICERGB) { return PDDeviceRGB.INSTANCE; } else if (name == COSName.DEVICEGRAY) { return PDDeviceGray.INSTANCE; } else if (name == COSName.PATTERN) { return new PDPattern(resources); } else if (resources != null) { if (!resources.hasColorSpace(name)) { throw new MissingResourceException("Missing color space: " + name.getName()); } return resources.getColorSpace(name); } else { throw new MissingResourceException("Unknown color space: " + name.getName()); } } else if (colorSpace instanceof COSArray) { COSArray array = (COSArray) colorSpace; if (array.size() == 0) { throw new IOException("Colorspace array is empty"); } COSBase base = array.getObject(0); if (!(base instanceof COSName)) { throw new IOException("First element in colorspace array must be a name"); } COSName name = (COSName) base; // TODO cache these returned color spaces? if (name == COSName.CALGRAY) { return new PDCalGray(array); } else if (name == COSName.CALRGB) { return new PDCalRGB(array); } else if (name == COSName.DEVICEN) { return new PDDeviceN(array); } else if (name == COSName.INDEXED) { return new PDIndexed(array); } else if (name == COSName.SEPARATION) { return new PDSeparation(array); } else if (name == COSName.ICCBASED) { return new PDICCBased(array); } else if (name == COSName.LAB) { return new PDLab(array); } else if (name == COSName.PATTERN) { if (array.size() == 1) { return new PDPattern(resources); } return new PDPattern(resources, PDColorSpace.create(array.get(1))); } else if (name == COSName.DEVICECMYK || name == COSName.DEVICERGB || name == COSName.DEVICEGRAY) { // not allowed in an array, but we sometimes encounter these regardless return create(name, resources, wasDefault); } else { throw new IOException("Invalid color space kind: " + name); } } else if (colorSpace instanceof COSDictionary) { COSDictionary csAsDic = (COSDictionary) colorSpace; if (csAsDic.containsKey(COSName.COLORSPACE)) { LOG.warn("Found invalid color space defined as dictionary {}", csAsDic); return create(csAsDic.getDictionaryObject(COSName.COLORSPACE), resources, wasDefault); } } throw new IOException("Expected a name or array but got: " + colorSpace); } // array for the given parameters protected COSArray array; /** * Returns the name of the color space. * * @return the name of the color space */ public abstract String getName(); /** * Returns the number of components in this color space * * @return the number of components in this color space */ public abstract int getNumberOfComponents(); /** * Returns the default decode array for this color space. * * @param bitsPerComponent the number of bits per component. * @return the default decode array */ public abstract float[] getDefaultDecode(int bitsPerComponent); /** * Returns the initial color value for this color space. * * @return the initial color value for this color space */ public abstract PDColor getInitialColor(); /** * Returns the RGB equivalent of the given color value. * * @param value a color value with component values between 0 and 1 * @return an array of R,G,B value between 0 and 255 * @throws IOException if the color conversion fails */ public abstract float[] toRGB(float[] value) throws IOException; /** * Returns the (A)RGB equivalent of the given raster. * * @param raster the source raster * @return an (A)RGB buffered image * @throws IOException if the color conversion fails */ public abstract BufferedImage toRGBImage(WritableRaster raster) throws IOException; /** * Returns the (A)RGB equivalent of the given raster, using the given AWT color space to perform the conversion. * * @param raster the source raster * @param colorSpace the AWT * @return an (A)RGB buffered image */ protected BufferedImage toRGBImageAWT(WritableRaster raster, ColorSpace colorSpace) { // // WARNING: this method is performance sensitive, modify with care! // // ICC Profile color transforms are only fast when performed using ColorConvertOp ColorModel colorModel = new ComponentColorModel(colorSpace, false, false, Transparency.OPAQUE, raster.getDataBuffer().getDataType()); BufferedImage src = new BufferedImage(colorModel, raster, false, null); BufferedImage dest = new BufferedImage(raster.getWidth(), raster.getHeight(), BufferedImage.TYPE_INT_RGB); ColorConvertOp op = new ColorConvertOp(null); op.filter(src, dest); return dest; } @Override public COSBase getCOSObject() { return array; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDDeviceCMYK.java000066400000000000000000000140511320103431700302050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.util.Arrays; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_Profile; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.InputStream; import java.net.URL; import org.sejda.sambox.cos.COSName; /** * Allows colors to be specified according to the subtractive CMYK (cyan, magenta, yellow, black) * model typical of printers and other paper-based output devices. * * @author John Hewson * @author Ben Litchfield */ public class PDDeviceCMYK extends PDDeviceColorSpace { /** The single instance of this class. */ public static PDDeviceCMYK INSTANCE; static { try { INSTANCE = new PDDeviceCMYK(); } catch (IOException e) { throw new RuntimeException(e); } } private final PDColor initialColor = new PDColor(new float[] { 0, 0, 0, 1 }, this); private final ICC_ColorSpace awtColorSpace; private boolean usePureJavaCMYKConversion = false; protected PDDeviceCMYK() throws IOException { // loads the ICC color profile for CMYK ICC_Profile iccProfile = getICCProfile(); if (iccProfile == null) { throw new IOException("Default CMYK color profile could not be loaded"); } awtColorSpace = new ICC_ColorSpace(iccProfile); // there is a JVM bug which results in a CMMException which appears to be a race // condition caused by lazy initialization of the color transform, so we perform // an initial color conversion while we're still in a static context, see PDFBOX-2184 awtColorSpace.toRGB(new float[] { 0, 0, 0, 0 }); usePureJavaCMYKConversion = System .getProperty("org.sejda.sambox.rendering.UsePureJavaCMYKConversion") != null; } protected ICC_Profile getICCProfile() throws IOException { // Adobe Acrobat uses "U.S. Web Coated (SWOP) v2" as the default // CMYK profile, however it is not available under an open license. // Instead, the "ISO Coated v2 300% (basICColor)" is used, which // is an open alternative to the "ISO Coated v2 300% (ECI)" profile. String name = "org/sejda/sambox/resources/icc/ISOcoated_v2_300_bas.icc"; URL url = PDDeviceCMYK.class.getClassLoader().getResource(name); if (url == null) { throw new IOException("Error loading resource: " + name); } InputStream input = url.openStream(); ICC_Profile iccProfile = ICC_Profile.getInstance(input); input.close(); return iccProfile; } @Override public String getName() { return COSName.DEVICECMYK.getName(); } @Override public int getNumberOfComponents() { return 4; } @Override public float[] getDefaultDecode(int bitsPerComponent) { return new float[] { 0, 1, 0, 1, 0, 1, 0, 1 }; } @Override public PDColor getInitialColor() { return initialColor; } @Override public float[] toRGB(float[] value) { return awtColorSpace.toRGB(value); } @Override public BufferedImage toRGBImage(WritableRaster raster) { return toRGBImageAWT(raster, awtColorSpace); } @Override protected BufferedImage toRGBImageAWT(WritableRaster raster, ColorSpace colorSpace) { if (usePureJavaCMYKConversion) { BufferedImage dest = new BufferedImage(raster.getWidth(), raster.getHeight(), BufferedImage.TYPE_INT_RGB); ColorSpace destCS = dest.getColorModel().getColorSpace(); WritableRaster destRaster = dest.getRaster(); float[] srcValues = new float[4]; float[] lastValues = new float[] { -1.0f, -1.0f, -1.0f, -1.0f }; float[] destValues = new float[3]; int width = raster.getWidth(); int startX = raster.getMinX(); int height = raster.getHeight(); int startY = raster.getMinY(); for (int x = startX; x < width + startX; x++) { for (int y = startY; y < height + startY; y++) { raster.getPixel(x, y, srcValues); // check if the last value can be reused if (!Arrays.equals(lastValues, srcValues)) { for (int k = 0; k < 4; k++) { lastValues[k] = srcValues[k]; srcValues[k] = srcValues[k] / 255f; } // use CIEXYZ as intermediate format to optimize the color conversion destValues = destCS.fromCIEXYZ(colorSpace.toCIEXYZ(srcValues)); for (int k = 0; k < destValues.length; k++) { destValues[k] = destValues[k] * 255f; } } destRaster.setPixel(x, y, destValues); } } return dest; } else { return super.toRGBImageAWT(raster, colorSpace); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDDeviceColorSpace.java000066400000000000000000000024411320103431700314740ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; /** * Device colour spaces directly specify colours or shades of gray produced by an output device. * * @author John Hewson */ public abstract class PDDeviceColorSpace extends PDColorSpace { @Override public String toString() { return getName(); } @Override public COSBase getCOSObject() { return COSName.getPDFName(getName()); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDDeviceGray.java000066400000000000000000000051361320103431700303500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSName; /** * A color space with black, white, and intermediate shades of gray. * * @author Ben Litchfield * @author John Hewson */ public final class PDDeviceGray extends PDDeviceColorSpace { /** The single instance of this class. */ public static final PDDeviceGray INSTANCE = new PDDeviceGray(); private final PDColor initialColor = new PDColor(new float[] { 0 }, this); private PDDeviceGray() { } @Override public String getName() { return COSName.DEVICEGRAY.getName(); } @Override public int getNumberOfComponents() { return 1; } @Override public float[] getDefaultDecode(int bitsPerComponent) { return new float[] { 0, 1 }; } @Override public PDColor getInitialColor() { return initialColor; } @Override public float[] toRGB(float[] value) { return new float[] { value[0], value[0], value[0] }; } @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { int width = raster.getWidth(); int height = raster.getHeight(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); int[] gray = new int[1]; int[] rgb = new int[3]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.getPixel(x, y, gray); rgb[0] = gray[0]; rgb[1] = gray[0]; rgb[2] = gray[0]; image.getRaster().setPixel(x, y, rgb); } } return image; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDDeviceN.java000066400000000000000000000444221320103431700276440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.pdmodel.common.function.PDFunction; /** * DeviceN colour spaces may contain an arbitrary number of colour components. * DeviceN represents a colour space containing multiple components that correspond to colorants * of some target device. As with Separation colour spaces, readers are able to approximate the * colorants if they are not available on the current output device, such as a display * * @author John Hewson * @author Ben Litchfield */ public class PDDeviceN extends PDSpecialColorSpace { // array indexes private static final int COLORANT_NAMES = 1; private static final int ALTERNATE_CS = 2; private static final int TINT_TRANSFORM = 3; private static final int DEVICEN_ATTRIBUTES = 4; // fields private PDColorSpace alternateColorSpace = null; private PDFunction tintTransform = null; private PDDeviceNAttributes attributes; private PDColor initialColor; // color conversion cache private int numColorants; private int[] colorantToComponent; private PDColorSpace processColorSpace; private PDSeparation[] spotColorSpaces; /** * Creates a new DeviceN color space. */ public PDDeviceN() { array = new COSArray(); array.add(COSName.DEVICEN); // empty placeholder array.add(COSNull.NULL); array.add(COSNull.NULL); array.add(COSNull.NULL); } /** * Creates a new DeviceN color space from the given COS array. * @param deviceN an array containing the color space information */ public PDDeviceN(COSArray deviceN) throws IOException { array = deviceN; alternateColorSpace = PDColorSpace.create(array.getObject(ALTERNATE_CS)); tintTransform = PDFunction.create(array.getObject(TINT_TRANSFORM)); if (array.size() > DEVICEN_ATTRIBUTES) { attributes = new PDDeviceNAttributes((COSDictionary)array.getObject(DEVICEN_ATTRIBUTES)); } initColorConversionCache(); // set initial color space int n = getNumberOfComponents(); float[] initial = new float[n]; for (int i = 0; i < n; i++) { initial[i] = 1; } initialColor = new PDColor(initial, this); } // initializes the color conversion cache private void initColorConversionCache() throws IOException { // there's nothing to cache for non-attribute spaces if (attributes == null) { return; } // colorant names List colorantNames = getColorantNames(); numColorants = colorantNames.size(); // process components colorantToComponent = new int[numColorants]; for (int c = 0; c < numColorants; c++) { colorantToComponent[c] = -1; } if (attributes.getProcess() != null) { List components = attributes.getProcess().getComponents(); // map each colorant to the corresponding process component (if any) for (int c = 0; c < numColorants; c++) { colorantToComponent[c] = components.indexOf(colorantNames.get(c)); } // process color space processColorSpace = attributes.getProcess().getColorSpace(); } // spot colorants spotColorSpaces = new PDSeparation[numColorants]; // spot color spaces Map spotColorants = attributes.getColorants(); // map each colorant to the corresponding spot color space for (int c = 0; c < numColorants; c++) { String name = colorantNames.get(c); PDSeparation spot = spotColorants.get(name); if (spot != null) { // spot colorant spotColorSpaces[c] = spot; // spot colors may replace process colors with same name // providing that the subtype is not NChannel. if (!isNChannel()) { colorantToComponent[c] = -1; } } else { // process colorant spotColorSpaces[c] = null; } } } @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { if (attributes != null) { return toRGBWithAttributes(raster); } else { return toRGBWithTintTransform(raster); } } // // WARNING: this method is performance sensitive, modify with care! // private BufferedImage toRGBWithAttributes(WritableRaster raster) throws IOException { int width = raster.getWidth(); int height = raster.getHeight(); BufferedImage rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); WritableRaster rgbRaster = rgbImage.getRaster(); // white background Graphics2D g = rgbImage.createGraphics(); g.setBackground(Color.WHITE); g.clearRect(0, 0, width, height); g.dispose(); // look up each colorant for (int c = 0; c < numColorants; c++) { PDColorSpace componentColorSpace; if (colorantToComponent[c] >= 0) { // process color componentColorSpace = processColorSpace; } else if (spotColorSpaces[c] == null) { // TODO this happens in the Altona Visual test, is there a better workaround? // missing spot color, fallback to using tintTransform return toRGBWithTintTransform(raster); } else { // spot color componentColorSpace = spotColorSpaces[c]; } // copy single-component to its own raster in the component color space WritableRaster componentRaster = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, width, height, componentColorSpace.getNumberOfComponents(), new Point(0, 0)); int[] samples = new int[numColorants]; int[] componentSamples = new int[componentColorSpace.getNumberOfComponents()]; boolean isProcessColorant = colorantToComponent[c] >= 0; int componentIndex = colorantToComponent[c]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.getPixel(x, y, samples); if (isProcessColorant) { // process color componentSamples[componentIndex] = samples[c]; } else { // spot color componentSamples[0] = samples[c]; } componentRaster.setPixel(x, y, componentSamples); } } // convert single-component raster to RGB BufferedImage rgbComponentImage = componentColorSpace.toRGBImage(componentRaster); WritableRaster rgbComponentRaster = rgbComponentImage.getRaster(); // combine the RGB component with the RGB composite raster int[] rgbChannel = new int[3]; int[] rgbComposite = new int[3]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { rgbComponentRaster.getPixel(x, y, rgbChannel); rgbRaster.getPixel(x, y, rgbComposite); // multiply (blend mode) rgbChannel[0] = rgbChannel[0] * rgbComposite[0] >> 8; rgbChannel[1] = rgbChannel[1] * rgbComposite[1] >> 8; rgbChannel[2] = rgbChannel[2] * rgbComposite[2] >> 8; rgbRaster.setPixel(x, y, rgbChannel); } } } return rgbImage; } // // WARNING: this method is performance sensitive, modify with care! // private BufferedImage toRGBWithTintTransform(WritableRaster raster) throws IOException { // map only in use if one color component Map map1 = new HashMap<>(); float key = 0; int width = raster.getWidth(); int height = raster.getHeight(); // use the tint transform to convert the sample into // the alternate color space (this is usually 1:many) BufferedImage rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); WritableRaster rgbRaster = rgbImage.getRaster(); int[] rgb = new int[3]; int numSrcComponents = getColorantNames().size(); float[] src = new float[numSrcComponents]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.getPixel(x, y, src); if (numSrcComponents == 1) { int[] pxl = map1.get(src[0]); if (pxl != null) { rgbRaster.setPixel(x, y, pxl); continue; } // need to remember key because src is modified key = src[0]; } // scale to 0..1 for (int s = 0; s < numSrcComponents; s++) { src[s] = src[s] / 255; } // convert to alternate color space via tint transform float[] result = tintTransform.eval(src); // convert from alternate color space to RGB float[] rgbFloat = alternateColorSpace.toRGB(result); for (int s = 0; s < 3; s++) { // scale to 0..255 rgb[s] = (int) (rgbFloat[s] * 255f); } if (numSrcComponents == 1) { // must clone because rgb is reused map1.put(key, rgb.clone()); } rgbRaster.setPixel(x, y, rgb); } } return rgbImage; } @Override public float[] toRGB(float[] value) throws IOException { if (attributes != null) { return toRGBWithAttributes(value); } else { return toRGBWithTintTransform(value); } } private float[] toRGBWithAttributes(float[] value) throws IOException { float[] rgbValue = new float[] { 1, 1, 1 }; // look up each colorant for (int c = 0; c < numColorants; c++) { PDColorSpace componentColorSpace; if (colorantToComponent[c] >= 0) { // process color componentColorSpace = processColorSpace; } else if (spotColorSpaces[c] == null) { // TODO this happens in the Altona Visual test, is there a better workaround? // missing spot color, fallback to using tintTransform return toRGBWithTintTransform(value); } else { // spot color componentColorSpace = spotColorSpaces[c]; } // get the single component boolean isProcessColorant = colorantToComponent[c] >= 0; float[] componentSamples = new float[componentColorSpace.getNumberOfComponents()]; int componentIndex = colorantToComponent[c]; if (isProcessColorant) { // process color componentSamples[componentIndex] = value[c]; } else { // spot color componentSamples[0] = value[c]; } // convert single component to RGB float[] rgbComponent = componentColorSpace.toRGB(componentSamples); // combine the RGB component value with the RGB composite value // multiply (blend mode) rgbValue[0] *= rgbComponent[0]; rgbValue[1] *= rgbComponent[1]; rgbValue[2] *= rgbComponent[2]; } return rgbValue; } private float[] toRGBWithTintTransform(float[] value) throws IOException { // use the tint transform to convert the sample into // the alternate color space (this is usually 1:many) float[] altValue = tintTransform.eval(value); // convert the alternate color space to RGB return alternateColorSpace.toRGB(altValue); } /** * Returns true if this color space has the NChannel subtype. * @return true if subtype is NChannel */ public boolean isNChannel() { return attributes != null && attributes.isNChannel(); } @Override public String getName() { return COSName.DEVICEN.getName(); } @Override public final int getNumberOfComponents() { return getColorantNames().size(); } @Override public float[] getDefaultDecode(int bitsPerComponent) { int n = getNumberOfComponents(); float[] decode = new float[n * 2]; for (int i = 0; i < n; i++) { decode[i * 2 + 1] = 1; } return decode; } @Override public PDColor getInitialColor() { return initialColor; } /** * Returns the list of colorants. * @return the list of colorants */ public List getColorantNames() { COSArray names = (COSArray)array.getObject(COLORANT_NAMES); return COSArrayList.convertCOSNameCOSArrayToList(names); } /** * Returns the attributes associated with the DeviceN color space. * @return the DeviceN attributes */ public PDDeviceNAttributes getAttributes() { return attributes; } /** * Sets the list of colorants * @param names the list of colorants */ public void setColorantNames(List names) { COSArray namesArray = COSArrayList.convertStringListToCOSNameCOSArray(names); array.set(COLORANT_NAMES, namesArray); } /** * Sets the color space attributes. * If null is passed in then all attribute will be removed. * @param attributes the color space attributes, or null */ public void setAttributes(PDDeviceNAttributes attributes) { this.attributes = attributes; if (attributes == null) { array.remove(DEVICEN_ATTRIBUTES); } else { // make sure array is large enough while (array.size() <= DEVICEN_ATTRIBUTES) { array.add(COSNull.NULL); } array.set(DEVICEN_ATTRIBUTES, attributes.getCOSDictionary()); } } /** * This will get the alternate color space for this separation. * * @return The alternate color space. * * @throws IOException If there is an error getting the alternate color * space. */ public PDColorSpace getAlternateColorSpace() throws IOException { if (alternateColorSpace == null) { alternateColorSpace = PDColorSpace.create(array.getObject(ALTERNATE_CS)); } return alternateColorSpace; } /** * This will set the alternate color space. * * @param cs The alternate color space. */ public void setAlternateColorSpace(PDColorSpace cs) { alternateColorSpace = cs; COSBase space = null; if (cs != null) { space = cs.getCOSObject(); } array.set(ALTERNATE_CS, space); } /** * This will get the tint transform function. * * @return The tint transform function. * * @throws IOException if there is an error creating the function. */ public PDFunction getTintTransform() throws IOException { if (tintTransform == null) { tintTransform = PDFunction.create(array.getObject(TINT_TRANSFORM)); } return tintTransform; } /** * This will set the tint transform function. * * @param tint The tint transform function. */ public void setTintTransform(PDFunction tint) { tintTransform = tint; array.set(TINT_TRANSFORM, tint); } @Override public String toString() { StringBuilder sb = new StringBuilder(getName()); sb.append('{'); for (String col : getColorantNames()) { sb.append('\"'); sb.append(col); sb.append("\" "); } sb.append(alternateColorSpace.getName()); sb.append(' '); sb.append(tintTransform); sb.append(' '); if (attributes != null) { sb.append(attributes); } sb.append('}'); return sb.toString(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDDeviceNAttributes.java000066400000000000000000000117461320103431700317160ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.COSDictionaryMap; /** * Contains additional information about the components of colour space. * Instead of using the alternate color space and tint transform, conforming readers may use custom * blending algorithms, along with other information provided in the attributes dictionary. * * @author Ben Litchfield */ public final class PDDeviceNAttributes { private final COSDictionary dictionary; /** * Creates a new DeviceN colour space attributes dictionary. */ public PDDeviceNAttributes() { dictionary = new COSDictionary(); } /** * Creates a new DeviceN colour space attributes dictionary from the given dictionary. * @param attributes a dictionary that has all of the attributes */ public PDDeviceNAttributes(COSDictionary attributes) { dictionary = attributes; } /** * Returns the underlying COS dictionary. * @return the dictionary that this object wraps */ public COSDictionary getCOSDictionary() { return dictionary; } /** * Returns a map of colorants and their associated Separation color space. * @return map of colorants to color spaces * @throws IOException If there is an error reading a color space */ public Map getColorants() throws IOException { Map actuals = new HashMap(); COSDictionary colorants = (COSDictionary)dictionary.getDictionaryObject(COSName.COLORANTS); if(colorants == null) { colorants = new COSDictionary(); dictionary.setItem(COSName.COLORANTS, colorants); } for(COSName name : colorants.keySet()) { COSBase value = colorants.getDictionaryObject(name); actuals.put(name.getName(), (PDSeparation)PDColorSpace.create(value)); } return new COSDictionaryMap(actuals, colorants); } /** * Returns the DeviceN Process Dictionary, or null if it is missing. * @return the DeviceN Process Dictionary, or null if it is missing. */ public PDDeviceNProcess getProcess() { COSDictionary process = (COSDictionary)dictionary.getDictionaryObject(COSName.PROCESS); if (process == null) { return null; } return new PDDeviceNProcess(process); } /** * Returns true if this is an NChannel (PDF 1.6) color space. * @return true if this is an NChannel color space. */ public boolean isNChannel() { return "NChannel".equals(dictionary.getNameAsString(COSName.SUBTYPE)); } /** * Sets the colorant map. * @param colorants the map of colorants */ public void setColorants(Map colorants) { COSDictionary colorantDict = null; if(colorants != null) { colorantDict = COSDictionaryMap.convert(colorants); } dictionary.setItem(COSName.COLORANTS, colorantDict); } @Override public String toString() { StringBuilder sb = new StringBuilder(dictionary.getNameAsString(COSName.SUBTYPE)); sb.append('{'); PDDeviceNProcess process = getProcess(); if (process != null) { sb.append(getProcess()); sb.append(' '); } Map colorants; try { colorants = getColorants(); sb.append("Colorants{"); for (Map.Entry col : colorants.entrySet()) { sb.append('\"'); sb.append(col.getKey()); sb.append("\": "); sb.append(col.getValue()); sb.append(' '); } sb.append('}'); } catch (IOException e) { sb.append("ERROR"); } sb.append('}'); return sb.toString(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDDeviceNProcess.java000066400000000000000000000064331320103431700312030ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * A DeviceN Process Dictionary * * @author John Hewson */ public class PDDeviceNProcess { private final COSDictionary dictionary; /** * Creates a new DeviceN Process Dictionary. */ public PDDeviceNProcess() { dictionary = new COSDictionary(); } /** * Creates a new DeviceN Process Dictionary from the given attributes. * @param attributes a DeviceN attributes dictionary */ public PDDeviceNProcess(COSDictionary attributes) { dictionary = attributes; } /** * Returns the underlying COS dictionary. * @return the underlying COS dictionary. */ public COSDictionary getCOSDictionary() { return dictionary; } /** * Returns the process color space * @return the process color space * @throws IOException if the color space cannot be read */ public PDColorSpace getColorSpace() throws IOException { COSBase cosColorSpace = dictionary.getDictionaryObject(COSName.COLORSPACE); if (cosColorSpace == null) { return null; // TODO: return a default? } return PDColorSpace.create(cosColorSpace); } /** * Returns the names of the color components. * @return the names of the color components */ public List getComponents() { List components = new ArrayList(); COSArray cosComponents = (COSArray)dictionary.getDictionaryObject(COSName.COMPONENTS); if (cosComponents == null) { return components; } for (COSBase name : cosComponents) { components.add(((COSName)name).getName()); } return components; } @Override public String toString() { StringBuilder sb = new StringBuilder("Process{"); try { sb.append(getColorSpace()); for (String component : getComponents()) { sb.append(" \""); sb.append(component); sb.append('\"'); } } catch (IOException e) { sb.append("ERROR"); } sb.append('}'); return sb.toString(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDDeviceRGB.java000066400000000000000000000105201320103431700300510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSName; /** * Colours in the DeviceRGB colour space are specified according to the additive * RGB (red-green-blue) colour model. * * @author Ben Litchfield * @author John Hewson */ public final class PDDeviceRGB extends PDDeviceColorSpace { /** This is the single instance of this class. */ public static final PDDeviceRGB INSTANCE = new PDDeviceRGB(); private final PDColor initialColor = new PDColor(new float[] { 0, 0, 0 }, this); private volatile ColorSpace awtColorSpace; private PDDeviceRGB() { } /** * Lazy setting of the AWT color space due to JDK race condition. */ private void init() { // no need to synchronize this check as it is atomic if (awtColorSpace != null) { return; } synchronized (this) { // we might have been waiting for another thread, so check again if (awtColorSpace != null) { return; } awtColorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); // there is a JVM bug which results in a CMMException which appears to be a race // condition caused by lazy initialization of the color transform, so we perform // an initial color conversion while we're still synchronized, see PDFBOX-2184 awtColorSpace.toRGB(new float[] { 0, 0, 0, 0 }); } } @Override public String getName() { return COSName.DEVICERGB.getName(); } /** * {@inheritDoc} */ @Override public int getNumberOfComponents() { return 3; } @Override public float[] getDefaultDecode(int bitsPerComponent) { return new float[] { 0, 1, 0, 1, 0, 1 }; } @Override public PDColor getInitialColor() { return initialColor; } @Override public float[] toRGB(float[] value) { return value; } @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { init(); ColorModel colorModel = new ComponentColorModel(awtColorSpace, false, false, Transparency.OPAQUE, raster.getDataBuffer().getDataType()); BufferedImage image = new BufferedImage(colorModel, raster, false, null); // // WARNING: this method is performance sensitive, modify with care! // // Please read PDFBOX-3854 and look at the related commits first. // The current code returns TYPE_INT_RGB images which prevents slowness due to threads // blocking each other when TYPE_CUSTOM images are used. // ColorConvertOp is not used here because it has a larger memory footprint and no further // performance improvement. // The multiparameter setRGB() call is not used because it brings no improvement. BufferedImage dest = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); int width = image.getWidth(); int height = image.getHeight(); for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { dest.setRGB(x, y, image.getRGB(x, y)); } } return dest; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDGamma.java000066400000000000000000000062751320103431700273550ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; /** * A gamma array, or collection of three floating point parameters used for color operations. * * @author Ben Litchfield */ public final class PDGamma implements COSObjectable { private COSArray values = null; /** * Creates a new gamma. * Defaults all values to 0, 0, 0. */ public PDGamma() { values = new COSArray(); values.add(new COSFloat(0.0f)); values.add(new COSFloat(0.0f)); values.add(new COSFloat(0.0f)); } /** * Creates a new gamma from a COS array. * @param array the array containing the XYZ values */ public PDGamma(COSArray array) { values = array; } /** * Convert this standard java object to a COS object. * @return the cos object that matches this Java object */ public COSBase getCOSObject() { return values; } /** * Convert this standard java object to a COS object. * @return the cos object that matches this Java object */ public COSArray getCOSArray() { return values; } /** * Returns the r value of the tristimulus. * @return the R value. */ public float getR() { return ((COSNumber)values.get(0)).floatValue(); } /** * Sets the r value of the tristimulus. * @param r the r value for the tristimulus */ public void setR(float r) { values.set(0, new COSFloat(r)); } /** * Returns the g value of the tristimulus. * @return the g value */ public float getG() { return ((COSNumber)values.get(1)).floatValue(); } /** * Sets the g value of the tristimulus. * @param g the g value for the tristimulus */ public void setG(float g) { values.set(1, new COSFloat(g)); } /** * Returns the b value of the tristimulus. * @return the B value */ public float getB() { return ((COSNumber)values.get(2)).floatValue(); } /** * Sets the b value of the tristimulus. * @param b he b value for the tristimulus */ public void setB(float b) { values.set(2, new COSFloat(b)); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDICCBased.java000066400000000000000000000331531320103431700276630ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import static org.sejda.util.RequireUtils.requireIOCondition; import java.awt.Color; import java.awt.color.CMMException; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_Profile; import java.awt.color.ProfileDataException; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import org.apache.commons.io.IOUtils; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDRange; import org.sejda.sambox.pdmodel.common.PDStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * ICCBased colour spaces are based on a cross-platform colour profile as defined by the International Color Consortium * (ICC). * * @author Ben Litchfield * @author John Hewson */ public final class PDICCBased extends PDCIEBasedColorSpace { private static final Logger LOG = LoggerFactory.getLogger(PDICCBased.class); private final PDStream stream; private int numberOfComponents = -1; private ICC_Profile iccProfile; private PDColorSpace alternateColorSpace; private ICC_ColorSpace awtColorSpace; private PDColor initialColor; private boolean isRGB = false; /** * Creates a new ICC color space with an empty stream. */ public PDICCBased() { array = new COSArray(); array.add(COSName.ICCBASED); stream = new PDStream(); array.add(stream); } /** * Creates a new ICC color space using the PDF array. * * @param iccArray the ICC stream object * @throws IOException if there is an error reading the ICC profile or if the parameter is invalid */ public PDICCBased(COSArray iccArray) throws IOException { requireIOCondition(iccArray.size() >= 2, "ICCBased colorspace array must have two elements"); requireIOCondition(iccArray.getObject(1) instanceof COSStream, "ICCBased colorspace array must have a stream as second element"); array = iccArray; stream = new PDStream((COSStream) iccArray.getObject(1)); loadICCProfile(); } @Override public String getName() { return COSName.ICCBASED.getName(); } /** * Get the underlying ICC profile stream. * * @return the underlying ICC profile stream */ public PDStream getPDStream() { return stream; } /** * Load the ICC profile, or init alternateColorSpace color space. */ private void loadICCProfile() throws IOException { InputStream input = null; try { input = this.stream.createInputStream(); // if the embedded profile is sRGB then we can use Java's built-in profile, which // results in a large performance gain as it's our native color space, see PDFBOX-2587 ICC_Profile profile; synchronized (LOG) { profile = ICC_Profile.getInstance(input); if (is_sRGB(profile)) { isRGB = true; awtColorSpace = (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB); iccProfile = awtColorSpace.getProfile(); } else { awtColorSpace = new ICC_ColorSpace(profile); iccProfile = profile; } // set initial colour float[] initial = new float[getNumberOfComponents()]; for (int c = 0; c < getNumberOfComponents(); c++) { initial[c] = Math.max(0, getRangeForComponent(c).getMin()); } initialColor = new PDColor(initial, this); // do things that trigger a ProfileDataException // or CMMException due to invalid profiles, see PDFBOX-1295 and PDFBOX-1740 // or ArrayIndexOutOfBoundsException, see PDFBOX-3610 awtColorSpace.toRGB(new float[awtColorSpace.getNumComponents()]); // this one triggers an exception for PDFBOX-3549 with KCMS new Color(awtColorSpace, new float[getNumberOfComponents()], 1f); } } catch (RuntimeException e) { if (e instanceof ProfileDataException || e instanceof CMMException || e instanceof IllegalArgumentException || e instanceof ArrayIndexOutOfBoundsException) { // fall back to alternateColorSpace color space awtColorSpace = null; alternateColorSpace = getAlternateColorSpace(); if (alternateColorSpace.equals(PDDeviceRGB.INSTANCE)) { isRGB = true; } LOG.warn("Can't read embedded ICC profile (" + e.getLocalizedMessage() + "), using alternate color space: " + alternateColorSpace.getName()); initialColor = alternateColorSpace.getInitialColor(); } else { throw e; } } finally { IOUtils.closeQuietly(input); } } /** * Returns true if the given profile is represents sRGB. */ private boolean is_sRGB(ICC_Profile profile) { byte[] bytes = Arrays.copyOfRange(profile.getData(ICC_Profile.icSigHead), ICC_Profile.icHdrModel, ICC_Profile.icHdrModel + 7); String deviceModel = new String(bytes, StandardCharsets.US_ASCII).trim(); return deviceModel.equals("sRGB"); } @Override public float[] toRGB(float[] value) throws IOException { if (isRGB) { return value; } if (awtColorSpace != null) { // WARNING: toRGB is very slow when used with LUT-based ICC profiles return awtColorSpace.toRGB(value); } return alternateColorSpace.toRGB(value); } @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { if (awtColorSpace != null) { return toRGBImageAWT(raster, awtColorSpace); } return alternateColorSpace.toRGBImage(raster); } @Override public int getNumberOfComponents() { if (numberOfComponents < 0) { numberOfComponents = stream.getCOSObject().getInt(COSName.N); } return numberOfComponents; } @Override public float[] getDefaultDecode(int bitsPerComponent) { if (awtColorSpace != null) { int n = getNumberOfComponents(); float[] decode = new float[n * 2]; for (int i = 0; i < n; i++) { decode[i * 2] = awtColorSpace.getMinValue(i); decode[i * 2 + 1] = awtColorSpace.getMaxValue(i); } return decode; } else { return alternateColorSpace.getDefaultDecode(bitsPerComponent); } } @Override public PDColor getInitialColor() { return initialColor; } /** * Returns a list of alternate color spaces for non-conforming readers. WARNING: Do not use the information in a * conforming reader. * * @return A list of alternateColorSpace color spaces. * @throws IOException If there is an error getting the alternateColorSpace color spaces. */ public PDColorSpace getAlternateColorSpace() throws IOException { COSBase alternate = stream.getCOSObject().getDictionaryObject(COSName.ALTERNATE); COSArray alternateArray; if (alternate == null) { alternateArray = new COSArray(); int numComponents = getNumberOfComponents(); COSName csName; switch (numComponents) { case 1: csName = COSName.DEVICEGRAY; break; case 3: csName = COSName.DEVICERGB; break; case 4: csName = COSName.DEVICECMYK; break; default: throw new IOException("Unknown color space number of components:" + numComponents); } alternateArray.add(csName); } else { if (alternate instanceof COSArray) { alternateArray = (COSArray) alternate; } else if (alternate instanceof COSName) { alternateArray = new COSArray(); alternateArray.add(alternate); } else { throw new IOException("Error: expected COSArray or COSName and not " + alternate.getClass().getName()); } } return PDColorSpace.create(alternateArray); } /** * Returns the range for a certain component number. This will never return null. If it is not present then the * range 0..1 will be returned. * * @param n the component number to get the range for * @return the range for this component */ public PDRange getRangeForComponent(int n) { COSArray rangeArray = (COSArray) stream.getCOSObject().getDictionaryObject(COSName.RANGE); if (rangeArray == null || rangeArray.size() < getNumberOfComponents() * 2) { return new PDRange(); // 0..1 } return new PDRange(rangeArray, n); } /** * Returns the metadata stream for this object, or null if there is no metadata stream. * * @return the metadata stream, or null if there is none */ public COSStream getMetadata() { return (COSStream) stream.getCOSObject().getDictionaryObject(COSName.METADATA); } /** * Returns the type of the color space in the ICC profile. Will be one of {@code TYPE_GRAY}, {@code TYPE_RGB}, or * {@code TYPE_CMYK}. * * @return an ICC color space type */ public int getColorSpaceType() { if (iccProfile != null) { return iccProfile.getColorSpaceType(); } // if the ICC Profile could not be read switch (alternateColorSpace.getNumberOfComponents()) { case 1: return ICC_ColorSpace.TYPE_GRAY; case 3: return ICC_ColorSpace.TYPE_RGB; case 4: return ICC_ColorSpace.TYPE_CMYK; default: // should not happen as all ICC color spaces in PDF must have 1,3, or 4 components return -1; } } /** * Sets the number of color components. * * @param n the number of color components */ // TODO it's probably not safe to use this @Deprecated public void setNumberOfComponents(int n) { numberOfComponents = n; stream.getCOSObject().setInt(COSName.N, n); } /** * Sets the list of alternateColorSpace color spaces. * * @param list the list of color space objects */ public void setAlternateColorSpaces(List list) { COSArray altArray = null; if (list != null) { altArray = COSArrayList.converterToCOSArray(list); } stream.getCOSObject().setItem(COSName.ALTERNATE, altArray); } /** * Sets the range for this color space. * * @param range the new range for the a component * @param n the component to set the range for */ public void setRangeForComponent(PDRange range, int n) { COSArray rangeArray = (COSArray) stream.getCOSObject().getDictionaryObject(COSName.RANGE); if (rangeArray == null) { rangeArray = new COSArray(); stream.getCOSObject().setItem(COSName.RANGE, rangeArray); } // extend range array with default values if needed while (rangeArray.size() < (n + 1) * 2) { rangeArray.add(new COSFloat(0)); rangeArray.add(new COSFloat(1)); } rangeArray.set(n * 2, new COSFloat(range.getMin())); rangeArray.set(n * 2 + 1, new COSFloat(range.getMax())); } /** * Sets the metadata stream that is associated with this color space. * * @param metadata the new metadata stream */ public void setMetadata(COSStream metadata) { stream.getCOSObject().setItem(COSName.METADATA, metadata); } @Override public String toString() { return getName() + "{numberOfComponents: " + getNumberOfComponents() + "}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDIndexed.java000066400000000000000000000205271320103431700277070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.Point; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.common.PDStream; /** * An Indexed colour space specifies that an area is to be painted using a colour table * of arbitrary colours from another color space. * * @author John Hewson * @author Ben Litchfield */ public final class PDIndexed extends PDSpecialColorSpace { private final PDColor initialColor = new PDColor(new float[] { 0 }, this); private PDColorSpace baseColorSpace = null; // cached lookup data private byte[] lookupData; private float[][] colorTable; private int actualMaxIndex; private int[][] rgbColorTable; /** * Creates a new Indexed color space. * Default DeviceRGB, hival 255. */ public PDIndexed() { array = new COSArray(); array.add(COSName.INDEXED); array.add(COSName.DEVICERGB); array.add(COSInteger.get(255)); array.add(COSNull.NULL); } /** * Creates a new Indexed color space from the given PDF array. * @param indexedArray the array containing the indexed parameters */ public PDIndexed(COSArray indexedArray) throws IOException { array = indexedArray; baseColorSpace = PDColorSpace.create(array.getObject(1)); readColorTable(); initRgbColorTable(); } @Override public String getName() { return COSName.INDEXED.getName(); } @Override public int getNumberOfComponents() { return 1; } @Override public float[] getDefaultDecode(int bitsPerComponent) { return new float[] { 0, (float)Math.pow(2, bitsPerComponent) - 1 }; } @Override public PDColor getInitialColor() { return initialColor; } // // WARNING: this method is performance sensitive, modify with care! // private void initRgbColorTable() throws IOException { int numBaseComponents = baseColorSpace.getNumberOfComponents(); // convert the color table into a 1-row BufferedImage in the base color space, // using a writable raster for high performance WritableRaster baseRaster = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, actualMaxIndex + 1, 1, numBaseComponents, new Point(0, 0)); int[] base = new int[numBaseComponents]; for (int i = 0, n = actualMaxIndex; i <= n; i++) { for (int c = 0; c < numBaseComponents; c++) { base[c] = (int)(colorTable[i][c] * 255f); } baseRaster.setPixel(i, 0, base); } // convert the base image to RGB BufferedImage rgbImage = baseColorSpace.toRGBImage(baseRaster); WritableRaster rgbRaster = rgbImage.getRaster(); // build an RGB lookup table from the raster rgbColorTable = new int[actualMaxIndex + 1][3]; int[] nil = null; for (int i = 0, n = actualMaxIndex; i <= n; i++) { rgbColorTable[i] = rgbRaster.getPixel(i, 0, nil); } } // // WARNING: this method is performance sensitive, modify with care! // @Override public float[] toRGB(float[] value) { if (value.length > 1) { throw new IllegalArgumentException("Indexed color spaces must have one color value"); } // scale and clamp input value int index = Math.round(value[0]); index = Math.max(index, 0); index = Math.min(index, actualMaxIndex); // lookup rgb int[] rgb = rgbColorTable[index]; return new float[] { rgb[0] / 255f, rgb[1] / 255f, rgb[2] / 255f }; } // // WARNING: this method is performance sensitive, modify with care! // @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { // use lookup table int width = raster.getWidth(); int height = raster.getHeight(); BufferedImage rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); WritableRaster rgbRaster = rgbImage.getRaster(); int[] src = new int[1]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.getPixel(x, y, src); // lookup int index = Math.min(src[0], actualMaxIndex); rgbRaster.setPixel(x, y, rgbColorTable[index]); } } return rgbImage; } /** * Returns the base color space. * @return the base color space. */ public PDColorSpace getBaseColorSpace() { return baseColorSpace; } // returns "hival" array element private int getHival() { return ((COSNumber) array.getObject(2)).intValue(); } // reads the lookup table data from the array private byte[] getLookupData() throws IOException { if (lookupData == null) { COSBase lookupTable = array.getObject(3); if (lookupTable instanceof COSString) { lookupData = ((COSString) lookupTable).getBytes(); } else if (lookupTable instanceof COSStream) { lookupData = new PDStream((COSStream)lookupTable).toByteArray(); } else if (lookupTable == null) { lookupData = new byte[0]; } else { throw new IOException("Error: Unknown type for lookup table " + lookupTable); } } return lookupData; } // // WARNING: this method is performance sensitive, modify with care! // private void readColorTable() throws IOException { byte[] lookupData = getLookupData(); int maxIndex = Math.min(getHival(), 255); int numComponents = baseColorSpace.getNumberOfComponents(); // some tables are too short if (lookupData.length / numComponents < maxIndex + 1) { maxIndex = lookupData.length / numComponents - 1; } actualMaxIndex = maxIndex; // TODO "actual" is ugly, tidy this up colorTable = new float[maxIndex + 1][numComponents]; for (int i = 0, offset = 0; i <= maxIndex; i++) { for (int c = 0; c < numComponents; c++) { colorTable[i][c] = (lookupData[offset] & 0xff) / 255f; offset++; } } } /** * Sets the base color space. * @param base the base color space */ public void setBaseColorSpace(PDColorSpace base) { array.set(1, base.getCOSObject()); baseColorSpace = base; } /** * Sets the highest value that is allowed. This cannot be higher than 255. * @param high the highest value for the lookup table */ public void setHighValue(int high) { array.set(2, COSInteger.get(high)); } @Override public String toString() { return "Indexed{base:" + baseColorSpace + " " + "hival:" + getHival() + " " + "lookup:(" + colorTable.length + " entries)}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDJPXColorSpace.java000066400000000000000000000052521320103431700307410ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSBase; /** * A color space embedded in a JPX file. * This wraps the AWT ColorSpace which is obtained after JAI Image I/O reads a JPX stream. * * @author John Hewson */ public final class PDJPXColorSpace extends PDColorSpace { private final ColorSpace awtColorSpace; /** * Creates a new JPX color space from the given AWT color space. * @param colorSpace AWT color space from a JPX image */ public PDJPXColorSpace(ColorSpace colorSpace) { this.awtColorSpace = colorSpace; } @Override public String getName() { return "JPX"; } @Override public int getNumberOfComponents() { return awtColorSpace.getNumComponents(); } @Override public float[] getDefaultDecode(int bitsPerComponent) { int n = getNumberOfComponents(); float[] decode = new float[n * 2]; for (int i = 0; i < n; i++) { decode[i * 2] = awtColorSpace.getMinValue(i); decode[i * 2 + 1] = awtColorSpace.getMaxValue(i); } return decode; } @Override public PDColor getInitialColor() { throw new UnsupportedOperationException("JPX color spaces don't support drawing"); } @Override public float[] toRGB(float[] value) { throw new UnsupportedOperationException("JPX color spaces don't support drawing"); } @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { return toRGBImageAWT(raster, awtColorSpace); } @Override public COSBase getCOSObject() { throw new UnsupportedOperationException("JPX color spaces don't have COS objects"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDLab.java000066400000000000000000000161021320103431700270170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDRange; /** * A Lab colour space is a CIE-based ABC colour space with two transformation stages. * * @author Ben Litchfield * @author John Hewson */ public final class PDLab extends PDCIEDictionaryBasedColorSpace { private PDColor initialColor; /** * Creates a new Lab color space. */ public PDLab() { super(COSName.LAB); } /** * Creates a new Lab color space from a PDF array. * @param lab the color space array */ public PDLab(COSArray lab) { super(lab); } @Override public String getName() { return COSName.LAB.getName(); } // // WARNING: this method is performance sensitive, modify with care! // @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { int width = raster.getWidth(); int height = raster.getHeight(); BufferedImage rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); WritableRaster rgbRaster = rgbImage.getRaster(); float minA = getARange().getMin(); float maxA = getARange().getMax(); float minB = getBRange().getMin(); float maxB = getBRange().getMax(); // always three components: ABC float[] abc = new float[3]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.getPixel(x, y, abc); // 0..255 -> 0..1 abc[0] /= 255; abc[1] /= 255; abc[2] /= 255; // scale to range abc[0] *= 100; abc[1] = minA + (abc[1] * (maxA - minA)); abc[2] = minB + (abc[2] * (maxB - minB)); float[] rgb = toRGB(abc); // 0..1 -> 0..255 rgb[0] *= 255; rgb[1] *= 255; rgb[2] *= 255; rgbRaster.setPixel(x, y, rgb); } } return rgbImage; } @Override public float[] toRGB(float[] value) { // CIE LAB to RGB, see http://en.wikipedia.org/wiki/Lab_color_space // L* float lstar = (value[0] + 16f) * (1f / 116f); // TODO: how to use the blackpoint? scale linearly between black & white? // XYZ float x = wpX * inverse(lstar + value[1] * (1f / 500f)); float y = wpY * inverse(lstar); float z = wpZ * inverse(lstar - value[2] * (1f / 200f)); return convXYZtoRGB(x, y, z); } // reverse transformation (f^-1) private float inverse(float x) { if (x > 6.0 / 29.0) { return x * x * x; } else { return (108f / 841f) * (x - (4f / 29f)); } } @Override public int getNumberOfComponents() { return 3; } @Override public float[] getDefaultDecode(int bitsPerComponent) { PDRange a = getARange(); PDRange b = getARange(); return new float[] { 0, 100, a.getMin(), a.getMax(), b.getMin(), b.getMax() }; } @Override public PDColor getInitialColor() { if (initialColor == null) { initialColor = new PDColor(new float[] { 0, Math.max(0, getARange().getMin()), Math.max(0, getBRange().getMin()) }, this); } return initialColor; } /** * creates a range array with default values (-100..100 -100..100). * @return the new range array. */ private COSArray getDefaultRangeArray() { COSArray range = new COSArray(); range.add(new COSFloat(-100)); range.add(new COSFloat(100)); range.add(new COSFloat(-100)); range.add(new COSFloat(100)); return range; } /** * This will get the valid range for the "a" component. * If none is found then the default will be returned, which is -100..100. * @return the "a" range. */ public PDRange getARange() { COSArray rangeArray = (COSArray) dictionary.getDictionaryObject(COSName.RANGE); if (rangeArray == null) { rangeArray = getDefaultRangeArray(); } return new PDRange(rangeArray, 0); } /** * This will get the valid range for the "b" component. * If none is found then the default will be returned, which is -100..100. * @return the "b" range. */ public PDRange getBRange() { COSArray rangeArray = (COSArray) dictionary.getDictionaryObject(COSName.RANGE); if (rangeArray == null) { rangeArray = getDefaultRangeArray(); } return new PDRange(rangeArray, 1); } /** * This will set the a range for the "a" component. * @param range the new range for the "a" component, * or null if defaults (-100..100) are to be set. */ public void setARange(PDRange range) { setComponentRangeArray(range, 0); } /** * This will set the "b" range for this color space. * @param range the new range for the "b" component, * or null if defaults (-100..100) are to be set. */ public void setBRange(PDRange range) { setComponentRangeArray(range, 2); } private void setComponentRangeArray(PDRange range, int index) { COSArray rangeArray = (COSArray) dictionary.getDictionaryObject(COSName.RANGE); if (rangeArray == null) { rangeArray = getDefaultRangeArray(); } if (range == null) { // reset to defaults rangeArray.set(index, new COSFloat(-100)); rangeArray.set(index + 1, new COSFloat(100)); } else { rangeArray.set(index, new COSFloat(range.getMin())); rangeArray.set(index + 1, new COSFloat(range.getMax())); } dictionary.setItem(COSName.RANGE, rangeArray); initialColor = null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDOutputIntent.java000066400000000000000000000075371320103431700310170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.color.ICC_Profile; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.common.PDStream; /** * An Output Intent describes the colour reproduction characteristics of a possible output device or production * condition. Output intents provide a means for matching the colour characteristics of a PDF document with those of a * target output device or production environment in which the document will be printed. * * @author Guillaume Bailleul */ public final class PDOutputIntent implements COSObjectable { private final COSDictionary dictionary; public PDOutputIntent(PDDocument doc, InputStream colorProfile) throws IOException { dictionary = new COSDictionary(); dictionary.setItem(COSName.TYPE, COSName.OUTPUT_INTENT); dictionary.setItem(COSName.S, COSName.GTS_PDFA1); PDStream destOutputIntent = configureOutputProfile(colorProfile); dictionary.setItem(COSName.DEST_OUTPUT_PROFILE, destOutputIntent); } public PDOutputIntent(COSDictionary dictionary) { this.dictionary = dictionary; } @Override public COSBase getCOSObject() { return dictionary; } public COSStream getDestOutputIntent() { return (COSStream) dictionary.getItem(COSName.DEST_OUTPUT_PROFILE); } public String getInfo() { return dictionary.getString(COSName.INFO); } public void setInfo(String value) { dictionary.setString(COSName.INFO, value); } public String getOutputCondition() { return dictionary.getString(COSName.OUTPUT_CONDITION); } public void setOutputCondition(String value) { dictionary.setString(COSName.OUTPUT_CONDITION, value); } public String getOutputConditionIdentifier() { return dictionary.getString(COSName.OUTPUT_CONDITION_IDENTIFIER); } public void setOutputConditionIdentifier(String value) { dictionary.setString(COSName.OUTPUT_CONDITION_IDENTIFIER, value); } public String getRegistryName() { return dictionary.getString(COSName.REGISTRY_NAME); } public void setRegistryName(String value) { dictionary.setString(COSName.REGISTRY_NAME, value); } private PDStream configureOutputProfile(InputStream colorProfile) throws IOException { ICC_Profile icc = ICC_Profile.getInstance(colorProfile); PDStream stream = new PDStream(new ByteArrayInputStream(icc.getData()), COSName.FLATE_DECODE); stream.getCOSObject().setInt(COSName.N, icc.getNumComponents()); return stream; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDPattern.java000066400000000000000000000075371320103431700277520ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.graphics.pattern.PDAbstractPattern; /** * A Pattern color space is either a Tiling pattern or a Shading pattern. * @author John Hewson * @author Ben Litchfield */ public final class PDPattern extends PDSpecialColorSpace { /** A pattern which leaves no marks on the page. */ private static PDColor EMPTY_PATTERN = new PDColor(new float[] { }, null); private final PDResources resources; private PDColorSpace underlyingColorSpace; /** * Creates a new pattern color space. * * @param resources The current resources. */ public PDPattern(PDResources resources) { this.resources = resources; this.array = new COSArray(); this.array.add(COSName.PATTERN); } /** * Creates a new uncolored tiling pattern color space. * * @param resources The current resources. * @param colorSpace The underlying color space. */ public PDPattern(PDResources resources, PDColorSpace colorSpace) { this.resources = resources; this.underlyingColorSpace = colorSpace; this.array = new COSArray(); this.array.add(COSName.PATTERN); this.array.add(colorSpace); } @Override public String getName() { return COSName.PATTERN.getName(); } @Override public int getNumberOfComponents() { throw new UnsupportedOperationException(); } @Override public float[] getDefaultDecode(int bitsPerComponent) { throw new UnsupportedOperationException(); } @Override public PDColor getInitialColor() { return EMPTY_PATTERN; } @Override public float[] toRGB(float[] value) { throw new UnsupportedOperationException(); } @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { throw new UnsupportedOperationException(); } /** * Returns the pattern for the given color. * * @param color color containing a pattern name * @return pattern for the given color * @throws java.io.IOException if the pattern name was not found. */ public PDAbstractPattern getPattern(PDColor color) throws IOException { PDAbstractPattern pattern = resources.getPattern(color.getPatternName()); if (pattern == null) { throw new IOException("pattern " + color.getPatternName() + " was not found"); } else { return pattern; } } /** * Returns the underlying color space, if this is an uncolored tiling pattern, otherwise null. */ public PDColorSpace getUnderlyingColorSpace() { return underlyingColorSpace; } @Override public String toString() { return "Pattern"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDSeparation.java000066400000000000000000000220351320103431700304300ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import java.awt.Point; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.pdmodel.common.function.PDFunction; /** * A Separation color space used to specify either additional colorants or for isolating the control of individual * colour components of a device colour space for a subtractive device. When such a space is the current colour space, * the current colour shall be a single-component value, called a tint, that controls the given colorant or colour * components only. * * @author Ben Litchfield * @author John Hewson */ public class PDSeparation extends PDSpecialColorSpace { private final PDColor initialColor = new PDColor(new float[] { 1 }, this); // array indexes private static final int COLORANT_NAMES = 1; private static final int ALTERNATE_CS = 2; private static final int TINT_TRANSFORM = 3; // fields private PDColorSpace alternateColorSpace = null; private PDFunction tintTransform = null; /** * Map used to speed up {@link #toRGB(float[])}. Note that this class contains three maps (this and the two in * {@link #toRGBImage(java.awt.image.WritableRaster) } and {@link #toRGBImage2(java.awt.image.WritableRaster) }. The * maps use different key intervals. This map here is needed for shading, which produce more than 256 different * float values, which we cast to int so that the map can work. */ private Map toRGBMap = null; /** * Creates a new Separation color space. */ public PDSeparation() { array = new COSArray(); array.add(COSName.SEPARATION); array.add(COSName.getPDFName("")); // add some placeholder array.add(COSNull.NULL); array.add(COSNull.NULL); } /** * Creates a new Separation color space from a PDF color space array. * * @param separation an array containing all separation information. * @throws IOException if the color space or the function could not be created. */ public PDSeparation(COSArray separation) throws IOException { array = separation; alternateColorSpace = PDColorSpace.create(array.getObject(ALTERNATE_CS)); tintTransform = PDFunction.create(array.getObject(TINT_TRANSFORM)); } @Override public String getName() { return COSName.SEPARATION.getName(); } @Override public int getNumberOfComponents() { return 1; } @Override public float[] getDefaultDecode(int bitsPerComponent) { return new float[] { 0, 1 }; } @Override public PDColor getInitialColor() { return initialColor; } @Override public float[] toRGB(float[] value) throws IOException { if (toRGBMap == null) { toRGBMap = new HashMap<>(); } int key = (int) (value[0] * 255); float[] retval = toRGBMap.get(key); if (retval != null) { return retval; } float[] altColor = tintTransform.eval(value); retval = alternateColorSpace.toRGB(altColor); toRGBMap.put(key, retval); return retval; } // // WARNING: this method is performance sensitive, modify with care! // @Override public BufferedImage toRGBImage(WritableRaster raster) throws IOException { if (alternateColorSpace instanceof PDLab) { // PDFBOX-3622 - regular converter fails for Lab colorspaces return toRGBImage2(raster); } // use the tint transform to convert the sample into // the alternate color space (this is usually 1:many) WritableRaster altRaster = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, raster.getWidth(), raster.getHeight(), alternateColorSpace.getNumberOfComponents(), new Point(0, 0)); int numAltComponents = alternateColorSpace.getNumberOfComponents(); int width = raster.getWidth(); int height = raster.getHeight(); float[] samples = new float[1]; Map calculatedValues = new HashMap(); Integer hash; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.getPixel(x, y, samples); int[] alt = calculatedValues.get(hash = Float.floatToIntBits(samples[0])); if (alt == null) { alt = new int[numAltComponents]; tintTransform(samples, alt); calculatedValues.put(hash, alt); } altRaster.setPixel(x, y, alt); } } // convert the alternate color space to RGB return alternateColorSpace.toRGBImage(altRaster); } // converter that works without using super implementation of toRGBImage() private BufferedImage toRGBImage2(WritableRaster raster) throws IOException { int width = raster.getWidth(); int height = raster.getHeight(); BufferedImage rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); WritableRaster rgbRaster = rgbImage.getRaster(); float[] samples = new float[1]; Map calculatedValues = new HashMap(); Integer hash; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.getPixel(x, y, samples); int[] rgb = calculatedValues.get(hash = Float.floatToIntBits(samples[0])); if (rgb == null) { samples[0] /= 255; float[] altColor = tintTransform.eval(samples); float[] fltab = alternateColorSpace.toRGB(altColor); rgb = new int[3]; rgb[0] = (int) (fltab[0] * 255); rgb[1] = (int) (fltab[1] * 255); rgb[2] = (int) (fltab[2] * 255); calculatedValues.put(hash, rgb); } rgbRaster.setPixel(x, y, rgb); } } return rgbImage; } protected void tintTransform(float[] samples, int[] alt) throws IOException { samples[0] /= 255; // 0..1 float[] result = tintTransform.eval(samples); for (int s = 0; s < alt.length; s++) { // scale to 0..255 alt[s] = (int) (result[s] * 255); } } /** * Returns the colorant name. * * @return the name of the colorant */ public PDColorSpace getAlternateColorSpace() { return alternateColorSpace; } /** * Returns the colorant name. * * @return the name of the colorant */ public String getColorantName() { COSName name = (COSName) array.getObject(COLORANT_NAMES); return name.getName(); } /** * Sets the colorant name. * * @param name the name of the colorant */ public void setColorantName(String name) { array.set(1, COSName.getPDFName(name)); } /** * Sets the alternate color space. * * @param colorSpace The alternate color space. */ public void setAlternateColorSpace(PDColorSpace colorSpace) { alternateColorSpace = colorSpace; COSBase space = null; if (colorSpace != null) { space = colorSpace.getCOSObject(); } array.set(ALTERNATE_CS, space); } /** * Sets the tint transform function. * * @param tint the tint transform function */ public void setTintTransform(PDFunction tint) { tintTransform = tint; array.set(TINT_TRANSFORM, tint); } @Override public String toString() { return getName() + "{" + "\"" + getColorantName() + "\"" + " " + alternateColorSpace.getName() + " " + tintTransform + "}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDSpecialColorSpace.java000066400000000000000000000022051320103431700316530ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import org.sejda.sambox.cos.COSBase; /** * Special colour spaces add features or properties to an underlying colour space. * * @author John Hewson */ public abstract class PDSpecialColorSpace extends PDColorSpace { @Override public COSBase getCOSObject() { return array; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/color/PDTristimulus.java000066400000000000000000000062441320103431700306730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.color; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; /** * A tristimulus, or collection of three floating point parameters used for color operations. * * @author Ben Litchfield */ public final class PDTristimulus implements COSObjectable { private COSArray values = null; /** * Constructor. Defaults all values to 0, 0, 0. */ public PDTristimulus() { values = new COSArray(); values.add(new COSFloat(0.0f)); values.add(new COSFloat(0.0f)); values.add(new COSFloat(0.0f)); } /** * Constructor from COS object. * @param array the array containing the XYZ values */ public PDTristimulus(COSArray array) { values = array; } /** * Constructor from COS object. * @param array the array containing the XYZ values */ public PDTristimulus(float[] array) { values = new COSArray(); for(int i=0; i= 4) { retval = new PDRectangle(array); } return retval; } /** * This will set the BBox (bounding box) for this form. * * @param bbox The new BBox for this form. */ public void setBBox(PDRectangle bbox) { if (bbox == null) { getCOSObject().removeItem(COSName.BBOX); } else { getCOSObject().setItem(COSName.BBOX, bbox.getCOSObject()); } } /** * This will get the optional Matrix of an XObjectForm. It maps the form space to user space. * * @return the form matrix if available, or the identity matrix. */ @Override public Matrix getMatrix() { COSArray array = (COSArray) getCOSObject().getDictionaryObject(COSName.MATRIX); if (array != null) { return new Matrix(array); } // default value is the identity matrix return new Matrix(); } /** * Sets the optional Matrix entry for the form XObject. * * @param transform the transformation matrix */ public void setMatrix(AffineTransform transform) { COSArray matrix = new COSArray(); double[] values = new double[6]; transform.getMatrix(values); for (double v : values) { matrix.add(new COSFloat((float) v)); } getCOSObject().setItem(COSName.MATRIX, matrix); } /** * This will get the key of this XObjectForm in the structural parent tree. Required if the form XObject contains * marked-content sequences that are structural content items. * * @return the integer key of the XObjectForm's entry in the structural parent tree */ public int getStructParents() { return getCOSObject().getInt(COSName.STRUCT_PARENTS, 0); } /** * This will set the key for this XObjectForm in the structural parent tree. * * @param structParent The new key for this XObjectForm. */ public void setStructParents(int structParent) { getCOSObject().setInt(COSName.STRUCT_PARENTS, structParent); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/form/PDTransparencyGroup.java000066400000000000000000000027551320103431700316450ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.form; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.ResourceCache; import org.sejda.sambox.pdmodel.common.PDStream; /** * A transparency group. * * @author John Hewson */ public class PDTransparencyGroup extends PDFormXObject { /** * Creates a Transparency Group for reading. * @param stream The XObject stream */ public PDTransparencyGroup(PDStream stream) { super(stream); } /** * Creates a Transparency Group for reading. * @param stream The XObject stream */ public PDTransparencyGroup(COSStream stream, ResourceCache cache) { super(stream, cache); } } PDTransparencyGroupAttributes.java000066400000000000000000000054341320103431700336320ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/form/* * 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. */ package org.sejda.sambox.pdmodel.graphics.form; import java.io.IOException; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * Transparency group attributes. * * @author Kühn & Weyh Software, GmbH */ public final class PDTransparencyGroupAttributes extends PDDictionaryWrapper { private PDColorSpace colorSpace; /** * Creates a group object with /Transparency subtype entry. */ public PDTransparencyGroupAttributes() { getCOSObject().setItem(COSName.TYPE, COSName.GROUP); getCOSObject().setItem(COSName.S, COSName.TRANSPARENCY); } public PDTransparencyGroupAttributes(COSDictionary dictionary) { super(dictionary); } /** * Returns the group color space or null if it isn't defined. * * @return the group color space. * @throws IOException */ public PDColorSpace getColorSpace() throws IOException { if (colorSpace == null && getCOSObject().containsKey(COSName.CS)) { colorSpace = PDColorSpace.create(getCOSObject().getDictionaryObject(COSName.CS)); } return colorSpace; } /** * @return true if this group is isolated. Isolated groups begin with the fully transparent image, non-isolated * begin with the current backdrop. */ public boolean isIsolated() { return getCOSObject().getBoolean(COSName.I, false); } public void setIsolated() { getCOSObject().setBoolean(COSName.I, true); } /** * @return true if this group is a knockout. A knockout group blends with original backdrop, a non-knockout group * blends with the current backdrop. */ public boolean isKnockout() { return getCOSObject().getBoolean(COSName.K, false); } public void setKnockout() { getCOSObject().setBoolean(COSName.K, true); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/000077500000000000000000000000001320103431700251765ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/CCITTFactory.java000066400000000000000000000327111320103431700302430ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.image; import static org.sejda.io.SeekableSources.seekableSourceFrom; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import javax.imageio.stream.MemoryCacheImageOutputStream; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.io.SeekableSource; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.filter.Filter; import org.sejda.sambox.filter.FilterFactory; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; /** * Factory for creating a PDImageXObject containing a CCITT Fax compressed TIFF image. * * @author Ben Litchfield * @author Paul King */ public final class CCITTFactory { private CCITTFactory() { } /** * Creates a new CCITT group 4 (T6) compressed image XObject from a b/w BufferedImage. This compression technique * usually results in smaller images than those produced by * {@link LosslessFactory#createFromImage(PDDocument, BufferedImage) }. * * @param image the image. * @return a new image XObject. * @throws IOException if there is an error creating the image. * @throws IllegalArgumentException if the BufferedImage is not a b/w image. */ public static PDImageXObject createFromImage(BufferedImage image) throws IOException { if (image.getType() != BufferedImage.TYPE_BYTE_BINARY && image.getColorModel().getPixelSize() != 1) { throw new IllegalArgumentException("Only 1-bit b/w images supported"); } int height = image.getHeight(); int width = image.getWidth(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (MemoryCacheImageOutputStream mcios = new MemoryCacheImageOutputStream(bos)) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // flip bit to avoid having to set /BlackIs1 mcios.writeBits(~(image.getRGB(x, y) & 1), 1); } if (mcios.getBitOffset() != 0) { mcios.writeBits(0, 8 - mcios.getBitOffset()); } } mcios.flush(); } return prepareImageXObject(bos.toByteArray(), width, height, PDDeviceGray.INSTANCE); } private static PDImageXObject prepareImageXObject(byte[] byteArray, int width, int height, PDColorSpace initColorSpace) throws IOException { FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); Filter filter = FilterFactory.INSTANCE.getFilter(COSName.CCITTFAX_DECODE); COSDictionary dict = new COSDictionary(); dict.setInt(COSName.COLUMNS, width); dict.setInt(COSName.ROWS, height); filter.encode(new ByteArrayInputStream(byteArray), baos, dict); ByteArrayInputStream encodedByteStream = new ByteArrayInputStream(baos.toByteArray()); PDImageXObject image = new PDImageXObject(encodedByteStream, COSName.CCITTFAX_DECODE, width, height, 1, initColorSpace); dict.setInt(COSName.K, -1); image.getCOSObject().setItem(COSName.DECODE_PARMS, dict); return image; } /** * Creates a new CCITT Fax compressed Image XObject from the first page of a TIFF file. * * @param file the TIFF file which contains a suitable CCITT compressed image * @return a new Image XObject * @throws IOException if there is an error reading the TIFF data. */ public static PDImageXObject createFromFile(File file) throws IOException { return createFromRandomAccessImpl(seekableSourceFrom(file), 0); } /** * Creates a new CCITT Fax compressed Image XObject from the first page of a TIFF file. * * @param file the TIFF file which contains a suitable CCITT compressed image * @param number TIFF image number, starting from 0 * @return a new Image XObject * @throws IOException if there is an error reading the TIFF data. */ public static PDImageXObject createFromFile(File file, int number) throws IOException { return createFromRandomAccessImpl(seekableSourceFrom(file), number); } /** * Creates a new CCITT Fax compressed Image XObject from a TIFF file. * * @param source the random access TIFF file which contains a suitable CCITT compressed image * @param number TIFF image number, starting from 0 * @return a new Image XObject, or null if no such page * @throws IOException if there is an error reading the TIFF data. */ private static PDImageXObject createFromRandomAccessImpl(SeekableSource source, int number) throws IOException { COSDictionary decodeParms = new COSDictionary(); SeekableSource tiffView = extractFromTiff(source, decodeParms, number); if (tiffView != null) { PDImageXObject pdImage = new PDImageXObject(tiffView.asInputStream(), COSName.CCITTFAX_DECODE, decodeParms.getInt(COSName.COLUMNS), decodeParms.getInt(COSName.ROWS), 1, PDDeviceGray.INSTANCE); COSDictionary dict = pdImage.getCOSObject(); dict.setItem(COSName.DECODE_PARMS, decodeParms); return pdImage; } return null; } // extracts the CCITT stream from the TIFF file private static SeekableSource extractFromTiff(SeekableSource source, COSDictionary params, int number) throws IOException { // First check the basic tiff header source.position(0); char endianess = (char) source.read(); if ((char) source.read() != endianess) { throw new IOException("Not a valid tiff file"); } // ensure that endianess is either M or I if (endianess != 'M' && endianess != 'I') { throw new IOException("Not a valid tiff file"); } int magicNumber = readshort(endianess, source); if (magicNumber != 42) { throw new IOException("Not a valid tiff file"); } // Relocate to the first set of tags int address = readlong(endianess, source); source.position(address); // If some higher page number is required, skip this page's tags, // then read the next page's address for (int i = 0; i < number; i++) { int numtags = readshort(endianess, source); if (numtags > 50) { throw new IOException("Not a valid tiff file"); } source.position(address + 2 + numtags * 12); address = readlong(endianess, source); if (address == 0) { return null; } source.position(address); } int numtags = readshort(endianess, source); // The number 50 is somewhat arbitary, it just stops us load up junk from somewhere // and tramping on if (numtags > 50) { throw new IOException("Not a valid tiff file"); } // Loop through the tags, some will convert to items in the parms dictionary // Other point us to where to find the data stream // The only parm which might change as a result of other options is K, so // We'll deal with that as a special; int k = -1000; // Default Non CCITT compression int dataoffset = 0; int datalength = 0; for (int i = 0; i < numtags; i++) { int tag = readshort(endianess, source); int type = readshort(endianess, source); int count = readlong(endianess, source); int val; // Note that when the type is shorter than 4 bytes, the rest can be garbage // and must be ignored. E.g. short (2 bytes) from "01 00 38 32" (little endian) // is 1, not 842530817 (seen in a real-life TIFF image). switch (type) { case 1: // byte value val = source.read(); source.read(); source.read(); source.read(); break; case 3: // short value val = readshort(endianess, source); source.read(); source.read(); break; default: // long and other types val = readlong(endianess, source); break; } switch (tag) { case 256: { params.setInt(COSName.COLUMNS, val); break; } case 257: { params.setInt(COSName.ROWS, val); break; } case 259: { if (val == 4) { k = -1; } if (val == 3) { k = 0; } break; // T6/T4 Compression } case 262: { if (val == 1) { params.setBoolean(COSName.BLACK_IS_1, true); } break; } case 266: { if (val != 1) { throw new UnsupportedTiffImageException( "FillOrder " + val + " is not supported"); } break; } case 273: { if (count == 1) { dataoffset = val; } break; } case 274: { // http://www.awaresystems.be/imaging/tiff/tifftags/orientation.html if (val != 1) { throw new UnsupportedTiffImageException( "Orientation " + val + " is not supported"); } break; } case 279: { if (count == 1) { datalength = val; } break; } case 292: { if ((val & 1) != 0) { k = 50; // T4 2D - arbitary positive K value } // http://www.awaresystems.be/imaging/tiff/tifftags/t4options.html if ((val & 4) != 0) { throw new UnsupportedTiffImageException( "CCITT Group 3 'uncompressed mode' is not supported"); } if ((val & 2) != 0) { throw new UnsupportedTiffImageException( "CCITT Group 3 'fill bits before EOL' is not supported"); } break; } case 324: { if (count == 1) { dataoffset = val; } break; } case 325: { if (count == 1) { datalength = val; } break; } default: { // do nothing } } } if (k == -1000) { throw new UnsupportedTiffImageException( "First image in tiff is not CCITT T4 or T6 compressed"); } if (dataoffset == 0) { throw new UnsupportedTiffImageException( "First image in tiff is not a single tile/strip"); } params.setInt(COSName.K, k); source.position(dataoffset); return source.view(dataoffset, datalength); } private static int readshort(char endianess, SeekableSource source) throws IOException { if (endianess == 'I') { return source.read() | (source.read() << 8); } return (source.read() << 8) | source.read(); } private static int readlong(char endianess, SeekableSource source) throws IOException { if (endianess == 'I') { return source.read() | (source.read() << 8) | (source.read() << 16) | (source.read() << 24); } return (source.read() << 24) | (source.read() << 16) | (source.read() << 8) | source.read(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/JPEGFactory.java000066400000000000000000000267711320103431700301330ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.image; import static java.util.Objects.nonNull; import static org.sejda.util.RequireUtils.requireIOCondition; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorConvertOp; import java.awt.image.WritableRaster; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.util.Iterator; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageWriter; import javax.imageio.metadata.IIOMetadata; import javax.imageio.plugins.jpeg.JPEGImageWriteParam; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; import org.apache.commons.io.IOUtils; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceCMYK; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; import org.w3c.dom.Element; /** * Factory for creating a PDImageXObject containing a JPEG compressed image. * * @author John Hewson */ public final class JPEGFactory { private JPEGFactory() { } /** * Creates a new JPEG Image XObject from an input stream containing JPEG data. * * The input stream data will be preserved and embedded in the PDF file without modification. * * @param file a JPEG file * @return a new Image XObject * * @throws IOException if the input stream cannot be read */ public static PDImageXObject createFromFile(File file) throws IOException { // read image BufferedImage awtImage = readJpegFile(file); // create Image XObject from stream PDImageXObject pdImage = new PDImageXObject( new BufferedInputStream(Files.newInputStream(file.toPath())), COSName.DCT_DECODE, awtImage.getWidth(), awtImage.getHeight(), awtImage.getColorModel().getComponentSize(0), getColorSpaceFromAWT(awtImage)); // no alpha if (awtImage.getColorModel().hasAlpha()) { throw new UnsupportedOperationException("alpha channel not implemented"); } return pdImage; } public static BufferedImage readJpegFile(File file) throws IOException { return readJpeg(file); } private static BufferedImage readJpeg(Object fileOrStream) throws IOException { Iterator readers = ImageIO.getImageReadersByFormatName("JPEG"); ImageReader reader = null; while (readers.hasNext()) { reader = readers.next(); if (reader.canReadRaster()) { break; } } requireIOCondition(nonNull(reader), "Cannot find an ImageIO reader for JPEG image"); try (ImageInputStream iis = ImageIO.createImageInputStream(fileOrStream)) { reader.setInput(iis); ImageIO.setUseCache(false); return reader.read(0); } finally { reader.dispose(); } } /** * Creates a new JPEG Image XObject from a Buffered Image. * * @param image the buffered image to embed * @return a new Image XObject * @throws IOException if the JPEG data cannot be written */ public static PDImageXObject createFromImage(BufferedImage image) throws IOException { return createFromImage(image, 0.75f); } /** * Creates a new JPEG Image XObject from a Buffered Image and a given quality. The image will be created at 72 DPI. * * @param image the buffered image to embed * @param quality the desired JPEG compression quality * @return a new Image XObject * @throws IOException if the JPEG data cannot be written */ public static PDImageXObject createFromImage(BufferedImage image, float quality) throws IOException { return createFromImage(image, quality, 72); } /** * Creates a new JPEG Image XObject from a Buffered Image, a given quality and DPI. * * @param image the buffered image to embed * @param quality the desired JPEG compression quality * @param dpi the desired DPI (resolution) of the JPEG * @return a new Image XObject * @throws IOException if the JPEG data cannot be written */ public static PDImageXObject createFromImage(BufferedImage image, float quality, int dpi) throws IOException { return createJPEG(image, quality, dpi); } // returns the alpha channel of an image private static BufferedImage getAlphaImage(BufferedImage image) { if (!image.getColorModel().hasAlpha()) { return null; } if (image.getTransparency() == Transparency.BITMASK) { throw new UnsupportedOperationException("BITMASK Transparency JPEG compression is not" + " useful, use LosslessImageFactory instead"); } WritableRaster alphaRaster = image.getAlphaRaster(); if (alphaRaster == null) { // happens sometimes (PDFBOX-2654) despite colormodel claiming to have alpha return null; } BufferedImage alphaImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); alphaImage.setData(alphaRaster); return alphaImage; } // Creates an Image XObject from a Buffered Image using JAI Image I/O private static PDImageXObject createJPEG(BufferedImage image, float quality, int dpi) throws IOException { // extract alpha channel (if any) BufferedImage awtColorImage = getColorImage(image); BufferedImage awtAlphaImage = getAlphaImage(image); // create XObject ByteArrayOutputStream baos = new ByteArrayOutputStream(); encodeImageToJPEGStream(awtColorImage, quality, dpi, baos); ByteArrayInputStream byteStream = new ByteArrayInputStream(baos.toByteArray()); PDImageXObject pdImage = new PDImageXObject(byteStream, COSName.DCT_DECODE, awtColorImage.getWidth(), awtColorImage.getHeight(), awtColorImage.getColorModel().getComponentSize(0), getColorSpaceFromAWT(awtColorImage)); // alpha -> soft mask if (awtAlphaImage != null) { PDImage xAlpha = JPEGFactory.createFromImage(awtAlphaImage, quality); pdImage.getCOSObject().setItem(COSName.SMASK, xAlpha); } return pdImage; } private static void encodeImageToJPEGStream(BufferedImage image, float quality, int dpi, OutputStream out) throws IOException { // encode to JPEG ImageOutputStream ios = null; ImageWriter imageWriter = null; try { // find JAI writer imageWriter = ImageIO.getImageWritersBySuffix("jpeg").next(); ios = ImageIO.createImageOutputStream(out); imageWriter.setOutput(ios); // add compression JPEGImageWriteParam jpegParam = (JPEGImageWriteParam) imageWriter .getDefaultWriteParam(); jpegParam.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT); jpegParam.setCompressionQuality(quality); // add metadata ImageTypeSpecifier imageTypeSpecifier = new ImageTypeSpecifier(image); IIOMetadata data = imageWriter.getDefaultImageMetadata(imageTypeSpecifier, jpegParam); Element tree = (Element) data.getAsTree("javax_imageio_jpeg_image_1.0"); Element jfif = (Element) tree.getElementsByTagName("app0JFIF").item(0); jfif.setAttribute("Xdensity", Integer.toString(dpi)); jfif.setAttribute("Ydensity", Integer.toString(dpi)); jfif.setAttribute("resUnits", "1"); // 1 = dots/inch // write imageWriter.write(data, new IIOImage(image, null, null), jpegParam); } finally { // clean up IOUtils.closeQuietly(out); if (ios != null) { ios.close(); } if (imageWriter != null) { imageWriter.dispose(); } } } // returns a PDColorSpace for a given BufferedImage public static PDColorSpace getColorSpaceFromAWT(BufferedImage awtImage) { if (awtImage.getColorModel().getNumComponents() == 1) { // 256 color (gray) JPEG return PDDeviceGray.INSTANCE; } ColorSpace awtColorSpace = awtImage.getColorModel().getColorSpace(); if (awtColorSpace instanceof ICC_ColorSpace && !awtColorSpace.isCS_sRGB()) { throw new UnsupportedOperationException("ICC color spaces not implemented"); } switch (awtColorSpace.getType()) { case ColorSpace.TYPE_RGB: return PDDeviceRGB.INSTANCE; case ColorSpace.TYPE_GRAY: return PDDeviceGray.INSTANCE; case ColorSpace.TYPE_CMYK: return PDDeviceCMYK.INSTANCE; default: throw new UnsupportedOperationException( "color space not implemented: " + awtColorSpace.getType()); } } // returns the color channels of an image private static BufferedImage getColorImage(BufferedImage image) { if (!image.getColorModel().hasAlpha()) { return image; } if (image.getColorModel().getColorSpace().getType() != ColorSpace.TYPE_RGB) { throw new UnsupportedOperationException("only RGB color spaces are implemented"); } // create an RGB image without alpha // BEWARE: the previous solution in the history // g.setComposite(AlphaComposite.Src) and g.drawImage() // didn't work properly for TYPE_4BYTE_ABGR. // alpha values of 0 result in a black dest pixel!!! BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR); return new ColorConvertOp(null).filter(image, rgbImage); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/LosslessFactory.java000066400000000000000000000220111320103431700311740ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.image; import java.awt.Transparency; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import javax.imageio.stream.MemoryCacheImageOutputStream; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.filter.Filter; import org.sejda.sambox.filter.FilterFactory; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; /** * Factory for creating a PDImageXObject containing a lossless compressed image. * * @author Tilman Hausherr */ public final class LosslessFactory { private LosslessFactory() { } /** * Creates a new lossless encoded Image XObject from a Buffered Image. * * @param image the buffered image to embed * @return a new Image XObject * @throws IOException if something goes wrong */ public static PDImageXObject createFromImage(BufferedImage image) throws IOException { int bpc; PDDeviceColorSpace deviceColorSpace; int height = image.getHeight(); int width = image.getWidth(); int[] rgbLineBuffer = new int[width]; byte[] imageData; if ((image.getType() == BufferedImage.TYPE_BYTE_GRAY && image.getColorModel().getPixelSize() <= 8) || (image.getType() == BufferedImage.TYPE_BYTE_BINARY && image.getColorModel().getPixelSize() == 1)) { // grayscale images need one color per sample bpc = image.getColorModel().getPixelSize(); deviceColorSpace = PDDeviceGray.INSTANCE; FastByteArrayOutputStream bos = new FastByteArrayOutputStream( (width * bpc / 8) + (width * bpc % 8 != 0 ? 1 : 0) * height); try (MemoryCacheImageOutputStream mcios = new MemoryCacheImageOutputStream(bos)) { for (int y = 0; y < height; ++y) { for (int pixel : image.getRGB(0, y, width, 1, rgbLineBuffer, 0, width)) { mcios.writeBits(pixel & 0xFF, bpc); } int bitOffset = mcios.getBitOffset(); if (bitOffset != 0) { mcios.writeBits(0, 8 - bitOffset); } } mcios.flush(); } imageData = bos.toByteArray(); } else { // RGB bpc = 8; deviceColorSpace = PDDeviceRGB.INSTANCE; imageData = new byte[width * height * 3]; int byteIdx = 0; for (int y = 0; y < height; ++y) { for (int pixel : image.getRGB(0, y, width, 1, rgbLineBuffer, 0, width)) { imageData[byteIdx++] = (byte) ((pixel >> 16) & 0xFF); imageData[byteIdx++] = (byte) ((pixel >> 8) & 0xFF); imageData[byteIdx++] = (byte) (pixel & 0xFF); } } } PDImageXObject pdImage = prepareImageXObject(imageData, image.getWidth(), image.getHeight(), bpc, deviceColorSpace); // alpha -> soft mask PDImage xAlpha = createAlphaFromARGBImage(image); if (xAlpha != null) { pdImage.getCOSObject().setItem(COSName.SMASK, xAlpha); } return pdImage; } /** * Creates a grayscale Flate encoded PDImageXObject from the alpha channel of an image. * * @param image an ARGB image. * * @return the alpha channel of an image as a grayscale image. * * @throws IOException if something goes wrong */ private static PDImageXObject createAlphaFromARGBImage(BufferedImage image) throws IOException { // this implementation makes the assumption that the raster uses // SinglePixelPackedSampleModel, i.e. the values can be used 1:1 for // the stream. // Sadly the type of the databuffer is TYPE_INT and not TYPE_BYTE. if (!image.getColorModel().hasAlpha()) { return null; } // extract the alpha information WritableRaster alphaRaster = image.getAlphaRaster(); if (alphaRaster == null) { // happens sometimes (PDFBOX-2654) despite colormodel claiming to have alpha return createAlphaFromARGBImage2(image); } int[] pixels = alphaRaster.getPixels(0, 0, alphaRaster.getWidth(), alphaRaster.getHeight(), (int[]) null); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int bpc; if (image.getTransparency() == Transparency.BITMASK) { bpc = 1; MemoryCacheImageOutputStream mcios = new MemoryCacheImageOutputStream(bos); int width = alphaRaster.getWidth(); int p = 0; for (int pixel : pixels) { mcios.writeBit(pixel); ++p; if (p % width == 0) { while (mcios.getBitOffset() != 0) { mcios.writeBit(0); } } } mcios.flush(); mcios.close(); } else { bpc = 8; for (int pixel : pixels) { bos.write(pixel); } } PDImageXObject pdImage = prepareImageXObject(bos.toByteArray(), image.getWidth(), image.getHeight(), bpc, PDDeviceGray.INSTANCE); return pdImage; } // create alpha image the hard way: get the alpha through getRGB() private static PDImageXObject createAlphaFromARGBImage2(BufferedImage bi) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); int bpc; if (bi.getTransparency() == Transparency.BITMASK) { bpc = 1; MemoryCacheImageOutputStream mcios = new MemoryCacheImageOutputStream(bos); for (int y = 0, h = bi.getHeight(); y < h; ++y) { for (int x = 0, w = bi.getWidth(); x < w; ++x) { int alpha = bi.getRGB(x, y) >>> 24; mcios.writeBit(alpha); } while (mcios.getBitOffset() != 0) { mcios.writeBit(0); } } mcios.flush(); mcios.close(); } else { bpc = 8; for (int y = 0, h = bi.getHeight(); y < h; ++y) { for (int x = 0, w = bi.getWidth(); x < w; ++x) { int alpha = bi.getRGB(x, y) >>> 24; bos.write(alpha); } } } PDImageXObject pdImage = prepareImageXObject(bos.toByteArray(), bi.getWidth(), bi.getHeight(), bpc, PDDeviceGray.INSTANCE); return pdImage; } /** * Create a PDImageXObject while making a decision whether not to compress, use Flate filter only, or Flate and LZW * filters. * * @param byteArray array with data. * @param width the image width * @param height the image height * @param bitsPerComponent the bits per component * @param initColorSpace the color space * @return the newly created PDImageXObject with the data compressed. * @throws IOException */ private static PDImageXObject prepareImageXObject(byte[] byteArray, int width, int height, int bitsPerComponent, PDColorSpace initColorSpace) throws IOException { FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); Filter filter = FilterFactory.INSTANCE.getFilter(COSName.FLATE_DECODE); filter.encode(new ByteArrayInputStream(byteArray), baos, new COSDictionary()); ByteArrayInputStream encodedByteStream = new ByteArrayInputStream(baos.toByteArray()); return new PDImageXObject(encodedByteStream, COSName.FLATE_DECODE, width, height, bitsPerComponent, initColorSpace); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/PDImage.java000066400000000000000000000107701320103431700273140ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.image; import java.awt.Paint; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; /** * An image in a PDF document. * * @author John Hewson */ public interface PDImage extends COSObjectable { /** * Returns the content of this image as an AWT buffered image with an (A)RGB color space. The size of the returned * image is the larger of the size of the image itself or its mask. * * @return content of this image as a buffered image. * @throws IOException */ BufferedImage getImage() throws IOException; /** * Returns an ARGB image filled with the given paint and using this image as a mask. * * @param paint the paint to fill the visible portions of the image with * @return a masked image filled with the given paint * @throws IOException if the image cannot be read * @throws IllegalStateException if the image is not a stencil. */ BufferedImage getStencilImage(Paint paint) throws IOException; /** * Returns an InputStream containing the image data, irrespective of whether this is an inline image or an image * XObject. * * @return Decoded stream * @throws IOException if the data could not be read. */ InputStream createInputStream() throws IOException; /** * @return image data in the form of a {@link ByteBuffer} */ ByteBuffer asByteBuffer() throws IOException; /** * Returns true if the image has no data. * * @throws IOException */ boolean isEmpty() throws IOException; /** * Returns true if the image is a stencil mask. */ boolean isStencil(); /** * Sets whether or not the image is a stencil. This corresponds to the {@code ImageMask} entry in the image stream's * dictionary. * * @param isStencil True to make the image a stencil. */ void setStencil(boolean isStencil); /** * Returns bits per component of this image, or -1 if one has not been set. */ int getBitsPerComponent(); /** * Set the number of bits per component. * * @param bitsPerComponent The number of bits per component. */ void setBitsPerComponent(int bitsPerComponent); /** * Returns the image's color space. * * @throws IOException If there is an error getting the color space. */ PDColorSpace getColorSpace() throws IOException; /** * Sets the color space for this image. * * @param colorSpace The color space for this image. */ void setColorSpace(PDColorSpace colorSpace); /** * Returns height of this image, or -1 if one has not been set. */ int getHeight(); /** * Sets the height of the image. * * @param height The height of the image. */ void setHeight(int height); /** * Returns the width of this image, or -1 if one has not been set. */ int getWidth(); /** * Sets the width of the image. * * @param width The width of the image. */ void setWidth(int width); /** * Sets the decode array. * * @param decode the new decode array. */ void setDecode(COSArray decode); /** * Returns the decode array. */ COSArray getDecode(); /** * Returns true if the image should be interpolated when rendered. */ boolean getInterpolate(); /** * Sets the Interpolate flag, true for high-quality image scaling. */ void setInterpolate(boolean value); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/PDImageXObject.java000066400000000000000000000401631320103431700305720ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.image; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import javax.imageio.ImageIO; import org.apache.commons.io.IOUtils; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.common.PDMetadata; import org.sejda.sambox.pdmodel.common.PDStream; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.util.filetypedetector.FileType; import org.sejda.sambox.util.filetypedetector.FileTypeDetector; /** * An Image XObject. * * @author John Hewson * @author Ben Litchfield */ public final class PDImageXObject extends PDXObject implements PDImage { private BufferedImage cachedImage; private PDColorSpace colorSpace; private PDResources resources; // current resource dictionary (has color spaces) /** * Creates a thumbnail Image XObject from the given COSBase and name. * * @param cosStream the COS stream * @return an XObject * @throws IOException if there is an error creating the XObject. */ public static PDImageXObject createThumbnail(COSStream cosStream) throws IOException { // thumbnails are special, any non-null subtype is treated as being "Image" PDStream pdStream = new PDStream(cosStream); return new PDImageXObject(pdStream, null); } /** * Creates an Image XObject in the given document. * * @param document the current document * @throws java.io.IOException if there is an error creating the XObject. */ public PDImageXObject() throws IOException { this(new PDStream(), null); } /** * Creates an Image XObject in the given document using the given filtered stream. * * @param encodedStream a filtered stream of image data * @param cosFilter the filter or a COSArray of filters * @param width the image width * @param height the image height * @param bitsPerComponent the bits per component * @param initColorSpace the color space * @throws IOException if there is an error creating the XObject. */ public PDImageXObject(InputStream encodedStream, COSBase cosFilter, int width, int height, int bitsPerComponent, PDColorSpace initColorSpace) throws IOException { super(createRawStream(encodedStream), COSName.IMAGE); getCOSObject().setItem(COSName.FILTER, cosFilter); resources = null; colorSpace = null; setBitsPerComponent(bitsPerComponent); setWidth(width); setHeight(height); setColorSpace(initColorSpace); } /** * Creates a COS stream from raw (encoded) data. */ private static COSStream createRawStream(InputStream rawInput) throws IOException { COSStream stream = new COSStream(); try (OutputStream output = stream.createFilteredStream()) { IOUtils.copy(rawInput, output); } return stream; } /** * Creates an Image XObject with the given stream as its contents and current color spaces. * * @param stream the XObject stream to read * @param resources the current resources * @throws java.io.IOException if there is an error creating the XObject. */ public PDImageXObject(PDStream stream, PDResources resources) throws IOException { super(stream, COSName.IMAGE); stream.getCOSObject().addAll(stream.getCOSObject().getDecodeResult().getParameters()); this.resources = resources; this.colorSpace = stream.getCOSObject().getDecodeResult().getJPXColorSpace(); } public static PDImageXObject createFromFile(String imagePath) throws IOException { return createFromFile(new File(imagePath)); } public static PDImageXObject createFromFile(File file) throws IOException { requireNotNullArg(file, "Cannot create image from a null file"); // we first try to match the first bytes to some known pattern, so we don't rely on the extension first FileType fileType = FileTypeDetector.detectFileType(file); if (fileType.equals(FileType.JPEG)) { return JPEGFactory.createFromFile(file); } if (fileType.equals(FileType.TIFF)) { return CCITTFactory.createFromFile(file); } // last resort, let's see if ImageIO can read it BufferedImage image = ImageIO.read(file); requireNotNullArg(image, "Image type not supported " + file.getName()); return LosslessFactory.createFromImage(image); } /** * Returns the metadata associated with this XObject, or null if there is none. * * @return the metadata associated with this object. */ public PDMetadata getMetadata() { COSStream cosStream = getCOSObject().getDictionaryObject(COSName.METADATA, COSStream.class); if (cosStream != null) { return new PDMetadata(cosStream); } return null; } /** * Sets the metadata associated with this XObject, or null if there is none. * * @param meta the metadata associated with this object */ public void setMetadata(PDMetadata meta) { getCOSObject().setItem(COSName.METADATA, meta); } /** * Returns the key of this XObject in the structural parent tree. * * @return this object's key the structural parent tree */ public int getStructParent() { return getCOSObject().getInt(COSName.STRUCT_PARENT, 0); } /** * Sets the key of this XObject in the structural parent tree. * * @param key the new key for this XObject */ public void setStructParent(int key) { getCOSObject().setInt(COSName.STRUCT_PARENT, key); } /** * {@inheritDoc} The returned images are cached for the lifetime of this XObject. */ @Override public BufferedImage getImage() throws IOException { if (cachedImage != null) { return cachedImage; } // get image as RGB BufferedImage image = SampledImageReader.getRGBImage(this, getColorKeyMask()); // soft mask (overrides explicit mask) PDImageXObject softMask = getSoftMask(); if (softMask != null) { image = applyMask(image, softMask.getOpaqueImage(), true); } else { // explicit mask - to be applied only if /ImageMask true PDImageXObject mask = getMask(); if (mask != null && mask.isStencil()) { image = applyMask(image, mask.getOpaqueImage(), false); } } cachedImage = image; return image; } /** * * @return the image without mask applied. The image is not cached * @throws IOException */ public BufferedImage getImageWithoutMasks() throws IOException { return SampledImageReader.getRGBImage(this, getColorKeyMask()); } /** * {@inheritDoc} The returned images are not cached. */ @Override public BufferedImage getStencilImage(Paint paint) throws IOException { if (!isStencil()) { throw new IllegalStateException("Image is not a stencil"); } return SampledImageReader.getStencilImage(this, paint); } /** * Returns an RGB buffered image containing the opaque image stream without any masks applied. If this Image XObject * is a mask then the buffered image will contain the raw mask. * * @return the image without any masks applied * @throws IOException if the image cannot be read */ public BufferedImage getOpaqueImage() throws IOException { return SampledImageReader.getRGBImage(this, null); } // explicit mask: RGB + Binary -> ARGB // soft mask: RGB + Gray -> ARGB private BufferedImage applyMask(BufferedImage image, BufferedImage mask, boolean isSoft) { if (mask == null) { return image; } int width = image.getWidth(); int height = image.getHeight(); // scale mask to fit image, or image to fit mask, whichever is larger if (mask.getWidth() < width || mask.getHeight() < height) { mask = scaleImage(mask, width, height); } else if (mask.getWidth() > width || mask.getHeight() > height) { width = mask.getWidth(); height = mask.getHeight(); image = scaleImage(image, width, height); } // compose to ARGB BufferedImage masked = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); WritableRaster src = image.getRaster(); WritableRaster dest = masked.getRaster(); WritableRaster alpha = mask.getRaster(); float[] rgb = new float[4]; float[] rgba = new float[4]; float[] alphaPixel = null; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { src.getPixel(x, y, rgb); rgba[0] = rgb[0]; rgba[1] = rgb[1]; rgba[2] = rgb[2]; alphaPixel = alpha.getPixel(x, y, alphaPixel); if (isSoft) { rgba[3] = alphaPixel[0]; } else { rgba[3] = 255 - alphaPixel[0]; } dest.setPixel(x, y, rgba); } } return masked; } /** * High-quality image scaling. */ private BufferedImage scaleImage(BufferedImage image, int width, int height) { BufferedImage image2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = image2.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.drawImage(image, 0, 0, width, height, 0, 0, image.getWidth(), image.getHeight(), null); g.dispose(); return image2; } /** * Returns the Mask Image XObject associated with this image, or null if there is none. * * @return Mask Image XObject */ public PDImageXObject getMask() throws IOException { COSStream cosStream = getCOSObject().getDictionaryObject(COSName.MASK, COSStream.class); if (cosStream != null) { // always DeviceGray return new PDImageXObject(new PDStream(cosStream), null); } return null; } /** * Returns the color key mask array associated with this image, or null if there is none. * * @return Mask Image XObject */ public COSArray getColorKeyMask() { return getCOSObject().getDictionaryObject(COSName.MASK, COSArray.class); } /** * Returns the Soft Mask Image XObject associated with this image, or null if there is none. * * @return the SMask Image XObject, or null. */ public PDImageXObject getSoftMask() throws IOException { COSStream cosStream = getCOSObject().getDictionaryObject(COSName.SMASK, COSStream.class); if (cosStream != null) { // always DeviceGray return new PDImageXObject(new PDStream(cosStream), null); } return null; } @Override public int getBitsPerComponent() { if (isStencil()) { return 1; } return getCOSObject().getInt(COSName.BITS_PER_COMPONENT, COSName.BPC); } @Override public void setBitsPerComponent(int bpc) { getCOSObject().setInt(COSName.BITS_PER_COMPONENT, bpc); } @Override public PDColorSpace getColorSpace() throws IOException { if (colorSpace == null) { COSBase cosBase = getCOSObject().getDictionaryObject(COSName.COLORSPACE, COSName.CS); if (cosBase != null) { colorSpace = PDColorSpace.create(cosBase, resources); } else if (isStencil()) { // stencil mask color space must be gray, it is often missing return PDDeviceGray.INSTANCE; } else { // an image without a color space is always broken throw new IOException("could not determine color space"); } } return colorSpace; } @Override public InputStream createInputStream() throws IOException { return getStream().createInputStream(); } @Override public ByteBuffer asByteBuffer() throws IOException { return getStream().getCOSObject().getUnfilteredByteBuffer(); } @Override public boolean isEmpty() throws IOException { return getStream().getCOSObject().isEmpty(); } @Override public void setColorSpace(PDColorSpace cs) { getCOSObject().setItem(COSName.COLORSPACE, cs != null ? cs.getCOSObject() : null); } @Override public int getHeight() { return getCOSObject().getInt(COSName.HEIGHT); } @Override public void setHeight(int h) { getCOSObject().setInt(COSName.HEIGHT, h); } @Override public int getWidth() { return getCOSObject().getInt(COSName.WIDTH); } @Override public void setWidth(int w) { getCOSObject().setInt(COSName.WIDTH, w); } @Override public boolean getInterpolate() { return getCOSObject().getBoolean(COSName.INTERPOLATE, false); } @Override public void setInterpolate(boolean value) { getCOSObject().setBoolean(COSName.INTERPOLATE, value); } @Override public void setDecode(COSArray decode) { getCOSObject().setItem(COSName.DECODE, decode); } @Override public COSArray getDecode() { COSBase decode = getCOSObject().getDictionaryObject(COSName.DECODE); if (decode instanceof COSArray) { return (COSArray) decode; } return null; } @Override public boolean isStencil() { return getCOSObject().getBoolean(COSName.IMAGE_MASK, false); } @Override public void setStencil(boolean isStencil) { getCOSObject().setBoolean(COSName.IMAGE_MASK, isStencil); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/PDInlineImage.java000066400000000000000000000223401320103431700304470ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.image; import java.awt.Paint; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.List; import org.sejda.io.FastByteArrayOutputStream; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.filter.DecodeResult; import org.sejda.sambox.filter.Filter; import org.sejda.sambox.filter.FilterFactory; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; /** * An inline image object which uses a special syntax to express the data for a small image directly within the content * stream. * * @author Ben Litchfield * @author John Hewson */ public final class PDInlineImage implements PDImage { // image parameters private final COSDictionary parameters; // the current resources, contains named color spaces private final PDResources resources; // image data private final byte[] decodedData; /** * Creates an inline image from the given parameters and data. * * @param parameters the image parameters * @param data the image data * @param resources the current resources * @throws IOException if the stream cannot be decoded */ public PDInlineImage(COSDictionary parameters, byte[] data, PDResources resources) throws IOException { this.parameters = parameters; this.resources = resources; DecodeResult decodeResult = null; List filters = getFilters(); if (filters == null || filters.isEmpty()) { this.decodedData = data; } else { ByteArrayInputStream in = new ByteArrayInputStream(data); FastByteArrayOutputStream out = new FastByteArrayOutputStream(data.length); for (int i = 0; i < filters.size(); i++) { // TODO handling of abbreviated names belongs here, rather than in other classes out.reset(); Filter filter = FilterFactory.INSTANCE.getFilter(filters.get(i)); decodeResult = filter.decode(in, out, parameters, i); in = new ByteArrayInputStream(out.toByteArray()); } this.decodedData = out.toByteArray(); } // repair parameters if (decodeResult != null) { parameters.addAll(decodeResult.getParameters()); } } @Override public COSBase getCOSObject() { return parameters; } @Override public int getBitsPerComponent() { if (isStencil()) { return 1; } return parameters.getInt(COSName.BPC, COSName.BITS_PER_COMPONENT, -1); } @Override public void setBitsPerComponent(int bitsPerComponent) { parameters.setInt(COSName.BPC, bitsPerComponent); } @Override public PDColorSpace getColorSpace() throws IOException { COSBase cs = parameters.getDictionaryObject(COSName.CS, COSName.COLORSPACE); if (cs != null) { return createColorSpace(cs); } else if (isStencil()) { // stencil mask color space must be gray, it is often missing return PDDeviceGray.INSTANCE; } else { // an image without a color space is always broken throw new IOException("could not determine inline image color space"); } } // deliver the long name of a device colorspace, or the parameter private COSBase toLongName(COSBase cs) { if (COSName.RGB.equals(cs)) { return COSName.DEVICERGB; } if (COSName.CMYK.equals(cs)) { return COSName.DEVICECMYK; } if (COSName.G.equals(cs)) { return COSName.DEVICEGRAY; } return cs; } private PDColorSpace createColorSpace(COSBase cs) throws IOException { if (cs instanceof COSName) { return PDColorSpace.create(toLongName(cs), resources); } if (cs instanceof COSArray && ((COSArray) cs).size() > 1) { COSArray srcArray = (COSArray) cs; COSBase csType = srcArray.get(0); if (COSName.I.equals(csType) || COSName.INDEXED.equals(csType)) { COSArray dstArray = new COSArray(); dstArray.addAll(srcArray); dstArray.set(0, COSName.INDEXED); dstArray.set(1, toLongName(srcArray.get(1))); return PDColorSpace.create(dstArray, resources); } throw new IOException("Illegal type of inline image color space: " + csType); } throw new IOException("Illegal type of object for inline image color space: " + cs); } @Override public void setColorSpace(PDColorSpace colorSpace) { COSBase base = null; if (colorSpace != null) { base = colorSpace.getCOSObject(); } parameters.setItem(COSName.CS, base); } @Override public int getHeight() { return parameters.getInt(COSName.H, COSName.HEIGHT, -1); } @Override public void setHeight(int height) { parameters.setInt(COSName.H, height); } @Override public int getWidth() { return parameters.getInt(COSName.W, COSName.WIDTH, -1); } @Override public void setWidth(int width) { parameters.setInt(COSName.W, width); } @Override public boolean getInterpolate() { return parameters.getBoolean(COSName.I, COSName.INTERPOLATE, false); } @Override public void setInterpolate(boolean value) { parameters.setBoolean(COSName.I, value); } /** * Returns a list of filters applied to this stream, or null if there are none. * * @return a list of filters applied to this stream */ // TODO return an empty list if there are none? public List getFilters() { List names = null; COSBase filters = parameters.getDictionaryObject(COSName.F, COSName.FILTER); if (filters instanceof COSName) { COSName name = (COSName) filters; names = new COSArrayList<>(name.getName(), name, parameters, COSName.FILTER); } else if (filters instanceof COSArray) { names = COSArrayList.convertCOSNameCOSArrayToList((COSArray) filters); } return names; } /** * Sets which filters are applied to this stream. * * @param filters the filters to apply to this stream. */ public void setFilters(List filters) { COSBase obj = COSArrayList.convertStringListToCOSNameCOSArray(filters); parameters.setItem(COSName.F, obj); } @Override public void setDecode(COSArray decode) { parameters.setItem(COSName.D, decode); } @Override public COSArray getDecode() { return (COSArray) parameters.getDictionaryObject(COSName.D, COSName.DECODE); } @Override public boolean isStencil() { return parameters.getBoolean(COSName.IM, COSName.IMAGE_MASK, false); } @Override public void setStencil(boolean isStencil) { parameters.setBoolean(COSName.IM, isStencil); } @Override public InputStream createInputStream() { return new ByteArrayInputStream(decodedData); } @Override public ByteBuffer asByteBuffer() { return ByteBuffer.wrap(decodedData).asReadOnlyBuffer(); } @Override public boolean isEmpty() { return decodedData.length == 0; } /** * Returns the inline image data. */ public byte[] getData() { return decodedData; } @Override public BufferedImage getImage() throws IOException { return SampledImageReader.getRGBImage(this, null); } @Override public BufferedImage getStencilImage(Paint paint) throws IOException { if (!isStencil()) { throw new IllegalStateException("Image is not a stencil"); } return SampledImageReader.getStencilImage(this, paint); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/SampledImageReader.java000066400000000000000000000423631320103431700315240ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.image; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.Arrays; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.pdmodel.graphics.color.PDIndexed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Reads a sampled image from a PDF file. * * @author John Hewson */ final class SampledImageReader { private static final Logger LOG = LoggerFactory.getLogger(SampledImageReader.class); private SampledImageReader() { } /** * Returns an ARGB image filled with the given paint and using the given image as a mask. * * @param paint the paint to fill the visible portions of the image with * @return a masked image filled with the given paint * @throws IOException if the image cannot be read * @throws IllegalStateException if the image is not a stencil. */ public static BufferedImage getStencilImage(PDImage pdImage, Paint paint) throws IOException { int width = pdImage.getWidth(); int height = pdImage.getHeight(); // compose to ARGB BufferedImage masked = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g = masked.createGraphics(); // draw the mask // g.drawImage(mask, 0, 0, null); // fill with paint using src-in // g.setComposite(AlphaComposite.SrcIn); g.setPaint(paint); g.fillRect(0, 0, width, height); g.dispose(); // set the alpha WritableRaster raster = masked.getRaster(); final int[] transparent = new int[4]; // avoid getting a BufferedImage for the mask to lessen memory footprint. // Such masks are always bpc=1 and have no colorspace, but have a decode. // (see 8.9.6.2 Stencil Masking) try (ImageInputStream iis = new MemoryCacheImageInputStream(pdImage.createInputStream())) { final float[] decode = getDecodeArray(pdImage); int value = decode[0] < decode[1] ? 1 : 0; int rowLen = width / 8; if (width % 8 > 0) { rowLen++; } byte[] buff = new byte[rowLen]; for (int y = 0; y < height; y++) { int x = 0; int readLen = iis.read(buff); for (int r = 0; r < rowLen && r < readLen; r++) { int byteValue = buff[r]; int mask = 128; int shift = 7; for (int i = 0; i < 8; i++) { int bit = (byteValue & mask) >> shift; mask >>= 1; --shift; if (bit == value) { raster.setPixel(x, y, transparent); } x++; if (x == width) { break; } } } if (readLen != rowLen) { LOG.warn("premature EOF, image will be incomplete"); break; } } } return masked; } /** * Returns the content of the given image as an AWT buffered image with an RGB color space. If a color key mask is * provided then an ARGB image is returned instead. This method never returns null. * * @param pdImage the image to read * @param colorKey an optional color key mask * @return content of this image as an RGB buffered image * @throws IOException if the image cannot be read */ public static BufferedImage getRGBImage(PDImage pdImage, COSArray colorKey) throws IOException { if (pdImage.isEmpty()) { throw new IOException("Image stream is empty"); } // get parameters, they must be valid or have been repaired final PDColorSpace colorSpace = pdImage.getColorSpace(); final int numComponents = colorSpace.getNumberOfComponents(); final int width = pdImage.getWidth(); final int height = pdImage.getHeight(); final int bitsPerComponent = pdImage.getBitsPerComponent(); final float[] decode = getDecodeArray(pdImage); if (width <= 0 || height <= 0) { throw new IOException("image width and height must be positive"); } if (bitsPerComponent == 1 && colorKey == null && numComponents == 1) { return from1Bit(pdImage); } // // An AWT raster must use 8/16/32 bits per component. Images with < 8bpc // will be unpacked into a byte-backed raster. Images with 16bpc will be reduced // in depth to 8bpc as they will be drawn to TYPE_INT_RGB images anyway. All code // in PDColorSpace#toRGBImage expects an 8-bit range, i.e. 0-255. // WritableRaster raster = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, width, height, numComponents, new Point(0, 0)); final float[] defaultDecode = pdImage.getColorSpace().getDefaultDecode(8); if (bitsPerComponent == 8 && Arrays.equals(decode, defaultDecode) && colorKey == null) { // convert image, faster path for non-decoded, non-colormasked 8-bit images return from8bit(pdImage, raster); } return fromAny(pdImage, raster, colorKey); } private static BufferedImage from1Bit(PDImage pdImage) throws IOException { final PDColorSpace colorSpace = pdImage.getColorSpace(); final int width = pdImage.getWidth(); final int height = pdImage.getHeight(); final float[] decode = getDecodeArray(pdImage); BufferedImage bim = null; WritableRaster raster; byte[] output; if (colorSpace instanceof PDDeviceGray) { // TYPE_BYTE_GRAY and not TYPE_BYTE_BINARY because this one is handled // without conversion to RGB by Graphics.drawImage // this reduces the memory footprint, only one byte per pixel instead of three. bim = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); raster = bim.getRaster(); } else { raster = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, width, height, 1, new Point(0, 0)); } output = ((DataBufferByte) raster.getDataBuffer()).getData(); // read bit stream try (InputStream iis = pdImage.createInputStream()) { final boolean isIndexed = colorSpace instanceof PDIndexed; int rowLen = width / 8; if (width % 8 > 0) { rowLen++; } // read stream byte value0; byte value1; if (isIndexed || decode[0] < decode[1]) { value0 = 0; value1 = (byte) 255; } else { value0 = (byte) 255; value1 = 0; } byte[] buff = new byte[rowLen]; int idx = 0; for (int y = 0; y < height; y++) { int x = 0; int readLen = iis.read(buff); for (int r = 0; r < rowLen && r < readLen; r++) { int value = buff[r]; int mask = 128; for (int i = 0; i < 8; i++) { int bit = value & mask; mask >>= 1; output[idx++] = bit == 0 ? value0 : value1; x++; if (x == width) { break; } } } if (readLen != rowLen) { LOG.warn("premature EOF, image will be incomplete"); break; } } if (bim != null) { return bim; } // use the color space to convert the image to RGB return colorSpace.toRGBImage(raster); } } // faster, 8-bit non-decoded, non-colormasked image conversion private static BufferedImage from8bit(PDImage pdImage, WritableRaster raster) throws IOException { // get the raster's underlying byte buffer byte[][] banks = ((DataBufferByte) raster.getDataBuffer()).getBankData(); ByteBuffer source = pdImage.asByteBuffer(); final int width = pdImage.getWidth(); final int height = pdImage.getHeight(); final int numComponents = pdImage.getColorSpace().getNumberOfComponents(); int max = width * height; for (int c = 0; c < numComponents; c++) { int sourceOffset = c; for (int i = 0; i < max; i++) { banks[c][i] = source.get(sourceOffset); sourceOffset += numComponents; } } // use the color space to convert the image to RGB return pdImage.getColorSpace().toRGBImage(raster); } // slower, general-purpose image conversion from any image format private static BufferedImage fromAny(PDImage pdImage, WritableRaster raster, COSArray colorKey) throws IOException { final PDColorSpace colorSpace = pdImage.getColorSpace(); final int numComponents = colorSpace.getNumberOfComponents(); final int width = pdImage.getWidth(); final int height = pdImage.getHeight(); final int bitsPerComponent = pdImage.getBitsPerComponent(); final float[] decode = getDecodeArray(pdImage); try (ImageInputStream iis = new MemoryCacheImageInputStream(pdImage.createInputStream())) { final float sampleMax = (float) Math.pow(2, bitsPerComponent) - 1f; final boolean isIndexed = colorSpace instanceof PDIndexed; // init color key mask float[] colorKeyRanges = null; BufferedImage colorKeyMask = null; if (colorKey != null) { colorKeyRanges = colorKey.toFloatArray(); colorKeyMask = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); } // calculate row padding int padding = 0; if (width * numComponents * bitsPerComponent % 8 > 0) { padding = 8 - (width * numComponents * bitsPerComponent % 8); } // read stream byte[] srcColorValues = new byte[numComponents]; byte[] alpha = new byte[1]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { boolean isMasked = true; for (int c = 0; c < numComponents; c++) { int value = (int) iis.readBits(bitsPerComponent); // color key mask requires values before they are decoded if (colorKeyRanges != null) { isMasked &= value >= colorKeyRanges[c * 2] && value <= colorKeyRanges[c * 2 + 1]; } // decode array final float dMin = decode[c * 2]; final float dMax = decode[(c * 2) + 1]; // interpolate to domain float output = dMin + (value * ((dMax - dMin) / sampleMax)); if (isIndexed) { // indexed color spaces get the raw value, because the TYPE_BYTE // below cannot be reversed by the color space without it having // knowledge of the number of bits per component srcColorValues[c] = (byte) Math.round(output); } else { // interpolate to TYPE_BYTE int outputByte = Math .round(((output - Math.min(dMin, dMax)) / Math.abs(dMax - dMin)) * 255f); srcColorValues[c] = (byte) outputByte; } } raster.setDataElements(x, y, srcColorValues); // set alpha channel in color key mask, if any if (colorKeyMask != null) { alpha[0] = (byte) (isMasked ? 255 : 0); colorKeyMask.getRaster().setDataElements(x, y, alpha); } } // rows are padded to the nearest byte iis.readBits(padding); } // use the color space to convert the image to RGB BufferedImage rgbImage = colorSpace.toRGBImage(raster); // apply color mask, if any if (colorKeyMask != null) { return applyColorKeyMask(rgbImage, colorKeyMask); } return rgbImage; } } // color key mask: RGB + Binary -> ARGB private static BufferedImage applyColorKeyMask(BufferedImage image, BufferedImage mask) { int width = image.getWidth(); int height = image.getHeight(); // compose to ARGB BufferedImage masked = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); WritableRaster src = image.getRaster(); WritableRaster dest = masked.getRaster(); WritableRaster alpha = mask.getRaster(); float[] rgb = new float[3]; float[] rgba = new float[4]; float[] alphaPixel = null; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { src.getPixel(x, y, rgb); rgba[0] = rgb[0]; rgba[1] = rgb[1]; rgba[2] = rgb[2]; alphaPixel = alpha.getPixel(x, y, alphaPixel); rgba[3] = 255 - alphaPixel[0]; dest.setPixel(x, y, rgba); } } return masked; } // gets decode array from dictionary or returns default private static float[] getDecodeArray(PDImage pdImage) throws IOException { final COSArray cosDecode = pdImage.getDecode(); float[] decode = null; if (cosDecode != null) { int numberOfComponents = pdImage.getColorSpace().getNumberOfComponents(); if (cosDecode.size() != numberOfComponents * 2) { if (pdImage.isStencil() && cosDecode.size() >= 2 && cosDecode.get(0) instanceof COSNumber && cosDecode.get(1) instanceof COSNumber) { float decode0 = ((COSNumber) cosDecode.get(0)).floatValue(); float decode1 = ((COSNumber) cosDecode.get(1)).floatValue(); if (decode0 >= 0 && decode0 <= 1 && decode1 >= 0 && decode1 <= 1) { LOG.warn("decode array " + cosDecode + " not compatible with color space, using the first two entries"); return new float[] { decode0, decode1 }; } } LOG.error("decode array " + cosDecode + " not compatible with color space, using default"); } else { decode = cosDecode.toFloatArray(); } } // use color space default if (decode == null) { return pdImage.getColorSpace().getDefaultDecode(pdImage.getBitsPerComponent()); } return decode; } } UnsupportedTiffImageException.java000066400000000000000000000020161320103431700337440ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/image/* * 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. */ package org.sejda.sambox.pdmodel.graphics.image; import java.io.IOException; public class UnsupportedTiffImageException extends IOException { public UnsupportedTiffImageException(String message) { super(message); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/optionalcontent/000077500000000000000000000000001320103431700273345ustar00rootroot00000000000000PDOptionalContentGroup.java000066400000000000000000000044411320103431700345040ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/optionalcontent/* * 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. */ package org.sejda.sambox.pdmodel.graphics.optionalcontent; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDPropertyList; /** * An optional content group (OCG). */ public class PDOptionalContentGroup extends PDPropertyList { /** * Creates a new optional content group (OCG). * @param name the name of the content group */ public PDOptionalContentGroup(String name) { this.dict.setItem(COSName.TYPE, COSName.OCG); setName(name); } /** * Creates a new instance based on a given {@link COSDictionary}. * @param dict the dictionary */ public PDOptionalContentGroup(COSDictionary dict) { super(dict); if (!dict.getItem(COSName.TYPE).equals(COSName.OCG)) { throw new IllegalArgumentException( "Provided dictionary is not of type '" + COSName.OCG + "'"); } } /** * Returns the name of the optional content group. * @return the name */ public String getName() { return dict.getString(COSName.NAME); } /** * Sets the name of the optional content group. * @param name the name */ public void setName(String name) { dict.setString(COSName.NAME, name); } //TODO Add support for "Intent" and "Usage" @Override public String toString() { return super.toString() + " (" + getName() + ")"; } } PDOptionalContentProperties.java000066400000000000000000000237621320103431700355530ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/optionalcontent/* * 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. */ package org.sejda.sambox.pdmodel.graphics.optionalcontent; import java.util.Collection; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This class represents the optional content properties dictionary. * * @since PDF 1.5 */ public class PDOptionalContentProperties implements COSObjectable { /** * Enumeration for the BaseState dictionary entry on the "D" dictionary. */ public enum BaseState { /** The "ON" value. */ ON(COSName.ON), /** The "OFF" value. */ OFF(COSName.OFF), /** The "Unchanged" value. */ UNCHANGED(COSName.UNCHANGED); private final COSName name; private BaseState(COSName value) { this.name = value; } /** * Returns the PDF name for the state. * @return the name of the state */ public COSName getName() { return this.name; } /** * Returns the base state represented by the given {@link COSName}. * @param state the state name * @return the state enum value */ public static BaseState valueOf(COSName state) { if (state == null) { return BaseState.ON; } return BaseState.valueOf(state.getName().toUpperCase()); } } private final COSDictionary dict; /** * Creates a new optional content properties dictionary. */ public PDOptionalContentProperties() { this.dict = new COSDictionary(); this.dict.setItem(COSName.OCGS, new COSArray()); this.dict.setItem(COSName.D, new COSDictionary()); } /** * Creates a new instance based on a given {@link COSDictionary}. * @param props the dictionary */ public PDOptionalContentProperties(COSDictionary props) { this.dict = props; } /** {@inheritDoc} */ @Override public COSBase getCOSObject() { return this.dict; } private COSArray getOCGs() { COSArray ocgs = (COSArray)this.dict.getItem(COSName.OCGS); if (ocgs == null) { ocgs = new COSArray(); this.dict.setItem(COSName.OCGS, ocgs); //OCGs is required } return ocgs; } private COSDictionary getD() { COSDictionary d = (COSDictionary)this.dict.getDictionaryObject(COSName.D); if (d == null) { d = new COSDictionary(); this.dict.setItem(COSName.D, d); //D is required } return d; } /** * Returns the optional content group of the given name. * @param name the group name * @return the optional content group or null, if there is no such group */ public PDOptionalContentGroup getGroup(String name) { COSArray ocgs = getOCGs(); for (COSBase o : ocgs) { COSDictionary ocg = toDictionary(o); String groupName = ocg.getString(COSName.NAME); if (groupName.equals(name)) { return new PDOptionalContentGroup(ocg); } } return null; } /** * Adds an optional content group (OCG). * @param ocg the optional content group */ public void addGroup(PDOptionalContentGroup ocg) { COSArray ocgs = getOCGs(); ocgs.add(ocg.getCOSObject()); //By default, add new group to the "Order" entry so it appears in the user interface COSArray order = (COSArray)getD().getDictionaryObject(COSName.ORDER); if (order == null) { order = new COSArray(); getD().setItem(COSName.ORDER, order); } order.add(ocg); } /** * Returns the collection of all optional content groups. * @return the optional content groups */ public Collection getOptionalContentGroups() { Collection coll = new java.util.ArrayList(); COSArray ocgs = getOCGs(); for (COSBase base : ocgs) { coll.add(new PDOptionalContentGroup(toDictionary(base))); } return coll; } /** * Returns the base state for optional content groups. * @return the base state */ public BaseState getBaseState() { COSDictionary d = getD(); COSName name = (COSName)d.getItem(COSName.BASE_STATE); return BaseState.valueOf(name); } /** * Sets the base state for optional content groups. * @param state the base state */ public void setBaseState(BaseState state) { COSDictionary d = getD(); d.setItem(COSName.BASE_STATE, state.getName()); } /** * Lists all optional content group names. * @return an array of all names */ public String[] getGroupNames() { COSArray ocgs = (COSArray)dict.getDictionaryObject(COSName.OCGS); int size = ocgs.size(); String[] groups = new String[size]; for (int i = 0; i < size; i++) { COSBase obj = ocgs.get(i); COSDictionary ocg = toDictionary(obj); groups[i] = ocg.getString(COSName.NAME); } return groups; } /** * Indicates whether a particular optional content group is found in the PDF file. * @param groupName the group name * @return true if the group exists, false otherwise */ public boolean hasGroup(String groupName) { String[] layers = getGroupNames(); for (String layer : layers) { if (layer.equals(groupName)) { return true; } } return false; } /** * Indicates whether an optional content group is enabled. * @param groupName the group name * @return true if the group is enabled */ public boolean isGroupEnabled(String groupName) { //TODO handle Optional Content Configuration Dictionaries, //i.e. OCProperties/Configs COSDictionary d = getD(); COSArray on = (COSArray)d.getDictionaryObject(COSName.ON); if (on != null) { for (COSBase o : on) { COSDictionary group = toDictionary(o); String name = group.getString(COSName.NAME); if (name.equals(groupName)) { return true; } } } COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF); if (off != null) { for (COSBase o : off) { COSDictionary group = toDictionary(o); String name = group.getString(COSName.NAME); if (name.equals(groupName)) { return false; } } } BaseState baseState = getBaseState(); boolean enabled = !baseState.equals(BaseState.OFF); //TODO What to do with BaseState.Unchanged? return enabled; } private COSDictionary toDictionary(COSBase o) { return (COSDictionary) o.getCOSObject(); } /** * Enables or disables an optional content group. * @param groupName the group name * @param enable true to enable, false to disable * @return true if the group already had an on or off setting, false otherwise */ public boolean setGroupEnabled(String groupName, boolean enable) { COSDictionary d = getD(); COSArray on = (COSArray)d.getDictionaryObject(COSName.ON); if (on == null) { on = new COSArray(); d.setItem(COSName.ON, on); } COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF); if (off == null) { off = new COSArray(); d.setItem(COSName.OFF, off); } boolean found = false; if (enable) { for (COSBase o : off) { COSDictionary group = toDictionary(o); String name = group.getString(COSName.NAME); if (name.equals(groupName)) { //enable group off.remove(o); on.add(o); found = true; break; } } } else { for (COSBase o : on) { COSDictionary group = toDictionary(o); String name = group.getString(COSName.NAME); if (name.equals(groupName)) { //disable group on.remove(o); off.add(o); found = true; break; } } } if (!found) { PDOptionalContentGroup ocg = getGroup(groupName); if (enable) { on.add(ocg.getCOSObject()); } else { off.add(ocg.getCOSObject()); } } return found; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/pattern/000077500000000000000000000000001320103431700255715ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/pattern/PDAbstractPattern.java000066400000000000000000000113711320103431700317640ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.pattern; import java.awt.geom.AffineTransform; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.util.Matrix; /** * A Pattern dictionary from a page's resources. */ public abstract class PDAbstractPattern implements COSObjectable { /** Tiling pattern type. */ public static final int TYPE_TILING_PATTERN = 1; /** Shading pattern type. */ public static final int TYPE_SHADING_PATTERN = 2; /** * Create the correct PD Model pattern based on the COS base pattern. * @param resourceDictionary the COS pattern dictionary * @return the newly created pattern resources object * @throws IOException If we are unable to create the PDPattern object. */ public static PDAbstractPattern create(COSDictionary resourceDictionary) throws IOException { PDAbstractPattern pattern; int patternType = resourceDictionary.getInt(COSName.PATTERN_TYPE, 0); switch (patternType) { case TYPE_TILING_PATTERN: pattern = new PDTilingPattern(resourceDictionary); break; case TYPE_SHADING_PATTERN: pattern = new PDShadingPattern(resourceDictionary); break; default: throw new IOException("Error: Unknown pattern type " + patternType); } return pattern; } private final COSDictionary patternDictionary; /** * Creates a new Pattern dictionary. */ public PDAbstractPattern() { patternDictionary = new COSDictionary(); patternDictionary.setName(COSName.TYPE, COSName.PATTERN.getName()); } /** * Creates a new Pattern dictionary from the given COS dictionary. * @param resourceDictionary The COSDictionary for this pattern resource. */ public PDAbstractPattern(COSDictionary resourceDictionary) { patternDictionary = resourceDictionary; } /** * This will get the underlying dictionary. * @return The dictionary for these pattern resources. */ @Override public COSDictionary getCOSObject() { return patternDictionary; } /** * This will set the paint type. * @param paintType The new paint type. */ public void setPaintType(int paintType) { patternDictionary.setInt(COSName.PAINT_TYPE, paintType); } /** * This will return the paint type. * @return The type of object that this is. */ public String getType() { return COSName.PATTERN.getName(); } /** * This will set the pattern type. * @param patternType The new pattern type. */ public void setPatternType(int patternType) { patternDictionary.setInt(COSName.PATTERN_TYPE, patternType); } /** * This will return the pattern type. * @return The pattern type */ public abstract int getPatternType(); /** * Returns the pattern matrix, or the identity matrix is none is available. */ public Matrix getMatrix() { COSArray array = (COSArray)getCOSObject().getDictionaryObject(COSName.MATRIX); if (array != null) { return new Matrix(array); } else { // default value is the identity matrix return new Matrix(); } } /** * Sets the optional Matrix entry for the Pattern. * @param transform the transformation matrix */ public void setMatrix(AffineTransform transform) { COSArray matrix = new COSArray(); double[] values = new double[6]; transform.getMatrix(values); for (double v : values) { matrix.add(new COSFloat((float)v)); } getCOSObject().setItem(COSName.MATRIX, matrix); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/pattern/PDShadingPattern.java000066400000000000000000000072701320103431700316010ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.pattern; import java.io.IOException; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.shading.PDShading; import org.sejda.sambox.pdmodel.graphics.state.PDExtendedGraphicsState; /** * A shading pattern dictionary. * */ public class PDShadingPattern extends PDAbstractPattern { private PDExtendedGraphicsState extendedGraphicsState; private PDShading shading; /** * Creates a new shading pattern. */ public PDShadingPattern() { super(); getCOSObject().setInt(COSName.PATTERN_TYPE, PDAbstractPattern.TYPE_SHADING_PATTERN); } /** * Creates a new shading pattern from the given COS dictionary. * @param resourceDictionary The COSDictionary for this pattern resource. */ public PDShadingPattern(COSDictionary resourceDictionary) { super(resourceDictionary); } @Override public int getPatternType() { return PDAbstractPattern.TYPE_SHADING_PATTERN; } /** * This will get the external graphics state for this pattern. * @return The extended graphics state for this pattern. */ public PDExtendedGraphicsState getExtendedGraphicsState() { if (extendedGraphicsState == null) { COSDictionary dictionary = (COSDictionary)getCOSObject() .getDictionaryObject(COSName.EXT_G_STATE); if( dictionary != null ) { extendedGraphicsState = new PDExtendedGraphicsState( dictionary ); } } return extendedGraphicsState; } /** * This will set the external graphics state for this pattern. * @param extendedGraphicsState The new extended graphics state for this pattern. */ public void setExtendedGraphicsState(PDExtendedGraphicsState extendedGraphicsState) { this.extendedGraphicsState = extendedGraphicsState; getCOSObject().setItem(COSName.EXT_G_STATE, extendedGraphicsState); } /** * This will get the shading resources for this pattern. * @return The shading resources for this pattern. * @throws IOException if something went wrong */ public PDShading getShading() throws IOException { if (shading == null) { COSDictionary dictionary = (COSDictionary) getCOSObject().getDictionaryObject(COSName.SHADING); if( dictionary != null ) { shading = PDShading.create(dictionary); } } return shading; } /** * This will set the shading resources for this pattern. * @param shadingResources The new shading resources for this pattern. */ public void setShading( PDShading shadingResources ) { shading = shadingResources; getCOSObject().setItem(COSName.SHADING, shadingResources); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/pattern/PDTilingPattern.java000066400000000000000000000143111320103431700314440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.pattern; import java.io.IOException; import java.io.InputStream; import org.sejda.sambox.contentstream.PDContentStream; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.PDStream; /** * A tiling pattern dictionary. * */ public class PDTilingPattern extends PDAbstractPattern implements PDContentStream { /** paint type 1 = colored tiling pattern. */ public static final int PAINT_COLORED = 1; /** paint type 2 = uncolored tiling pattern. */ public static final int PAINT_UNCOLORED = 2; /** tiling type 1 = constant spacing. */ public static final int TILING_CONSTANT_SPACING = 1; /** tiling type 2 = no distortion. */ public static final int TILING_NO_DISTORTION = 2; /** tiling type 3 = constant spacing and faster tiling. */ public static final int TILING_CONSTANT_SPACING_FASTER_TILING = 3; /** * Creates a new tiling pattern. */ public PDTilingPattern() { super(); getCOSObject().setInt(COSName.PATTERN_TYPE, PDAbstractPattern.TYPE_TILING_PATTERN); } /** * Creates a new tiling pattern from the given COS dictionary. * * @param resourceDictionary The COSDictionary for this pattern resource. */ public PDTilingPattern(COSDictionary resourceDictionary) { super(resourceDictionary); } @Override public int getPatternType() { return PDAbstractPattern.TYPE_TILING_PATTERN; } /** * This will set the paint type. * * @param paintType The new paint type. */ @Override public void setPaintType(int paintType) { getCOSObject().setInt(COSName.PAINT_TYPE, paintType); } /** * This will return the paint type. * * @return The paint type */ public int getPaintType() { return getCOSObject().getInt(COSName.PAINT_TYPE, 0); } /** * This will set the tiling type. * * @param tilingType The new tiling type. */ public void setTilingType(int tilingType) { getCOSObject().setInt(COSName.TILING_TYPE, tilingType); } /** * This will return the tiling type. * * @return The tiling type */ public int getTilingType() { return getCOSObject().getInt(COSName.TILING_TYPE, 0); } /** * This will set the XStep value. * * @param xStep The new XStep value. */ public void setXStep(float xStep) { getCOSObject().setFloat(COSName.X_STEP, xStep); } /** * This will return the XStep value. * * @return The XStep value */ public float getXStep() { return getCOSObject().getFloat(COSName.X_STEP, 0); } /** * This will set the YStep value. * * @param yStep The new YStep value. */ public void setYStep(float yStep) { getCOSObject().setFloat(COSName.Y_STEP, yStep); } /** * This will return the YStep value. * * @return The YStep value */ public float getYStep() { return getCOSObject().getFloat(COSName.Y_STEP, 0); } public PDStream getContentStream() { return new PDStream((COSStream) getCOSObject()); } @Override public InputStream getContents() throws IOException { COSDictionary dict = getCOSObject(); if (dict instanceof COSStream) { return ((COSStream) getCOSObject()).getUnfilteredStream(); } return null; } /** * This will get the resources for this pattern. This will return null if no resources are available at this level. * * @return The resources for this pattern. */ @Override public PDResources getResources() { PDResources retval = null; COSDictionary resources = (COSDictionary) getCOSObject() .getDictionaryObject(COSName.RESOURCES); if (resources != null) { retval = new PDResources(resources); } return retval; } /** * This will set the resources for this pattern. * * @param resources The new resources for this pattern. */ public void setResources(PDResources resources) { getCOSObject().setItem(COSName.RESOURCES, resources); } /** * An array of four numbers in the form coordinate system (see below), giving the coordinates of the left, bottom, * right, and top edges, respectively, of the pattern's bounding box. * * @return The BBox of the pattern. */ @Override public PDRectangle getBBox() { PDRectangle retval = null; COSArray array = (COSArray) getCOSObject().getDictionaryObject(COSName.BBOX); if (array != null) { retval = new PDRectangle(array); } return retval; } /** * This will set the BBox (bounding box) for this Pattern. * * @param bbox The new BBox for this Pattern. */ public void setBBox(PDRectangle bbox) { if (bbox == null) { getCOSObject().removeItem(COSName.BBOX); } else { getCOSObject().setItem(COSName.BBOX, bbox.getCOSObject()); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/000077500000000000000000000000001320103431700255315ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/AxialShadingContext.java000066400000000000000000000231111320103431700322730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.pdmodel.common.function.PDFunction; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT PaintContext for axial shading. * * Performance improvement done as part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ public class AxialShadingContext extends ShadingContext implements PaintContext { private static final Logger LOG = LoggerFactory.getLogger(AxialShadingContext.class); private PDShadingType2 axialShadingType; private final float[] coords; private final float[] domain; private final boolean[] extend; private final double x1x0; private final double y1y0; private final float d1d0; private final double denom; private final int factor; private final int[] colorTable; private AffineTransform rat; /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param colorModel the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @param deviceBounds the bounds of the area to paint, in device units * @throws IOException if there is an error getting the color space or doing color conversion. */ public AxialShadingContext(PDShadingType2 shading, ColorModel colorModel, AffineTransform xform, Matrix matrix, Rectangle deviceBounds) throws IOException { super(shading, colorModel, xform, matrix); this.axialShadingType = shading; coords = shading.getCoords().toFloatArray(); // domain values if (shading.getDomain() != null) { domain = shading.getDomain().toFloatArray(); } else { // set default values domain = new float[] { 0, 1 }; } // extend values COSArray extendValues = shading.getExtend(); if (extendValues != null) { extend = new boolean[2]; extend[0] = ((COSBoolean) extendValues.getObject(0)).getValue(); extend[1] = ((COSBoolean) extendValues.getObject(1)).getValue(); } else { // set default values extend = new boolean[] { false, false }; } // calculate some constants to be used in getRaster x1x0 = coords[2] - coords[0]; y1y0 = coords[3] - coords[1]; d1d0 = domain[1] - domain[0]; denom = Math.pow(x1x0, 2) + Math.pow(y1y0, 2); try { // get inverse transform to be independent of current user / device space // when handling actual pixels in getRaster() rat = matrix.createAffineTransform().createInverse(); rat.concatenate(xform.createInverse()); } catch (NoninvertibleTransformException ex) { LOG.error(ex.getMessage() + ", matrix: " + matrix, ex); LOG.error(ex.getMessage(), ex); } // shading space -> device space AffineTransform shadingToDevice = (AffineTransform) xform.clone(); shadingToDevice.concatenate(matrix.createAffineTransform()); // worst case for the number of steps is opposite diagonal corners, so use that double dist = Math.sqrt(Math.pow(deviceBounds.getMaxX() - deviceBounds.getMinX(), 2) + Math.pow(deviceBounds.getMaxY() - deviceBounds.getMinY(), 2)); factor = (int) Math.ceil(dist); // build the color table for the given number of steps colorTable = calcColorTable(); } /** * Calculate the color on the axial line and store them in an array. * * @return an array, index denotes the relative position, the corresponding value is the color on the axial line * @throws IOException if the color conversion fails. */ private int[] calcColorTable() throws IOException { int[] map = new int[factor + 1]; if (factor == 0 || d1d0 == 0) { float[] values = axialShadingType.evalFunction(domain[0]); map[0] = convertToRGB(values); } else { for (int i = 0; i <= factor; i++) { float t = domain[0] + d1d0 * i / factor; float[] values = axialShadingType.evalFunction(t); map[i] = convertToRGB(values); } } return map; } @Override public void dispose() { super.dispose(); axialShadingType = null; } @Override public ColorModel getColorModel() { return super.getColorModel(); } @Override public Raster getRaster(int x, int y, int w, int h) { // create writable raster WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h); boolean useBackground; int[] data = new int[w * h * 4]; for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { useBackground = false; float[] values = new float[] { x + i, y + j }; rat.transform(values, 0, values, 0, 1); double inputValue = x1x0 * (values[0] - coords[0]) + y1y0 * (values[1] - coords[1]); // TODO this happens if start == end, see PDFBOX-1442 if (denom == 0) { if (getBackground() == null) { continue; } useBackground = true; } else { inputValue /= denom; } // input value is out of range if (inputValue < 0) { // the shading has to be extended if extend[0] == true if (extend[0]) { inputValue = 0; } else { if (getBackground() == null) { continue; } useBackground = true; } } // input value is out of range else if (inputValue > 1) { // the shading has to be extended if extend[1] == true if (extend[1]) { inputValue = 1; } else { if (getBackground() == null) { continue; } useBackground = true; } } int value; if (useBackground) { // use the given backgound color values value = getRgbBackground(); } else { int key = (int) (inputValue * factor); value = colorTable[key]; } int index = (j * w + i) * 4; data[index] = value & 255; value >>= 8; data[index + 1] = value & 255; value >>= 8; data[index + 2] = value & 255; data[index + 3] = 255; } } raster.setPixels(0, 0, w, h, data); return raster; } /** * Returns the coords values. */ public float[] getCoords() { return coords; } /** * Returns the domain values. */ public float[] getDomain() { return domain; } /** * Returns the extend values. */ public boolean[] getExtend() { return extend; } /** * Returns the function. * * @throws java.io.IOException if we were not able to create the function. */ public PDFunction getFunction() throws IOException { return axialShadingType.getFunction(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/AxialShadingPaint.java000066400000000000000000000047031320103431700317300ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Color; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT Paint for axial shading. * */ public class AxialShadingPaint implements Paint { private static final Logger LOG = LoggerFactory.getLogger(AxialShadingPaint.class); private final PDShadingType2 shading; private final Matrix matrix; /** * Constructor. * * @param shadingType2 the shading resources * @param matrix the pattern matrix concatenated with that of the parent content stream */ AxialShadingPaint(PDShadingType2 shadingType2, Matrix matrix) { shading = shadingType2; this.matrix = matrix; } @Override public int getTransparency() { return 0; } @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { try { return new AxialShadingContext(shading, cm, xform, matrix, deviceBounds); } catch (IOException e) { LOG.error("An error occurred while painting", e); return new Color(0, 0, 0, 0).createContext(cm, deviceBounds, userBounds, xform, hints); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/CoonsPatch.java000066400000000000000000000171571320103431700304500ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.geom.Point2D; import java.util.List; /** * This class is used to describe a patch for type 6 shading. This was done as * part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class CoonsPatch extends Patch { /** * Constructor of a patch for type 6 shading. * * @param points 12 control points * @param color 4 corner colors */ protected CoonsPatch(Point2D[] points, float[][] color) { super(points, color); controlPoints = reshapeControlPoints(points); level = calcLevel(); listOfTriangles = getTriangles(); } // adjust the 12 control points to 4 groups, each group defines one edge of a patch private Point2D[][] reshapeControlPoints(Point2D[] points) { Point2D[][] fourRows = new Point2D[4][4]; fourRows[2] = new Point2D[] { points[0], points[1], points[2], points[3] }; // d1 fourRows[1] = new Point2D[] { points[3], points[4], points[5], points[6] }; // c2 fourRows[3] = new Point2D[] { points[9], points[8], points[7], points[6] }; // d2 fourRows[0] = new Point2D[] { points[0], points[11], points[10], points[9] }; // c1 return fourRows; } // calculate the dividing level from control points private int[] calcLevel() { int[] l = { 4, 4 }; // if two opposite edges are both lines, there is a possibility to reduce the dividing level if (isEdgeALine(controlPoints[0]) && isEdgeALine(controlPoints[1])) { double lc1 = getLen(controlPoints[0][0], controlPoints[0][3]), lc2 = getLen(controlPoints[1][0], controlPoints[1][3]); // determine the dividing level by the lengths of edges if (lc1 > 800 || lc2 > 800) { // keeps init value 4 } else if (lc1 > 400 || lc2 > 400) { l[0] = 3; } else if (lc1 > 200 || lc2 > 200) { l[0] = 2; } else { l[0] = 1; } } // the other two opposite edges if (isEdgeALine(controlPoints[2]) && isEdgeALine(controlPoints[3])) { double ld1 = getLen(controlPoints[2][0], controlPoints[2][3]), ld2 = getLen(controlPoints[3][0], controlPoints[3][3]); if (ld1 > 800 || ld2 > 800) { // keeps init value 4 } else if (ld1 > 400 || ld2 > 400) { l[1] = 3; } else if (ld1 > 200 || ld2 > 200) { l[1] = 2; } else { l[1] = 1; } } return l; } // get a list of triangles which compose this coons patch private List getTriangles() { // 4 edges are 4 cubic Bezier curves CubicBezierCurve eC1 = new CubicBezierCurve(controlPoints[0], level[0]); CubicBezierCurve eC2 = new CubicBezierCurve(controlPoints[1], level[0]); CubicBezierCurve eD1 = new CubicBezierCurve(controlPoints[2], level[1]); CubicBezierCurve eD2 = new CubicBezierCurve(controlPoints[3], level[1]); CoordinateColorPair[][] patchCC = getPatchCoordinatesColor(eC1, eC2, eD1, eD2); return getShadedTriangles(patchCC); } @Override protected Point2D[] getFlag1Edge() { return controlPoints[1].clone(); } @Override protected Point2D[] getFlag2Edge() { Point2D[] implicitEdge = new Point2D[4]; implicitEdge[0] = controlPoints[3][3]; implicitEdge[1] = controlPoints[3][2]; implicitEdge[2] = controlPoints[3][1]; implicitEdge[3] = controlPoints[3][0]; return implicitEdge; } @Override protected Point2D[] getFlag3Edge() { Point2D[] implicitEdge = new Point2D[4]; implicitEdge[0] = controlPoints[0][3]; implicitEdge[1] = controlPoints[0][2]; implicitEdge[2] = controlPoints[0][1]; implicitEdge[3] = controlPoints[0][0]; return implicitEdge; } /* dividing a patch into a grid, return a matrix of the coordinate and color at the crossing points of the grid, the rule to calculate the coordinate is defined in page 195 of PDF32000_2008.pdf, the rule to calculate the cooresponding color is bilinear interpolation */ private CoordinateColorPair[][] getPatchCoordinatesColor(CubicBezierCurve c1, CubicBezierCurve c2, CubicBezierCurve d1, CubicBezierCurve d2) { Point2D[] curveC1 = c1.getCubicBezierCurve(); Point2D[] curveC2 = c2.getCubicBezierCurve(); Point2D[] curveD1 = d1.getCubicBezierCurve(); Point2D[] curveD2 = d2.getCubicBezierCurve(); int numberOfColorComponents = cornerColor[0].length; int szV = curveD1.length; int szU = curveC1.length; CoordinateColorPair[][] patchCC = new CoordinateColorPair[szV][szU]; double stepV = (double) 1 / (szV - 1); double stepU = (double) 1 / (szU - 1); double v = -stepV; for (int i = 0; i < szV; i++) { // v and u are the assistant parameters v += stepV; double u = -stepU; for (int j = 0; j < szU; j++) { u += stepU; double scx = (1 - v) * curveC1[j].getX() + v * curveC2[j].getX(); double scy = (1 - v) * curveC1[j].getY() + v * curveC2[j].getY(); double sdx = (1 - u) * curveD1[i].getX() + u * curveD2[i].getX(); double sdy = (1 - u) * curveD1[i].getY() + u * curveD2[i].getY(); double sbx = (1 - v) * ((1 - u) * controlPoints[0][0].getX() + u * controlPoints[0][3].getX()) + v * ((1 - u) * controlPoints[1][0].getX() + u * controlPoints[1][3].getX()); double sby = (1 - v) * ((1 - u) * controlPoints[0][0].getY() + u * controlPoints[0][3].getY()) + v * ((1 - u) * controlPoints[1][0].getY() + u * controlPoints[1][3].getY()); double sx = scx + sdx - sbx; double sy = scy + sdy - sby; // the above code in this for loop defines the patch surface (coordinates) Point2D tmpC = new Point2D.Double(sx, sy); float[] paramSC = new float[numberOfColorComponents]; for (int ci = 0; ci < numberOfColorComponents; ci++) { paramSC[ci] = (float) ((1 - v) * ((1 - u) * cornerColor[0][ci] + u * cornerColor[3][ci]) + v * ((1 - u) * cornerColor[1][ci] + u * cornerColor[2][ci])); // bilinear interpolation } patchCC[i][j] = new CoordinateColorPair(tmpC, paramSC); } } return patchCC; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/CoordinateColorPair.java000066400000000000000000000022141320103431700322750ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.geom.Point2D; /** * This class is used to store a point's coordinate and its corresponding color. * This was done as part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class CoordinateColorPair { final Point2D coordinate; final float[] color; /** * Constructor. * * @param p point * @param c color */ CoordinateColorPair(Point2D p, float[] c) { coordinate = p; color = c.clone(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/CubicBezierCurve.java000066400000000000000000000061721320103431700315750ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.geom.Point2D; /** * This class is used to describe the edge of each patch for type 6 shading. * This was done as part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class CubicBezierCurve { private final Point2D[] controlPoints; private final int level; private final Point2D[] curve; /** * Constructor of CubicBezierCurve * * @param ctrlPnts, 4 control points [p0, p1, p2, p3] * @param l, dividing level, if l = 0, one cubic Bezier curve is divided * into 2^0 = 1 segments, if l = n, one cubic Bezier curve is divided into * 2^n segments */ CubicBezierCurve(Point2D[] ctrlPnts, int l) { controlPoints = ctrlPnts.clone(); level = l; curve = getPoints(level); } /** * Get level parameter * * @return level */ int getLevel() { return level; } // calculate sampled points on the cubic Bezier curve defined by the 4 given control points private Point2D[] getPoints(int l) { if (l < 0) { l = 0; } int sz = (1 << l) + 1; Point2D[] res = new Point2D[sz]; double step = (double) 1 / (sz - 1); double t = -step; for (int i = 0; i < sz; i++) { t += step; double tmpX = (1 - t) * (1 - t) * (1 - t) * controlPoints[0].getX() + 3 * t * (1 - t) * (1 - t) * controlPoints[1].getX() + 3 * t * t * (1 - t) * controlPoints[2].getX() + t * t * t * controlPoints[3].getX(); double tmpY = (1 - t) * (1 - t) * (1 - t) * controlPoints[0].getY() + 3 * t * (1 - t) * (1 - t) * controlPoints[1].getY() + 3 * t * t * (1 - t) * controlPoints[2].getY() + t * t * t * controlPoints[3].getY(); res[i] = new Point2D.Double(tmpX, tmpY); } return res; } /** * Get sampled points of this cubic Bezier curve. * * @return sampled points */ Point2D[] getCubicBezierCurve() { return curve; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Point2D p : controlPoints) { if (sb.length() > 0) { sb.append(' '); } sb.append(p); } return "Cubic Bezier curve{control points p0, p1, p2, p3: " + sb + "}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/GouraudShadingContext.java000066400000000000000000000122571320103431700326540ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.stream.ImageInputStream; import org.sejda.sambox.pdmodel.common.PDRange; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Shades Gouraud triangles for Type4ShadingContext and Type5ShadingContext. * * @author Tilman Hausherr * @author Shaola Ren */ abstract class GouraudShadingContext extends TriangleBasedShadingContext { private static final Logger LOG = LoggerFactory.getLogger(GouraudShadingContext.class); /** * triangle list. */ private List triangleList = new ArrayList(); /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param colorModel the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @throws IOException if something went wrong */ protected GouraudShadingContext(PDShading shading, ColorModel colorModel, AffineTransform xform, Matrix matrix) throws IOException { super(shading, colorModel, xform, matrix); } /** * Read a vertex from the bit input stream performs interpolations. * * @param input bit input stream * @param maxSrcCoord max value for source coordinate (2^bits-1) * @param maxSrcColor max value for source color (2^bits-1) * @param rangeX dest range for X * @param rangeY dest range for Y * @param colRangeTab dest range array for colors * @param matrix the pattern matrix concatenated with that of the parent content stream * @return a new vertex with the flag and the interpolated values * @throws IOException if something went wrong */ protected Vertex readVertex(ImageInputStream input, long maxSrcCoord, long maxSrcColor, PDRange rangeX, PDRange rangeY, PDRange[] colRangeTab, Matrix matrix, AffineTransform xform) throws IOException { float[] colorComponentTab = new float[numberOfColorComponents]; long x = input.readBits(bitsPerCoordinate); long y = input.readBits(bitsPerCoordinate); float dstX = interpolate(x, maxSrcCoord, rangeX.getMin(), rangeX.getMax()); float dstY = interpolate(y, maxSrcCoord, rangeY.getMin(), rangeY.getMax()); LOG.debug("coord: " + String.format("[%06X,%06X] -> [%f,%f]", x, y, dstX, dstY)); Point2D p = matrix.transformPoint(dstX, dstY); xform.transform(p, p); for (int n = 0; n < numberOfColorComponents; ++n) { int color = (int) input.readBits(bitsPerColorComponent); colorComponentTab[n] = interpolate(color, maxSrcColor, colRangeTab[n].getMin(), colRangeTab[n].getMax()); LOG.debug("color[" + n + "]: " + color + "/" + String.format("%02x", color) + "-> color[" + n + "]: " + colorComponentTab[n]); } return new Vertex(p, colorComponentTab); } void setTriangleList(List triangleList) { this.triangleList = triangleList; } @Override protected Map calcPixelTable(Rectangle deviceBounds) throws IOException { Map map = new HashMap(); super.calcPixelTable(triangleList, map, deviceBounds); return map; } @Override public void dispose() { triangleList = null; super.dispose(); } /** * Calculate the interpolation, see p.345 pdf spec 1.7. * * @param src src value * @param srcMax max src value (2^bits-1) * @param dstMin min dst value * @param dstMax max dst value * @return interpolated value */ private float interpolate(float src, long srcMax, float dstMin, float dstMax) { return dstMin + (src * (dstMax - dstMin) / srcMax); } @Override protected boolean isDataEmpty() { return triangleList.isEmpty(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Line.java000066400000000000000000000073161320103431700272720ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Point; import java.util.HashSet; import java.util.Set; /** * This class describes a rasterized line. This was done as part of GSoC2014, * Tilman Hausherr is the mentor. * * @author Shaola Ren */ class Line { private final Point point0; private final Point point1; private final float[] color0; private final float[] color1; protected final Set linePoints; // all the points in this rasterized line /** * Constructor of class Line. * * @param p0 one end of a line * @param p1 the other end of the line * @param c0 color of point p0 * @param c1 color of point p1 */ Line(Point p0, Point p1, float[] c0, float[] c1) { point0 = p0; point1 = p1; color0 = c0.clone(); color1 = c1.clone(); linePoints = calcLine(point0.x, point0.y, point1.x, point1.y); } /** * Calculate the points of a line with Bresenham's line algorithm * Bresenham's * line algorithm * * @param x0 coordinate * @param y0 coordinate * @param x1 coordinate * @param y1 coordinate * @return all the points on the rasterized line from (x0, y0) to (x1, y1) */ private Set calcLine(int x0, int y0, int x1, int y1) { Set points = new HashSet(3); int dx = Math.abs(x1 - x0); int dy = Math.abs(y1 - y0); int sx = x0 < x1 ? 1 : -1; int sy = y0 < y1 ? 1 : -1; int err = dx - dy; while (true) { points.add(new Point(x0, y0)); if (x0 == x1 && y0 == y1) { break; } int e2 = 2 * err; if (e2 > -dy) { err -= dy; x0 += sx; } if (e2 < dx) { err += dx; y0 += sy; } } return points; } /** * Calculate the color of a point on a rasterized line by linear * interpolation. * * @param p target point, p should always be contained in linePoints * @return color */ protected float[] calcColor(Point p) { int numberOfColorComponents = color0.length; float[] pc = new float[numberOfColorComponents]; if (point0.x == point1.x && point0.y == point1.y) { return color0; } else if (point0.x == point1.x) { float l = point1.y - point0.y; for (int i = 0; i < numberOfColorComponents; i++) { pc[i] = (color0[i] * (point1.y - p.y) / l + color1[i] * (p.y - point0.y) / l); } } else { float l = point1.x - point0.x; for (int i = 0; i < numberOfColorComponents; i++) { pc[i] = (color0[i] * (point1.x - p.x) / l + color1[i] * (p.x - point0.x) / l); } } return pc; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PDShading.java000066400000000000000000000305301320103431700301760ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Paint; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.function.PDFunction; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.util.Matrix; /** * A Shading Resource. * */ public abstract class PDShading implements COSObjectable { private final COSDictionary dictionary; private COSArray background = null; private PDRectangle bBox = null; private PDColorSpace colorSpace = null; private PDFunction function = null; private PDFunction[] functionArray = null; /** * shading type 1 = function based shading. */ public static final int SHADING_TYPE1 = 1; /** * shading type 2 = axial shading. */ public static final int SHADING_TYPE2 = 2; /** * shading type 3 = radial shading. */ public static final int SHADING_TYPE3 = 3; /** * shading type 4 = Free-Form Gouraud-Shaded Triangle Meshes. */ public static final int SHADING_TYPE4 = 4; /** * shading type 5 = Lattice-Form Gouraud-Shaded Triangle Meshes. */ public static final int SHADING_TYPE5 = 5; /** * shading type 6 = Coons Patch Meshes. */ public static final int SHADING_TYPE6 = 6; /** * shading type 7 = Tensor-Product Patch Meshes. */ public static final int SHADING_TYPE7 = 7; /** * Default constructor. */ public PDShading() { dictionary = new COSDictionary(); } /** * Constructor using the given shading dictionary. * * @param shadingDictionary the dictionary for this shading */ public PDShading(COSDictionary shadingDictionary) { dictionary = shadingDictionary; } /** * This will get the underlying dictionary. * * @return the dictionary for this shading. */ @Override public COSDictionary getCOSObject() { return dictionary; } /** * This will return the type. * * @return the type of object that this is */ public String getType() { return COSName.SHADING.getName(); } /** * This will set the shading type. * * @param shadingType the new shading type */ public void setShadingType(int shadingType) { dictionary.setInt(COSName.SHADING_TYPE, shadingType); } /** * This will return the shading type. * * @return the shading typ */ public abstract int getShadingType(); /** * This will set the background. * * @param newBackground the new background */ public void setBackground(COSArray newBackground) { background = newBackground; dictionary.setItem(COSName.BACKGROUND, newBackground); } /** * This will return the background. * * @return the background */ public COSArray getBackground() { if (background == null) { background = (COSArray) dictionary.getDictionaryObject(COSName.BACKGROUND); } return background; } /** * An array of four numbers in the form coordinate system (see below), giving the coordinates of the left, bottom, * right, and top edges, respectively, of the shading's bounding box. * * @return the BBox of the form */ public PDRectangle getBBox() { if (bBox == null) { COSArray array = (COSArray) dictionary.getDictionaryObject(COSName.BBOX); if (array != null) { bBox = new PDRectangle(array); } } return bBox; } /** * This will set the BBox (bounding box) for this Shading. * * @param newBBox the new BBox */ public void setBBox(PDRectangle newBBox) { bBox = newBBox; if (bBox == null) { dictionary.removeItem(COSName.BBOX); } else { dictionary.setItem(COSName.BBOX, bBox.getCOSObject()); } } /** * This will set the AntiAlias value. * * @param antiAlias the new AntiAlias value */ public void setAntiAlias(boolean antiAlias) { dictionary.setBoolean(COSName.ANTI_ALIAS, antiAlias); } /** * This will return the AntiAlias value. * * @return the AntiAlias value */ public boolean getAntiAlias() { return dictionary.getBoolean(COSName.ANTI_ALIAS, false); } /** * This will get the color space or null if none exists. * * @return the color space for the shading * @throws IOException if there is an error getting the color space */ public PDColorSpace getColorSpace() throws IOException { if (colorSpace == null) { COSBase colorSpaceDictionary = dictionary.getDictionaryObject(COSName.CS, COSName.COLORSPACE); colorSpace = PDColorSpace.create(colorSpaceDictionary); } return colorSpace; } /** * This will set the color space for the shading. * * @param colorSpace the color space */ public void setColorSpace(PDColorSpace colorSpace) { this.colorSpace = colorSpace; if (colorSpace != null) { dictionary.setItem(COSName.COLORSPACE, colorSpace.getCOSObject()); } else { dictionary.removeItem(COSName.COLORSPACE); } } /** * Create the correct PD Model shading based on the COS base shading. * * @param resourceDictionary the COS shading dictionary * @return the newly created shading resources object * @throws IOException if we are unable to create the PDShading object */ public static PDShading create(COSDictionary resourceDictionary) throws IOException { PDShading shading = null; int shadingType = resourceDictionary.getInt(COSName.SHADING_TYPE, 0); switch (shadingType) { case SHADING_TYPE1: shading = new PDShadingType1(resourceDictionary); break; case SHADING_TYPE2: shading = new PDShadingType2(resourceDictionary); break; case SHADING_TYPE3: shading = new PDShadingType3(resourceDictionary); break; case SHADING_TYPE4: shading = new PDShadingType4(resourceDictionary); break; case SHADING_TYPE5: shading = new PDShadingType5(resourceDictionary); break; case SHADING_TYPE6: shading = new PDShadingType6(resourceDictionary); break; case SHADING_TYPE7: shading = new PDShadingType7(resourceDictionary); break; default: throw new IOException("Error: Unknown shading type " + shadingType); } return shading; } /** * This will set the function for the color conversion. * * @param newFunction the new function */ public void setFunction(PDFunction newFunction) { functionArray = null; function = newFunction; getCOSObject().setItem(COSName.FUNCTION, newFunction); } /** * This will set the functions COSArray for the color conversion. * * @param newFunctions the new COSArray containing all functions */ public void setFunction(COSArray newFunctions) { functionArray = null; function = null; getCOSObject().setItem(COSName.FUNCTION, newFunctions); } /** * This will return the function used to convert the color values. * * @return the function * @throws java.io.IOException if we were not able to create the function. */ public PDFunction getFunction() throws IOException { if (function == null) { COSBase dictionaryFunctionObject = getCOSObject().getDictionaryObject(COSName.FUNCTION); if (dictionaryFunctionObject != null) { function = PDFunction.create(dictionaryFunctionObject); } } return function; } /** * Provide the function(s) of the shading dictionary as array. * * @return an array containing the function(s) * @throws IOException if something went wrong */ private PDFunction[] getFunctionsArray() throws IOException { if (functionArray == null) { COSBase functionObject = getCOSObject().getDictionaryObject(COSName.FUNCTION); if (functionObject instanceof COSDictionary) { functionArray = new PDFunction[1]; functionArray[0] = PDFunction.create(functionObject); } else if (functionObject instanceof COSArray) { COSArray functionCOSArray = (COSArray) functionObject; int numberOfFunctions = functionCOSArray.size(); functionArray = new PDFunction[numberOfFunctions]; for (int i = 0; i < numberOfFunctions; i++) { functionArray[i] = PDFunction.create(functionCOSArray.get(i)); } } else { throw new IOException( "mandatory /Function element must be a dictionary or an array"); } } return functionArray; } /** * Convert the input value using the functions of the shading dictionary. * * @param inputValue the input value * @return the output values * @throws IOException thrown if something went wrong */ public float[] evalFunction(float inputValue) throws IOException { return evalFunction(new float[] { inputValue }); } /** * Convert the input values using the functions of the shading dictionary. * * @param input the input values * @return the output values * @throws IOException thrown if something went wrong */ public float[] evalFunction(float[] input) throws IOException { PDFunction[] functions = getFunctionsArray(); int numberOfFunctions = functions.length; float[] returnValues; if (numberOfFunctions == 1) { returnValues = functions[0].eval(input); } else { returnValues = new float[numberOfFunctions]; for (int i = 0; i < numberOfFunctions; i++) { float[] newValue = functions[i].eval(input); returnValues[i] = newValue[0]; } } // From the PDF spec: // "If the value returned by the function for a given colour component // is out of range, it shall be adjusted to the nearest valid value." for (int i = 0; i < returnValues.length; ++i) { if (returnValues[i] < 0) { returnValues[i] = 0; } else if (returnValues[i] > 1) { returnValues[i] = 1; } } return returnValues; } /** * Returns an AWT paint which corresponds to this shading * * @param matrix the pattern matrix concatenated with that of the parent content stream, this matrix which maps the * pattern's internal coordinate system to user space * @return an AWT Paint instance */ public abstract Paint toPaint(Matrix matrix); } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PDShadingType1.java000066400000000000000000000062741320103431700311310ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Paint; import java.awt.geom.AffineTransform; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.util.Matrix; /** * Resources for a function based shading. */ public class PDShadingType1 extends PDShading { private COSArray domain = null; /** * Constructor using the given shading dictionary. * * @param shadingDictionary the dictionary for this shading */ public PDShadingType1(COSDictionary shadingDictionary) { super(shadingDictionary); } @Override public int getShadingType() { return PDShading.SHADING_TYPE1; } /** * This will get the optional Matrix of a function based shading. * * @return the matrix */ public Matrix getMatrix() { COSArray array = (COSArray) getCOSObject().getDictionaryObject(COSName.MATRIX); if (array != null) { return new Matrix(array); } else { // identity matrix is the default return new Matrix(); } } /** * Sets the optional Matrix entry for the function based shading. * * @param transform the transformation matrix */ public void setMatrix(AffineTransform transform) { COSArray matrix = new COSArray(); double[] values = new double[6]; transform.getMatrix(values); for (double v : values) { matrix.add(new COSFloat((float) v)); } getCOSObject().setItem(COSName.MATRIX, matrix); } /** * This will get the optional Domain values of a function based shading. * * @return the domain values */ public COSArray getDomain() { if (domain == null) { domain = (COSArray) getCOSObject().getDictionaryObject(COSName.DOMAIN); } return domain; } /** * Sets the optional Domain entry for the function based shading. * * @param newDomain the domain array */ public void setDomain(COSArray newDomain) { domain = newDomain; getCOSObject().setItem(COSName.DOMAIN, newDomain); } @Override public Paint toPaint(Matrix matrix) { return new Type1ShadingPaint(this, matrix); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PDShadingType2.java000066400000000000000000000066011320103431700311240ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Paint; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.util.Matrix; /** * Resources for an axial shading. */ public class PDShadingType2 extends PDShading { private COSArray coords = null; private COSArray domain = null; private COSArray extend = null; /** * Constructor using the given shading dictionary. * * @param shadingDictionary the dictionary for this shading */ public PDShadingType2(COSDictionary shadingDictionary) { super(shadingDictionary); } @Override public int getShadingType() { return PDShading.SHADING_TYPE2; } /** * This will get the optional Extend values for this shading. * * @return the extend values */ public COSArray getExtend() { if (extend == null) { extend = (COSArray) getCOSObject().getDictionaryObject(COSName.EXTEND); } return extend; } /** * Sets the optional Extend entry for this shading. * * @param newExtend the extend array */ public void setExtend(COSArray newExtend) { extend = newExtend; getCOSObject().setItem(COSName.EXTEND, newExtend); } /** * This will get the optional Domain values for this shading. * * @return the domain values */ public COSArray getDomain() { if (domain == null) { domain = (COSArray) getCOSObject().getDictionaryObject(COSName.DOMAIN); } return domain; } /** * Sets the optional Domain entry for this shading. * * @param newDomain the domain array */ public void setDomain(COSArray newDomain) { domain = newDomain; getCOSObject().setItem(COSName.DOMAIN, newDomain); } /** * This will get the Coords values for this shading. * * @return the coordinate values */ public COSArray getCoords() { if (coords == null) { coords = (COSArray) getCOSObject().getDictionaryObject(COSName.COORDS); } return coords; } /** * Sets the Coords entry for this shading. * * @param newCoords the coordinates array */ public void setCoords(COSArray newCoords) { coords = newCoords; getCOSObject().setItem(COSName.COORDS, newCoords); } @Override public Paint toPaint(Matrix matrix) { return new AxialShadingPaint(this, matrix); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PDShadingType3.java000066400000000000000000000027641320103431700311330ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Paint; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.util.Matrix; /** * Resources for a radial shading. */ public class PDShadingType3 extends PDShadingType2 { /** * Constructor using the given shading dictionary. * * @param shadingDictionary the dictionary for this shading */ public PDShadingType3(COSDictionary shadingDictionary) { super(shadingDictionary); } @Override public int getShadingType() { return PDShading.SHADING_TYPE3; } @Override public Paint toPaint(Matrix matrix) { return new RadialShadingPaint(this, matrix); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PDShadingType4.java000066400000000000000000000042311320103431700311230ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Paint; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.util.Matrix; /** * Resources for a shading type 4 (Free-Form Gouraud-Shaded Triangle Mesh). */ public class PDShadingType4 extends PDTriangleBasedShadingType { /** * Constructor using the given shading dictionary. * * @param shadingDictionary the dictionary for this shading */ public PDShadingType4(COSDictionary shadingDictionary) { super(shadingDictionary); } @Override public int getShadingType() { return PDShading.SHADING_TYPE4; } /** * The bits per flag of this shading. This will return -1 if one has not * been set. * * @return The number of bits per flag. */ public int getBitsPerFlag() { return getCOSObject().getInt(COSName.BITS_PER_FLAG, -1); } /** * Set the number of bits per flag. * * @param bitsPerFlag the number of bits per flag */ public void setBitsPerFlag(int bitsPerFlag) { getCOSObject().setInt(COSName.BITS_PER_FLAG, bitsPerFlag); } @Override public Paint toPaint(Matrix matrix) { return new Type4ShadingPaint(this, matrix); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PDShadingType5.java000066400000000000000000000042731320103431700311320ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Paint; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.util.Matrix; /** * Resources for a shading type 5 (Lattice-Form Gouraud-Shade Triangle Mesh). */ public class PDShadingType5 extends PDTriangleBasedShadingType { /** * Constructor using the given shading dictionary. * * @param shadingDictionary the dictionary for this shading */ public PDShadingType5(COSDictionary shadingDictionary) { super(shadingDictionary); } @Override public int getShadingType() { return PDShading.SHADING_TYPE5; } /** * The vertices per row of this shading. This will return -1 if one has not * been set. * * @return the number of vertices per row */ public int getVerticesPerRow() { return getCOSObject().getInt(COSName.VERTICES_PER_ROW, -1); } /** * Set the number of vertices per row. * * @param verticesPerRow the number of vertices per row */ public void setVerticesPerRow(int verticesPerRow) { getCOSObject().setInt(COSName.VERTICES_PER_ROW, verticesPerRow); } @Override public Paint toPaint(Matrix matrix) { return new Type5ShadingPaint(this, matrix); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PDShadingType6.java000066400000000000000000000030701320103431700311250ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Paint; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.util.Matrix; /** * Resources for a shading type 6 (Coons Patch Mesh). */ public class PDShadingType6 extends PDShadingType4 { /** * Constructor using the given shading dictionary. * * @param shadingDictionary the dictionary for this shading */ public PDShadingType6(COSDictionary shadingDictionary) { super(shadingDictionary); } @Override public int getShadingType() { return PDShading.SHADING_TYPE6; } @Override public Paint toPaint(Matrix matrix) { return new Type6ShadingPaint(this, matrix); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PDShadingType7.java000066400000000000000000000031011320103431700311210ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Paint; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.util.Matrix; /** * Resources for a shading type 7 (Tensor-Product Patch Mesh). */ public class PDShadingType7 extends PDShadingType6 { /** * Constructor using the given shading dictionary. * * @param shadingDictionary the dictionary for this shading */ public PDShadingType7(COSDictionary shadingDictionary) { super(shadingDictionary); } @Override public int getShadingType() { return PDShading.SHADING_TYPE7; } @Override public Paint toPaint(Matrix matrix) { return new Type7ShadingPaint(this, matrix); } } PDTriangleBasedShadingType.java000066400000000000000000000067311320103431700334140ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDRange; /** * Common resources for shading types 4,5,6 and 7 */ abstract class PDTriangleBasedShadingType extends PDShading { // an array of 2^n numbers specifying the linear mapping of sample values // into the range appropriate for the function's output values. Default // value: same as the value of Range private COSArray decode = null; PDTriangleBasedShadingType(COSDictionary shadingDictionary) { super(shadingDictionary); } /** * The bits per component of this shading. This will return -1 if one has * not been set. * * @return the number of bits per component */ public int getBitsPerComponent() { return getCOSObject().getInt(COSName.BITS_PER_COMPONENT, -1); } /** * Set the number of bits per component. * * @param bitsPerComponent the number of bits per component */ public void setBitsPerComponent(int bitsPerComponent) { getCOSObject().setInt(COSName.BITS_PER_COMPONENT, bitsPerComponent); } /** * The bits per coordinate of this shading. This will return -1 if one has * not been set. * * @return the number of bits per coordinate */ public int getBitsPerCoordinate() { return getCOSObject().getInt(COSName.BITS_PER_COORDINATE, -1); } /** * Set the number of bits per coordinate. * * @param bitsPerComponent the number of bits per coordinate */ public void setBitsPerCoordinate(int bitsPerComponent) { getCOSObject().setInt(COSName.BITS_PER_COORDINATE, bitsPerComponent); } /** * Returns all decode values as COSArray. * * @return the decode array */ private COSArray getDecodeValues() { if (decode == null) { decode = (COSArray) getCOSObject().getDictionaryObject(COSName.DECODE); } return decode; } /** * This will set the decode values. * * @param decodeValues the new decode values */ public void setDecodeValues(COSArray decodeValues) { decode = decodeValues; getCOSObject().setItem(COSName.DECODE, decodeValues); } /** * Get the decode for the input parameter. * * @param paramNum the function parameter number * @return the decode parameter range or null if none is set */ public PDRange getDecodeForParameter(int paramNum) { PDRange retval = null; COSArray decodeValues = getDecodeValues(); if (decodeValues != null && decodeValues.size() >= paramNum * 2 + 1) { retval = new PDRange(decodeValues, paramNum); } return retval; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Patch.java000066400000000000000000000171201320103431700274340ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; /** * Patch is extended by CoonsPatch and TensorPatch. This was done as part of * GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ abstract class Patch { protected Point2D[][] controlPoints; protected float[][] cornerColor; /* level = {levelU, levelV}, levelU defines the patch's u direction edges should be divided into 2^levelU parts, level V defines the patch's v direction edges should be divided into 2^levelV parts */ protected int[] level; protected List listOfTriangles; /** * Constructor of Patch. * * @param ctl control points, size is 12 (for type 6 shading) or 16 (for * type 7 shading) * @param color 4 corner's colors */ Patch(Point2D[] ctl, float[][] color) { cornerColor = color.clone(); } /** * Get the implicit edge for flag = 1. * * @return implicit control points */ protected abstract Point2D[] getFlag1Edge(); /** * Get the implicit edge for flag = 2. * * @return implicit control points */ protected abstract Point2D[] getFlag2Edge(); /** * Get the implicit edge for flag = 3. * * @return implicit control points */ protected abstract Point2D[] getFlag3Edge(); /** * Get the implicit color for flag = 1. * * @return color */ protected float[][] getFlag1Color() { int numberOfColorComponents = cornerColor[0].length; float[][] implicitCornerColor = new float[2][numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; i++) { implicitCornerColor[0][i] = cornerColor[1][i]; implicitCornerColor[1][i] = cornerColor[2][i]; } return implicitCornerColor; } /** * Get implicit color for flag = 2. * * @return color */ protected float[][] getFlag2Color() { int numberOfColorComponents = cornerColor[0].length; float[][] implicitCornerColor = new float[2][numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; i++) { implicitCornerColor[0][i] = cornerColor[2][i]; implicitCornerColor[1][i] = cornerColor[3][i]; } return implicitCornerColor; } /** * Get implicit color for flag = 3. * * @return color */ protected float[][] getFlag3Color() { int numberOfColorComponents = cornerColor[0].length; float[][] implicitCornerColor = new float[2][numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; i++) { implicitCornerColor[0][i] = cornerColor[3][i]; implicitCornerColor[1][i] = cornerColor[0][i]; } return implicitCornerColor; } /** * Calculate the distance from point ps to point pe. * * @param ps one end of a line * @param pe the other end of the line * @return length of the line */ protected double getLen(Point2D ps, Point2D pe) { double x = pe.getX() - ps.getX(); double y = pe.getY() - ps.getY(); return Math.sqrt(x * x + y * y); } /** * Whether the for control points are on a line. * * @param ctl an edge's control points, the size of ctl is 4 * @return true when 4 control points are on a line, otherwise false */ protected boolean isEdgeALine(Point2D[] ctl) { double ctl1 = Math.abs(edgeEquationValue(ctl[1], ctl[0], ctl[3])); double ctl2 = Math.abs(edgeEquationValue(ctl[2], ctl[0], ctl[3])); double x = Math.abs(ctl[0].getX() - ctl[3].getX()); double y = Math.abs(ctl[0].getY() - ctl[3].getY()); return (ctl1 <= x && ctl2 <= x) || (ctl1 <= y && ctl2 <= y); } /** * A line from point p1 to point p2 defines an equation, adjust the form of * the equation to let the rhs equals 0, then calculate the lhs value by * plugging the coordinate of p in the lhs expression. * * @param p target point * @param p1 one end of a line * @param p2 the other end of a line * @return calculated value */ protected double edgeEquationValue(Point2D p, Point2D p1, Point2D p2) { return (p2.getY() - p1.getY()) * (p.getX() - p1.getX()) - (p2.getX() - p1.getX()) * (p.getY() - p1.getY()); } /** * An assistant method to accomplish type 6 and type 7 shading. * * @param patchCC all the crossing point coordinates and color of a grid * @return a ShadedTriangle list which can compose the grid patch */ protected List getShadedTriangles(CoordinateColorPair[][] patchCC) { List list = new ArrayList(); int szV = patchCC.length; int szU = patchCC[0].length; for (int i = 1; i < szV; i++) { for (int j = 1; j < szU; j++) { Point2D p0 = patchCC[i - 1][j - 1].coordinate, p1 = patchCC[i - 1][j].coordinate, p2 = patchCC[i][j].coordinate, p3 = patchCC[i][j - 1].coordinate; boolean ll = true; if (overlaps(p0, p1) || overlaps(p0, p3)) { ll = false; } else { // p0, p1 and p3 are in counter clock wise order, p1 has priority over p0, p3 has priority over p1 Point2D[] llCorner = { p0, p1, p3 }; float[][] llColor = { patchCC[i - 1][j - 1].color, patchCC[i - 1][j].color, patchCC[i][j - 1].color }; ShadedTriangle tmpll = new ShadedTriangle(llCorner, llColor); // lower left triangle list.add(tmpll); } if (ll && (overlaps(p2, p1) || overlaps(p2, p3))) { } else { // p3, p1 and p2 are in counter clock wise order, p1 has priority over p3, p2 has priority over p1 Point2D[] urCorner = { p3, p1, p2 }; float[][] urColor = { patchCC[i][j - 1].color, patchCC[i - 1][j].color, patchCC[i][j].color }; ShadedTriangle tmpur = new ShadedTriangle(urCorner, urColor); // upper right triangle list.add(tmpur); } } } return list; } // whether two points p0 and p1 are degenerated into one point within the coordinates' accuracy 0.001 private boolean overlaps(Point2D p0, Point2D p1) { return Math.abs(p0.getX() - p1.getX()) < 0.001 && Math.abs(p0.getY() - p1.getY()) < 0.001; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/PatchMeshesShadingContext.java000066400000000000000000000251461320103431700334530ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import static java.util.Objects.isNull; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.io.EOFException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDRange; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is extended in Type6ShadingContext and Type7ShadingContext. This * was done as part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ abstract class PatchMeshesShadingContext extends TriangleBasedShadingContext { private static final Logger LOG = LoggerFactory.getLogger(PatchMeshesShadingContext.class); /** * patch list */ private List patchList = new ArrayList(); /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param colorModel the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @param deviceBounds device bounds * @param controlPoints number of control points, 12 for type 6 shading and 16 for type 7 shading * @throws IOException if something went wrong */ protected PatchMeshesShadingContext(PDShadingType6 shading, ColorModel colorModel, AffineTransform xform, Matrix matrix, Rectangle deviceBounds, int controlPoints) throws IOException { super(shading, colorModel, xform, matrix); patchList = collectPatches(shading, xform, matrix, controlPoints); createPixelTable(deviceBounds); } /** * Create a patch list from a data stream, the returned list contains all the patches contained * in the data stream. * * @param shadingType the shading type * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @param controlPoints number of control points, 12 for type 6 shading and 16 for type 7 shading * @return the obtained patch list * @throws IOException when something went wrong */ final List collectPatches(PDShadingType6 shadingType, AffineTransform xform, Matrix matrix, int controlPoints) throws IOException { COSDictionary dict = shadingType.getCOSObject(); int bitsPerFlag = shadingType.getBitsPerFlag(); PDRange rangeX = shadingType.getDecodeForParameter(0); PDRange rangeY = shadingType.getDecodeForParameter(1); PDRange[] colRange = new PDRange[numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; ++i) { colRange[i] = shadingType.getDecodeForParameter(2 + i); if (isNull(colRange[i])) { throw new IOException("Range missing in shading /Decode entry"); } } List list = new ArrayList<>(); long maxSrcCoord = (long) Math.pow(2, bitsPerCoordinate) - 1; long maxSrcColor = (long) Math.pow(2, bitsPerColorComponent) - 1; COSStream cosStream = (COSStream) dict; ImageInputStream mciis = new MemoryCacheImageInputStream(cosStream.getUnfilteredStream()); try { Point2D[] implicitEdge = new Point2D[4]; float[][] implicitCornerColor = new float[2][numberOfColorComponents]; byte flag = 0; try { flag = (byte) (mciis.readBits(bitsPerFlag) & 3); } catch (EOFException ex) { LOG.error(ex.getMessage()); } boolean eof = false; while (!eof) { try { boolean isFree = (flag == 0); Patch current = readPatch(mciis, isFree, implicitEdge, implicitCornerColor, maxSrcCoord, maxSrcColor, rangeX, rangeY, colRange, matrix, xform, controlPoints); if (current == null) { break; } list.add(current); flag = (byte) (mciis.readBits(bitsPerFlag) & 3); switch (flag) { case 0: break; case 1: implicitEdge = current.getFlag1Edge(); implicitCornerColor = current.getFlag1Color(); break; case 2: implicitEdge = current.getFlag2Edge(); implicitCornerColor = current.getFlag2Color(); break; case 3: implicitEdge = current.getFlag3Edge(); implicitCornerColor = current.getFlag3Color(); break; default: LOG.warn("bad flag: " + flag); break; } } catch (EOFException ex) { eof = true; } } } finally { mciis.close(); } return list; } /** * Read a single patch from a data stream, a patch contains information of its coordinates and * color parameters. * * @param input the image source data stream * @param isFree whether this is a free patch * @param implicitEdge implicit edge when a patch is not free, otherwise it's not used * @param implicitCornerColor implicit colors when a patch is not free, otherwise it's not used * @param maxSrcCoord the maximum coordinate value calculated from source data * @param maxSrcColor the maximum color value calculated from source data * @param rangeX range for coordinate x * @param rangeY range for coordinate y * @param colRange range for color * @param matrix the pattern matrix concatenated with that of the parent content stream * @param xform transformation for user to device space * @param controlPoints number of control points, 12 for type 6 shading and 16 for type 7 shading * @return a single patch * @throws IOException when something went wrong */ protected Patch readPatch(ImageInputStream input, boolean isFree, Point2D[] implicitEdge, float[][] implicitCornerColor, long maxSrcCoord, long maxSrcColor, PDRange rangeX, PDRange rangeY, PDRange[] colRange, Matrix matrix, AffineTransform xform, int controlPoints) throws IOException { float[][] color = new float[4][numberOfColorComponents]; Point2D[] points = new Point2D[controlPoints]; int pStart = 4, cStart = 2; if (isFree) { pStart = 0; cStart = 0; } else { points[0] = implicitEdge[0]; points[1] = implicitEdge[1]; points[2] = implicitEdge[2]; points[3] = implicitEdge[3]; for (int i = 0; i < numberOfColorComponents; i++) { color[0][i] = implicitCornerColor[0][i]; color[1][i] = implicitCornerColor[1][i]; } } try { for (int i = pStart; i < controlPoints; i++) { long x = input.readBits(bitsPerCoordinate); long y = input.readBits(bitsPerCoordinate); float px = interpolate(x, maxSrcCoord, rangeX.getMin(), rangeX.getMax()); float py = interpolate(y, maxSrcCoord, rangeY.getMin(), rangeY.getMax()); Point2D p = matrix.transformPoint(px, py); xform.transform(p, p); points[i] = p; } for (int i = cStart; i < 4; i++) { for (int j = 0; j < numberOfColorComponents; j++) { long c = input.readBits(bitsPerColorComponent); color[i][j] = interpolate(c, maxSrcColor, colRange[j].getMin(), colRange[j].getMax()); } } } catch (EOFException ex) { LOG.debug("EOF"); return null; } return generatePatch(points, color); } /** * Create a patch using control points and 4 corner color values, in * Type6ShadingContext, a CoonsPatch is returned; in Type6ShadingContext, a * TensorPatch is returned. * * @param points 12 or 16 control points * @param color 4 corner colors * @return a patch instance */ abstract Patch generatePatch(Point2D[] points, float[][] color); /** * Get a point coordinate on a line by linear interpolation. */ private float interpolate(float x, long maxValue, float rangeMin, float rangeMax) { return rangeMin + (x / maxValue) * (rangeMax - rangeMin); } @Override protected Map calcPixelTable(Rectangle deviceBounds) throws IOException { Map map = new HashMap(); for (Patch it : patchList) { super.calcPixelTable(it.listOfTriangles, map, deviceBounds); } return map; } @Override public void dispose() { patchList = null; super.dispose(); } @Override protected boolean isDataEmpty() { return patchList.isEmpty(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/RadialShadingContext.java000066400000000000000000000325121320103431700324360ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.pdmodel.common.function.PDFunction; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT PaintContext for radial shading. * * Performance improvement done as part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ public class RadialShadingContext extends ShadingContext implements PaintContext { private static final Logger LOG = LoggerFactory.getLogger(RadialShadingContext.class); private PDShadingType3 radialShadingType; private final float[] coords; private final float[] domain; private final boolean[] extend; private final double x1x0; private final double y1y0; private final double r1r0; private final double r0pow2; private final float d1d0; private final double denom; private final int factor; private final int[] colorTable; private AffineTransform rat; /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param colorModel the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @param deviceBounds the bounds of the area to paint, in device units * @throws IOException if there is an error getting the color space or doing color conversion. */ public RadialShadingContext(PDShadingType3 shading, ColorModel colorModel, AffineTransform xform, Matrix matrix, Rectangle deviceBounds) throws IOException { super(shading, colorModel, xform, matrix); this.radialShadingType = shading; coords = shading.getCoords().toFloatArray(); // domain values if (this.radialShadingType.getDomain() != null) { domain = shading.getDomain().toFloatArray(); } else { // set default values domain = new float[] { 0, 1 }; } // extend values COSArray extendValues = shading.getExtend(); if (extendValues != null) { extend = new boolean[2]; extend[0] = ((COSBoolean) extendValues.getObject(0)).getValue(); extend[1] = ((COSBoolean) extendValues.getObject(1)).getValue(); } else { // set default values extend = new boolean[] { false, false }; } // calculate some constants to be used in getRaster x1x0 = coords[3] - coords[0]; y1y0 = coords[4] - coords[1]; r1r0 = coords[5] - coords[2]; r0pow2 = Math.pow(coords[2], 2); denom = Math.pow(x1x0, 2) + Math.pow(y1y0, 2) - Math.pow(r1r0, 2); d1d0 = domain[1] - domain[0]; try { // get inverse transform to be independent of current user / device space // when handling actual pixels in getRaster() rat = matrix.createAffineTransform().createInverse(); rat.concatenate(xform.createInverse()); } catch (NoninvertibleTransformException ex) { LOG.error(ex.getMessage() + ", matrix: " + matrix, ex); LOG.error(ex.getMessage(), ex); } // shading space -> device space AffineTransform shadingToDevice = (AffineTransform) xform.clone(); shadingToDevice.concatenate(matrix.createAffineTransform()); // worst case for the number of steps is opposite diagonal corners, so use that double dist = Math.sqrt(Math.pow(deviceBounds.getMaxX() - deviceBounds.getMinX(), 2) + Math.pow(deviceBounds.getMaxY() - deviceBounds.getMinY(), 2)); factor = (int) Math.ceil(dist); // build the color table for the given number of steps colorTable = calcColorTable(); } /** * Calculate the color on the line that connects two circles' centers and store the result in an * array. * * @return an array, index denotes the relative position, the corresponding value the color */ private int[] calcColorTable() throws IOException { int[] map = new int[factor + 1]; if (factor == 0 || d1d0 == 0) { float[] values = radialShadingType.evalFunction(domain[0]); map[0] = convertToRGB(values); } else { for (int i = 0; i <= factor; i++) { float t = domain[0] + d1d0 * i / factor; float[] values = radialShadingType.evalFunction(t); map[i] = convertToRGB(values); } } return map; } @Override public void dispose() { super.dispose(); radialShadingType = null; } @Override public ColorModel getColorModel() { return super.getColorModel(); } @Override public Raster getRaster(int x, int y, int w, int h) { // create writable raster WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h); float inputValue = -1; boolean useBackground; int[] data = new int[w * h * 4]; for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { float[] values = new float[] { x + i, y + j }; rat.transform(values, 0, values, 0, 1); useBackground = false; float[] inputValues = calculateInputValues(values[0], values[1]); if (Float.isNaN(inputValues[0]) && Float.isNaN(inputValues[1])) { if (getBackground() == null) { continue; } useBackground = true; } else { // choose 1 of the 2 values if (inputValues[0] >= 0 && inputValues[0] <= 1) { // both values are in the range -> choose the larger one if (inputValues[1] >= 0 && inputValues[1] <= 1) { inputValue = Math.max(inputValues[0], inputValues[1]); } // first value is in the range, the second not -> choose first value else { inputValue = inputValues[0]; } } else { // first value is not in the range, // but the second -> choose second value if (inputValues[1] >= 0 && inputValues[1] <= 1) { inputValue = inputValues[1]; } // both are not in the range else { if (extend[0] && extend[1]) { inputValue = Math.max(inputValues[0], inputValues[1]); } else if (extend[0]) { inputValue = inputValues[0]; } else if (extend[1]) { inputValue = inputValues[1]; } else if (getBackground() != null) { useBackground = true; } else { continue; } } } // input value is out of range if (inputValue > 1) { // extend shading if extend[1] is true and nonzero radius if (extend[1] && coords[5] > 0) { inputValue = 1; } else { if (getBackground() == null) { continue; } useBackground = true; } } // input value is out of range else if (inputValue < 0) { // extend shading if extend[0] is true and nonzero radius if (extend[0] && coords[2] > 0) { inputValue = 0; } else { if (getBackground() == null) { continue; } useBackground = true; } } } int value; if (useBackground) { // use the given backgound color values value = getRgbBackground(); } else { int key = (int) (inputValue * factor); value = colorTable[key]; } int index = (j * w + i) * 4; data[index] = value & 255; value >>= 8; data[index + 1] = value & 255; value >>= 8; data[index + 2] = value & 255; data[index + 3] = 255; } } raster.setPixels(0, 0, w, h, data); return raster; } private float[] calculateInputValues(double x, double y) { // According to Adobes Technical Note #5600 we have to do the following // // x0, y0, r0 defines the start circle x1, y1, r1 defines the end circle // // The parametric equations for the center and radius of the gradient fill circle moving // between the start circle and the end circle as a function of s are as follows: // // xc(s) = x0 + s * (x1 - x0) yc(s) = y0 + s * (y1 - y0) r(s) = r0 + s * (r1 - r0) // // Given a geometric coordinate position (x, y) in or along the gradient fill, the // corresponding value of s can be determined by solving the quadratic constraint equation: // // [x - xc(s)]2 + [y - yc(s)]2 = [r(s)]2 // // The following code calculates the 2 possible values of s // double p = -(x - coords[0]) * x1x0 - (y - coords[1]) * y1y0 - coords[2] * r1r0; double q = (Math.pow(x - coords[0], 2) + Math.pow(y - coords[1], 2) - r0pow2); double root = Math.sqrt(p * p - denom * q); float root1 = (float) ((-p + root) / denom); float root2 = (float) ((-p - root) / denom); if (denom < 0) { return new float[] { root1, root2 }; } else { return new float[] { root2, root1 }; } } /** * Returns the coords values. */ public float[] getCoords() { return coords; } /** * Returns the domain values. */ public float[] getDomain() { return domain; } /** * Returns the extend values. */ public boolean[] getExtend() { return extend; } /** * Returns the function. * * @throws java.io.IOException if we were not able to create the function. */ public PDFunction getFunction() throws IOException { return radialShadingType.getFunction(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/RadialShadingPaint.java000066400000000000000000000046761320103431700320770ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Color; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT Paint for radial shading. * */ public class RadialShadingPaint implements Paint { private static final Logger LOG = LoggerFactory.getLogger(RadialShadingPaint.class); private final PDShadingType3 shading; private final Matrix matrix; /** * Constructor. * * @param shading the shading resources * @param matrix the pattern matrix concatenated with that of the parent content stream */ RadialShadingPaint(PDShadingType3 shading, Matrix matrix) { this.shading = shading; this.matrix = matrix; } @Override public int getTransparency() { return 0; } @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { try { return new RadialShadingContext(shading, cm, xform, matrix, deviceBounds); } catch (IOException e) { LOG.error("An error occurred while painting", e); return new Color(0, 0, 0, 0).createContext(cm, deviceBounds, userBounds, xform, hints); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/ShadedTriangle.java000066400000000000000000000210351320103431700312530ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Point; import java.awt.geom.Point2D; import java.util.HashSet; import java.util.Set; /** * This is an assistant class for accomplishing type 4, 5, 6 and 7 shading. It * describes a triangle actually, which is used to compose a patch. It contains * the degenerated cases, a triangle degenerates to a line or to a point. This * was done as part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class ShadedTriangle { protected final Point2D[] corner; // vertices coordinates of a triangle protected final float[][] color; private final double area; // area of the triangle /* degree = 3 describes a normal triangle, degree = 2 when a triangle degenerates to a line, degree = 1 when a triangle degenerates to a point */ private final int degree; // describes a rasterized line when a triangle degerates to a line, otherwise null private final Line line; // corner's edge (the opposite edge of a corner) equation value private final double v0; private final double v1; private final double v2; /** * Constructor. * * @param p an array of the 3 vertices of a triangle * @param c an array of color corresponding the vertex array p */ ShadedTriangle(Point2D[] p, float[][] c) { corner = p.clone(); color = c.clone(); area = getArea(p[0], p[1], p[2]); degree = calcDeg(p); if (degree == 2) { if (overlaps(corner[1], corner[2]) && !overlaps(corner[0], corner[2])) { Point p0 = new Point((int) Math.round(corner[0].getX()), (int) Math.round(corner[0].getY())); Point p1 = new Point((int) Math.round(corner[2].getX()), (int) Math.round(corner[2].getY())); line = new Line(p0, p1, color[0], color[2]); } else { Point p0 = new Point((int) Math.round(corner[1].getX()), (int) Math.round(corner[1].getY())); Point p1 = new Point((int) Math.round(corner[2].getX()), (int) Math.round(corner[2].getY())); line = new Line(p0, p1, color[1], color[2]); } } else { line = null; } v0 = edgeEquationValue(p[0], p[1], p[2]); v1 = edgeEquationValue(p[1], p[2], p[0]); v2 = edgeEquationValue(p[2], p[0], p[1]); } /** * Calculate the degree value of a triangle. * * @param p 3 vertices coordinates * @return number of unique points in the 3 vertices of a triangle, 3, 2 or * 1 */ private int calcDeg(Point2D[] p) { Set set = new HashSet(); for (Point2D itp : p) { Point np = new Point((int) Math.round(itp.getX() * 1000), (int) Math.round(itp.getY() * 1000)); set.add(np); } return set.size(); } public int getDeg() { return degree; } /** * get the boundary of a triangle. * * @return {xmin, xmax, ymin, ymax} */ public int[] getBoundary() { int[] boundary = new int[4]; int x0 = (int) Math.round(corner[0].getX()); int x1 = (int) Math.round(corner[1].getX()); int x2 = (int) Math.round(corner[2].getX()); int y0 = (int) Math.round(corner[0].getY()); int y1 = (int) Math.round(corner[1].getY()); int y2 = (int) Math.round(corner[2].getY()); boundary[0] = Math.min(Math.min(x0, x1), x2); boundary[1] = Math.max(Math.max(x0, x1), x2); boundary[2] = Math.min(Math.min(y0, y1), y2); boundary[3] = Math.max(Math.max(y0, y1), y2); return boundary; } /** * Get the line of a triangle. * * @return points of the line, or null if this triangle isn't a line */ public Line getLine() { return line; } /** * Whether a point is contained in this ShadedTriangle. * * @param p the target point * @return false if p is outside of this triangle, otherwise true */ public boolean contains(Point2D p) { if (degree == 1) { return overlaps(corner[0], p) | overlaps(corner[1], p) | overlaps(corner[2], p); } else if (degree == 2) { Point tp = new Point((int) Math.round(p.getX()), (int) Math.round(p.getY())); return line.linePoints.contains(tp); } /* the following code judges whether a point is contained in a normal triangle, taking the on edge case as contained */ double pv0 = edgeEquationValue(p, corner[1], corner[2]); /* if corner[0] and point p are on different sides of line from corner[1] to corner[2], p is outside of the triangle */ if (pv0 * v0 < 0) { return false; } double pv1 = edgeEquationValue(p, corner[2], corner[0]); /* if vertex corner[1] and point p are on different sides of line from corner[2] to corner[0], p is outside of the triangle */ if (pv1 * v1 < 0) { return false; } double pv2 = edgeEquationValue(p, corner[0], corner[1]); /* only left one case: if corner[1] and point p are on different sides of line from corner[2] to corner[0], p is outside of the triangle, otherwise p is contained in the triangle */ return pv2 * v2 >= 0; // !(pv2 * v2 < 0) } /* check whether two points overlaps each other, as points' coordinates are of type double, the coordinates' accuracy used here is 0.001 */ private boolean overlaps(Point2D p0, Point2D p1) { return Math.abs(p0.getX() - p1.getX()) < 0.001 && Math.abs(p0.getY() - p1.getY()) < 0.001; } /* two points can define a line equation, adjust the form of the equation to let the rhs equals 0, calculate the lhs value by plugging the coordinate of p in the lhs expression */ private double edgeEquationValue(Point2D p, Point2D p1, Point2D p2) { return (p2.getY() - p1.getY()) * (p.getX() - p1.getX()) - (p2.getX() - p1.getX()) * (p.getY() - p1.getY()); } // calcuate the area of a triangle private double getArea(Point2D a, Point2D b, Point2D c) { return Math.abs((c.getX() - b.getX()) * (c.getY() - a.getY()) - (c.getX() - a.getX()) * (c.getY() - b.getY())) / 2.0; } /** * Calculate the color of a point. * * @param p the target point * @return an array denotes the point's color */ public float[] calcColor(Point2D p) { int numberOfColorComponents = color[0].length; float[] pCol = new float[numberOfColorComponents]; switch (degree) { case 1: for (int i = 0; i < numberOfColorComponents; i++) { // average pCol[i] = (color[0][i] + color[1][i] + color[2][i]) / 3.0f; } break; case 2: // linear interpolation Point tp = new Point((int) Math.round(p.getX()), (int) Math.round(p.getY())); return line.calcColor(tp); default: float aw = (float) (getArea(p, corner[1], corner[2]) / area); float bw = (float) (getArea(p, corner[2], corner[0]) / area); float cw = (float) (getArea(p, corner[0], corner[1]) / area); for (int i = 0; i < numberOfColorComponents; i++) { // barycentric interpolation pCol[i] = color[0][i] * aw + color[1][i] * bw + color[2][i] * cw; } break; } return pCol; } @Override public String toString() { return corner[0] + " " + corner[1] + " " + corner[2]; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/ShadingContext.java000066400000000000000000000071761320103431700313310ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.util.Matrix; /** * A base class to handle what is common to all shading types. * * @author Shaola Ren * @author Tilman Hausherr */ public abstract class ShadingContext { private float[] background; private int rgbBackground; private final PDShading shading; private ColorModel outputColorModel; private PDColorSpace shadingColorSpace; /** * Constructor. * * @param shading the shading type to be used * @param cm the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @throws java.io.IOException if there is an error getting the color space * or doing background color conversion. */ public ShadingContext(PDShading shading, ColorModel cm, AffineTransform xform, Matrix matrix) throws IOException { this.shading = shading; shadingColorSpace = shading.getColorSpace(); // create the output color model using RGB+alpha as color space ColorSpace outputCS = ColorSpace.getInstance(ColorSpace.CS_sRGB); outputColorModel = new ComponentColorModel(outputCS, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); // get background values if available COSArray bg = shading.getBackground(); if (bg != null) { background = bg.toFloatArray(); rgbBackground = convertToRGB(background); } } PDColorSpace getShadingColorSpace() { return shadingColorSpace; } PDShading getShading() { return shading; } float[] getBackground() { return background; } int getRgbBackground() { return rgbBackground; } /** * Convert color values from shading colorspace to RGB color values encoded * into an integer. * * @param values color values in shading colorspace. * @return RGB values encoded in an integer. * @throws java.io.IOException if the color conversion fails. */ final int convertToRGB(float[] values) throws IOException { int normRGBValues; float[] rgbValues = shadingColorSpace.toRGB(values); normRGBValues = (int) (rgbValues[0] * 255); normRGBValues |= (int) (rgbValues[1] * 255) << 8; normRGBValues |= (int) (rgbValues[2] * 255) << 16; return normRGBValues; } ColorModel getColorModel() { return outputColorModel; } void dispose() { outputColorModel = null; shadingColorSpace = null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/TensorPatch.java000066400000000000000000000220021320103431700306220ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.geom.Point2D; import java.util.List; /** * This class is used to describe a patch for type 7 shading. This was done as * part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class TensorPatch extends Patch { /** * Constructor of a patch for type 7 shading. * * @param points 16 control points * @param color 4 corner colors */ protected TensorPatch(Point2D[] tcp, float[][] color) { super(tcp, color); controlPoints = reshapeControlPoints(tcp); level = calcLevel(); listOfTriangles = getTriangles(); } /* order the 16 1d points to a square matrix which is as the one described in p.199 of PDF3200_2008.pdf rotated 90 degrees clockwise */ private Point2D[][] reshapeControlPoints(Point2D[] tcp) { Point2D[][] square = new Point2D[4][4]; for (int i = 0; i <= 3; i++) { square[0][i] = tcp[i]; square[3][i] = tcp[9 - i]; } for (int i = 1; i <= 2; i++) { square[i][0] = tcp[12 - i]; square[i][2] = tcp[12 + i]; square[i][3] = tcp[3 + i]; } square[1][1] = tcp[12]; square[2][1] = tcp[15]; return square; } // calculate the dividing level from the control points private int[] calcLevel() { int[] l = { 4, 4 }; Point2D[] ctlC1 = new Point2D[4]; Point2D[] ctlC2 = new Point2D[4]; for (int j = 0; j < 4; j++) { ctlC1[j] = controlPoints[j][0]; ctlC2[j] = controlPoints[j][3]; } // if two opposite edges are both lines, there is a possibility to reduce the dividing level if (isEdgeALine(ctlC1) && isEdgeALine(ctlC2)) { /* if any of the 4 inner control points is out of the patch formed by the 4 edges, keep the high dividing level, otherwise, determine the dividing level by the lengths of edges */ if (isOnSameSideCC(controlPoints[1][1]) || isOnSameSideCC(controlPoints[1][2]) || isOnSameSideCC(controlPoints[2][1]) || isOnSameSideCC(controlPoints[2][2])) { // keep the high dividing level } else { // length's unit is one pixel in device space double lc1 = getLen(ctlC1[0], ctlC1[3]), lc2 = getLen(ctlC2[0], ctlC2[3]); if (lc1 > 800 || lc2 > 800) { // keeps init value 4 } else if (lc1 > 400 || lc2 > 400) { l[0] = 3; } else if (lc1 > 200 || lc2 > 200) { l[0] = 2; } else { l[0] = 1; } } } // the other two opposite edges if (isEdgeALine(controlPoints[0]) && isEdgeALine(controlPoints[3])) { if (isOnSameSideDD(controlPoints[1][1]) || isOnSameSideDD(controlPoints[1][2]) || isOnSameSideDD(controlPoints[2][1]) || isOnSameSideDD(controlPoints[2][2])) { // keep the high dividing level } else { double ld1 = getLen(controlPoints[0][0], controlPoints[0][3]); double ld2 = getLen(controlPoints[3][0], controlPoints[3][3]); if (ld1 > 800 || ld2 > 800) { // keeps init value 4 } else if (ld1 > 400 || ld2 > 400) { l[1] = 3; } else if (ld1 > 200 || ld2 > 200) { l[1] = 2; } else { l[1] = 1; } } } return l; } // whether a point is on the same side of edge C1 and edge C2 private boolean isOnSameSideCC(Point2D p) { double cc = edgeEquationValue(p, controlPoints[0][0], controlPoints[3][0]) * edgeEquationValue(p, controlPoints[0][3], controlPoints[3][3]); return cc > 0; } // whether a point is on the same side of edge D1 and edge D2 private boolean isOnSameSideDD(Point2D p) { double dd = edgeEquationValue(p, controlPoints[0][0], controlPoints[0][3]) * edgeEquationValue(p, controlPoints[3][0], controlPoints[3][3]); return dd > 0; } // get a list of triangles which compose this tensor patch private List getTriangles() { CoordinateColorPair[][] patchCC = getPatchCoordinatesColor(); return getShadedTriangles(patchCC); } @Override protected Point2D[] getFlag1Edge() { Point2D[] implicitEdge = new Point2D[4]; for (int i = 0; i < 4; i++) { implicitEdge[i] = controlPoints[i][3]; } return implicitEdge; } @Override protected Point2D[] getFlag2Edge() { Point2D[] implicitEdge = new Point2D[4]; for (int i = 0; i < 4; i++) { implicitEdge[i] = controlPoints[3][3 - i]; } return implicitEdge; } @Override protected Point2D[] getFlag3Edge() { Point2D[] implicitEdge = new Point2D[4]; for (int i = 0; i < 4; i++) { implicitEdge[i] = controlPoints[3 - i][0]; } return implicitEdge; } /* dividing a patch into a grid according to level, then calculate the coordinate and color of each crossing point in the grid, the rule to calculate the coordinate is tensor-product which is defined in page 119 of PDF32000_2008.pdf, the method to calculate the cooresponding color is bilinear interpolation */ private CoordinateColorPair[][] getPatchCoordinatesColor() { int numberOfColorComponents = cornerColor[0].length; double[][] bernsteinPolyU = getBernsteinPolynomials(level[0]); int szU = bernsteinPolyU[0].length; double[][] bernsteinPolyV = getBernsteinPolynomials(level[1]); int szV = bernsteinPolyV[0].length; CoordinateColorPair[][] patchCC = new CoordinateColorPair[szV][szU]; double stepU = 1.0 / (szU - 1); double stepV = 1.0 / (szV - 1); double v = -stepV; for (int k = 0; k < szV; k++) { // v and u are the assistant parameters v += stepV; double u = -stepU; for (int l = 0; l < szU; l++) { double tmpx = 0.0; double tmpy = 0.0; // these two "for" loops are for the equation to define the patch surface (coordinates) for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { tmpx += controlPoints[i][j].getX() * bernsteinPolyU[i][l] * bernsteinPolyV[j][k]; tmpy += controlPoints[i][j].getY() * bernsteinPolyU[i][l] * bernsteinPolyV[j][k]; } } Point2D tmpC = new Point2D.Double(tmpx, tmpy); u += stepU; float[] paramSC = new float[numberOfColorComponents]; for (int ci = 0; ci < numberOfColorComponents; ci++) { paramSC[ci] = (float) ((1 - v) * ((1 - u) * cornerColor[0][ci] + u * cornerColor[3][ci]) + v * ((1 - u) * cornerColor[1][ci] + u * cornerColor[2][ci])); // bilinear interpolation } patchCC[k][l] = new CoordinateColorPair(tmpC, paramSC); } } return patchCC; } // Bernstein polynomials which are defined in page 119 of PDF32000_2008.pdf private double[][] getBernsteinPolynomials(int lvl) { int sz = (1 << lvl) + 1; double[][] poly = new double[4][sz]; double step = 1.0 / (sz - 1); double t = -step; for (int i = 0; i < sz; i++) { t += step; poly[0][i] = (1 - t) * (1 - t) * (1 - t); poly[1][i] = 3 * t * (1 - t) * (1 - t); poly[2][i] = 3 * t * t * (1 - t); poly[3][i] = t * t * t; } return poly; } } TriangleBasedShadingContext.java000066400000000000000000000155461320103431700336770ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.PaintContext; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.util.List; import java.util.Map; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Intermediate class extended by the shading types 4,5,6 and 7 that contains the common methods * used by these classes. * * @author Shaola Ren * @author Tilman Hausherr */ abstract class TriangleBasedShadingContext extends ShadingContext implements PaintContext { private static final Logger LOG = LoggerFactory.getLogger(TriangleBasedShadingContext.class); protected int bitsPerCoordinate; protected int bitsPerColorComponent; protected int numberOfColorComponents; private final boolean hasFunction; // map of pixels within triangles to their RGB color private Map pixelTable; /** * Constructor. * * @param shading the shading type to be used * @param cm the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @throws IOException if there is an error getting the color space or doing background color conversion. */ TriangleBasedShadingContext(PDShading shading, ColorModel cm, AffineTransform xform, Matrix matrix) throws IOException { super(shading, cm, xform, matrix); PDTriangleBasedShadingType triangleBasedShadingType = (PDTriangleBasedShadingType) shading; hasFunction = shading.getFunction() != null; bitsPerCoordinate = triangleBasedShadingType.getBitsPerCoordinate(); LOG.debug("bitsPerCoordinate: " + (Math.pow(2, bitsPerCoordinate) - 1)); bitsPerColorComponent = triangleBasedShadingType.getBitsPerComponent(); LOG.debug("bitsPerColorComponent: " + bitsPerColorComponent); numberOfColorComponents = hasFunction ? 1 : getShadingColorSpace().getNumberOfComponents(); LOG.debug("numberOfColorComponents: " + numberOfColorComponents); } /** * Creates the pixel table. */ protected final void createPixelTable(Rectangle deviceBounds) throws IOException { pixelTable = calcPixelTable(deviceBounds); } /** * Calculate every point and its color and store them in a Hash table. * * @return a Hash table which contains all the points' positions and colors of one image */ abstract Map calcPixelTable(Rectangle deviceBounds) throws IOException; /** * Get the points from the triangles, calculate their color and add point-color mappings. */ protected void calcPixelTable(List triangleList, Map map, Rectangle deviceBounds) throws IOException { for (ShadedTriangle tri : triangleList) { int degree = tri.getDeg(); if (degree == 2) { Line line = tri.getLine(); for (Point p : line.linePoints) { map.put(p, evalFunctionAndConvertToRGB(line.calcColor(p))); } } else { int[] boundary = tri.getBoundary(); boundary[0] = Math.max(boundary[0], deviceBounds.x); boundary[1] = Math.min(boundary[1], deviceBounds.x + deviceBounds.width); boundary[2] = Math.max(boundary[2], deviceBounds.y); boundary[3] = Math.min(boundary[3], deviceBounds.y + deviceBounds.height); for (int x = boundary[0]; x <= boundary[1]; x++) { for (int y = boundary[2]; y <= boundary[3]; y++) { Point p = new Point(x, y); if (tri.contains(p)) { map.put(p, evalFunctionAndConvertToRGB(tri.calcColor(p))); } } } } } } /** * Convert color to RGB color value, using function if required, then convert from the shading * color space to an RGB value, which is encoded into an integer. */ private int evalFunctionAndConvertToRGB(float[] values) throws IOException { if (hasFunction) { values = getShading().evalFunction(values); } return convertToRGB(values); } /** * Returns true if the shading has an empty data stream. */ abstract boolean isDataEmpty(); @Override public final ColorModel getColorModel() { return super.getColorModel(); } @Override public void dispose() { super.dispose(); } @Override public final Raster getRaster(int x, int y, int w, int h) { WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h); int[] data = new int[w * h * 4]; if (!isDataEmpty() || getBackground() != null) { for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { Point p = new Point(x + col, y + row); int value; Integer v = pixelTable.get(p); if (v != null) { value = v; } else { if (getBackground() == null) { continue; } value = getRgbBackground(); } int index = (row * w + col) * 4; data[index] = value & 255; value >>= 8; data[index + 1] = value & 255; value >>= 8; data[index + 2] = value & 255; data[index + 3] = 255; } } } raster.setPixels(0, 0, w, h, data); return raster; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type1ShadingContext.java000066400000000000000000000127151320103431700322470ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.PaintContext; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT PaintContext for function-based (Type 1) shading. * * @author Tilman Hausherr */ class Type1ShadingContext extends ShadingContext implements PaintContext { private static final Logger LOG = LoggerFactory.getLogger(Type1ShadingContext.class); private PDShadingType1 type1ShadingType; private AffineTransform rat; private final float[] domain; /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param colorModel the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream */ Type1ShadingContext(PDShadingType1 shading, ColorModel colorModel, AffineTransform xform, Matrix matrix) throws IOException { super(shading, colorModel, xform, matrix); this.type1ShadingType = shading; // (Optional) An array of four numbers [ xmin xmax ymin ymax ] // specifying the rectangular domain of coordinates over which the // color function(s) are defined. Default value: [ 0.0 1.0 0.0 1.0 ]. if (shading.getDomain() != null) { domain = shading.getDomain().toFloatArray(); } else { domain = new float[] { 0, 1, 0, 1 }; } try { // get inverse transform to be independent of // shading matrix and current user / device space // when handling actual pixels in getRaster() rat = shading.getMatrix().createAffineTransform().createInverse(); rat.concatenate(matrix.createAffineTransform().createInverse()); rat.concatenate(xform.createInverse()); } catch (NoninvertibleTransformException ex) { LOG.error(ex.getMessage() + ", matrix: " + matrix, ex); rat = new AffineTransform(); } } @Override public void dispose() { super.dispose(); type1ShadingType = null; } @Override public ColorModel getColorModel() { return super.getColorModel(); } @Override public Raster getRaster(int x, int y, int w, int h) { WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h); int[] data = new int[w * h * 4]; for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { int index = (j * w + i) * 4; boolean useBackground = false; float[] values = new float[] { x + i, y + j }; rat.transform(values, 0, values, 0, 1); if (values[0] < domain[0] || values[0] > domain[1] || values[1] < domain[2] || values[1] > domain[3]) { if (getBackground() == null) { continue; } useBackground = true; } // evaluate function if (useBackground) { values = getBackground(); } else { try { values = type1ShadingType.evalFunction(values); } catch (IOException e) { LOG.error("error while processing a function", e); } } // convert color values from shading color space to RGB PDColorSpace shadingColorSpace = getShadingColorSpace(); if (shadingColorSpace != null) { try { values = shadingColorSpace.toRGB(values); } catch (IOException e) { LOG.error("error processing color space", e); } } data[index] = (int) (values[0] * 255); data[index + 1] = (int) (values[1] * 255); data[index + 2] = (int) (values[2] * 255); data[index + 3] = 255; } } raster.setPixels(0, 0, w, h, data); return raster; } public float[] getDomain() { return domain; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type1ShadingPaint.java000066400000000000000000000045561320103431700317020ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Color; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT PaintContext for function-based (Type 1) shading. */ class Type1ShadingPaint implements Paint { private static final Logger LOG = LoggerFactory.getLogger(Type1ShadingPaint.class); private final PDShadingType1 shading; private final Matrix matrix; /** * Constructor. * * @param shading the shading resources * @param matrix the pattern matrix concatenated with that of the parent content stream */ Type1ShadingPaint(PDShadingType1 shading, Matrix matrix) { this.shading = shading; this.matrix = matrix; } @Override public int getTransparency() { return 0; } @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { try { return new Type1ShadingContext(shading, cm, xform, matrix); } catch (IOException e) { LOG.error("An error occurred while painting", e); return new Color(0, 0, 0, 0).createContext(cm, deviceBounds, userBounds, xform, hints); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type4ShadingContext.java000066400000000000000000000152271320103431700322530ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.io.EOFException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDRange; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT PaintContext for Gouraud Triangle Mesh (Type 4) shading. * * @author Tilman Hausherr * @author Shaola Ren */ class Type4ShadingContext extends GouraudShadingContext { private static final Logger LOG = LoggerFactory.getLogger(Type4ShadingContext.class); private final int bitsPerFlag; /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param cm the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream */ Type4ShadingContext(PDShadingType4 shading, ColorModel cm, AffineTransform xform, Matrix matrix, Rectangle deviceBounds) throws IOException { super(shading, cm, xform, matrix); LOG.debug("Type4ShadingContext"); bitsPerFlag = shading.getBitsPerFlag(); // TODO handle cases where bitperflag isn't 8 LOG.debug("bitsPerFlag: " + bitsPerFlag); setTriangleList(collectTriangles(shading, xform, matrix)); createPixelTable(deviceBounds); } private List collectTriangles(PDShadingType4 freeTriangleShadingType, AffineTransform xform, Matrix matrix) throws IOException { COSDictionary dict = freeTriangleShadingType.getCOSObject(); PDRange rangeX = freeTriangleShadingType.getDecodeForParameter(0); PDRange rangeY = freeTriangleShadingType.getDecodeForParameter(1); PDRange[] colRange = new PDRange[numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; ++i) { colRange[i] = freeTriangleShadingType.getDecodeForParameter(2 + i); } List list = new ArrayList(); long maxSrcCoord = (long) Math.pow(2, bitsPerCoordinate) - 1; long maxSrcColor = (long) Math.pow(2, bitsPerColorComponent) - 1; COSStream stream = (COSStream) dict; ImageInputStream mciis = new MemoryCacheImageInputStream(stream.getUnfilteredStream()); try { byte flag = (byte) 0; try { flag = (byte) (mciis.readBits(bitsPerFlag) & 3); } catch (EOFException ex) { LOG.error(ex.getMessage()); } boolean eof = false; while (!eof) { Vertex p0, p1, p2; Point2D[] ps; float[][] cs; int lastIndex; try { switch (flag) { case 0: p0 = readVertex(mciis, maxSrcCoord, maxSrcColor, rangeX, rangeY, colRange, matrix, xform); flag = (byte) (mciis.readBits(bitsPerFlag) & 3); if (flag != 0) { LOG.error("bad triangle: " + flag); } p1 = readVertex(mciis, maxSrcCoord, maxSrcColor, rangeX, rangeY, colRange, matrix, xform); mciis.readBits(bitsPerFlag); if (flag != 0) { LOG.error("bad triangle: " + flag); } p2 = readVertex(mciis, maxSrcCoord, maxSrcColor, rangeX, rangeY, colRange, matrix, xform); ps = new Point2D[] { p0.point, p1.point, p2.point }; cs = new float[][] { p0.color, p1.color, p2.color }; list.add(new ShadedTriangle(ps, cs)); flag = (byte) (mciis.readBits(bitsPerFlag) & 3); break; case 1: case 2: lastIndex = list.size() - 1; if (lastIndex < 0) { LOG.error("broken data stream: " + list.size()); } else { ShadedTriangle preTri = list.get(lastIndex); p2 = readVertex(mciis, maxSrcCoord, maxSrcColor, rangeX, rangeY, colRange, matrix, xform); ps = new Point2D[] { flag == 1 ? preTri.corner[1] : preTri.corner[0], preTri.corner[2], p2.point }; cs = new float[][] { flag == 1 ? preTri.color[1] : preTri.color[0], preTri.color[2], p2.color }; list.add(new ShadedTriangle(ps, cs)); flag = (byte) (mciis.readBits(bitsPerFlag) & 3); } break; default: LOG.warn("bad flag: " + flag); break; } } catch (EOFException ex) { eof = true; } } } finally { mciis.close(); } return list; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type4ShadingPaint.java000066400000000000000000000046031320103431700316760ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Color; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT PaintContext for Gouraud Triangle Mesh (Type 4) shading. */ class Type4ShadingPaint implements Paint { private static final Logger LOG = LoggerFactory.getLogger(Type4ShadingPaint.class); private final PDShadingType4 shading; private final Matrix matrix; /** * Constructor. * * @param shading the shading resources * @param matrix the pattern matrix concatenated with that of the parent content stream */ Type4ShadingPaint(PDShadingType4 shading, Matrix matrix) { this.shading = shading; this.matrix = matrix; } @Override public int getTransparency() { return 0; } @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { try { return new Type4ShadingContext(shading, cm, xform, matrix, deviceBounds); } catch (IOException e) { LOG.error("An error occurred while painting", e); return new Color(0, 0, 0, 0).createContext(cm, deviceBounds, userBounds, xform, hints); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type5ShadingContext.java000066400000000000000000000125201320103431700322450ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.io.EOFException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDRange; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT PaintContext for Gouraud Triangle Lattice (Type 5) shading. * * @author Tilman Hausherr * @author Shaola Ren */ class Type5ShadingContext extends GouraudShadingContext { private static final Logger LOG = LoggerFactory.getLogger(Type5ShadingContext.class); /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param cm the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @throws IOException if something went wrong */ Type5ShadingContext(PDShadingType5 shading, ColorModel cm, AffineTransform xform, Matrix matrix, Rectangle deviceBounds) throws IOException { super(shading, cm, xform, matrix); LOG.debug("Type5ShadingContext"); setTriangleList(collectTriangles(shading, xform, matrix)); createPixelTable(deviceBounds); } private List collectTriangles(PDShadingType5 latticeTriangleShadingType, AffineTransform xform, Matrix matrix) throws IOException { COSDictionary cosDictionary = latticeTriangleShadingType.getCOSObject(); PDRange rangeX = latticeTriangleShadingType.getDecodeForParameter(0); PDRange rangeY = latticeTriangleShadingType.getDecodeForParameter(1); int numPerRow = latticeTriangleShadingType.getVerticesPerRow(); PDRange[] colRange = new PDRange[numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; ++i) { colRange[i] = latticeTriangleShadingType.getDecodeForParameter(2 + i); } List vlist = new ArrayList(); long maxSrcCoord = (long) Math.pow(2, bitsPerCoordinate) - 1; long maxSrcColor = (long) Math.pow(2, bitsPerColorComponent) - 1; COSStream cosStream = (COSStream) cosDictionary; try (ImageInputStream mciis = new MemoryCacheImageInputStream( cosStream.getUnfilteredStream())) { boolean eof = false; while (!eof) { Vertex p; try { p = readVertex(mciis, maxSrcCoord, maxSrcColor, rangeX, rangeY, colRange, matrix, xform); vlist.add(p); } catch (EOFException ex) { eof = true; } } } int sz = vlist.size(), rowNum = sz / numPerRow; Vertex[][] latticeArray = new Vertex[rowNum][numPerRow]; List list = new ArrayList(); if (rowNum < 2) { // must have at least two rows; if not, return empty list return list; } for (int i = 0; i < rowNum; i++) { for (int j = 0; j < numPerRow; j++) { latticeArray[i][j] = vlist.get(i * numPerRow + j); } } for (int i = 0; i < rowNum - 1; i++) { for (int j = 0; j < numPerRow - 1; j++) { Point2D[] ps = new Point2D[] { latticeArray[i][j].point, latticeArray[i][j + 1].point, latticeArray[i + 1][j].point }; float[][] cs = new float[][] { latticeArray[i][j].color, latticeArray[i][j + 1].color, latticeArray[i + 1][j].color }; list.add(new ShadedTriangle(ps, cs)); ps = new Point2D[] { latticeArray[i][j + 1].point, latticeArray[i + 1][j].point, latticeArray[i + 1][j + 1].point }; cs = new float[][] { latticeArray[i][j + 1].color, latticeArray[i + 1][j].color, latticeArray[i + 1][j + 1].color }; list.add(new ShadedTriangle(ps, cs)); } } return list; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type5ShadingPaint.java000066400000000000000000000045771320103431700317110ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Color; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT Paint for Gouraud Triangle Lattice (Type 5) shading. */ class Type5ShadingPaint implements Paint { private static final Logger LOG = LoggerFactory.getLogger(Type5ShadingPaint.class); private final PDShadingType5 shading; private final Matrix matrix; /** * Constructor. * * @param shading the shading resources * @param matrix the pattern matrix concatenated with that of the parent content stream */ Type5ShadingPaint(PDShadingType5 shading, Matrix matrix) { this.shading = shading; this.matrix = matrix; } @Override public int getTransparency() { return 0; } @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { try { return new Type5ShadingContext(shading, cm, xform, matrix, deviceBounds); } catch (IOException e) { LOG.error("An error occurred while painting", e); return new Color(0, 0, 0, 0).createContext(cm, deviceBounds, userBounds, xform, hints); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type6ShadingContext.java000066400000000000000000000036211320103431700322500ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; /** * AWT PaintContext for coons patch meshes (type 6) shading. This was done as * part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class Type6ShadingContext extends PatchMeshesShadingContext { /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param colorModel the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @param deviceBounds device bounds * @throws IOException if something went wrong */ Type6ShadingContext(PDShadingType6 shading, ColorModel colorModel, AffineTransform xform, Matrix matrix, Rectangle deviceBounds) throws IOException { super(shading, colorModel, xform, matrix, deviceBounds, 12); } @Override protected Patch generatePatch(Point2D[] points, float[][] color) { return new CoonsPatch(points, color); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type6ShadingPaint.java000066400000000000000000000044021320103431700316750ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Color; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT Paint for coons patch meshes (Type 6) shading. This was done as part of * GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class Type6ShadingPaint implements Paint { private static final Logger LOG = LoggerFactory.getLogger(Type6ShadingPaint.class); private final PDShadingType6 shading; private final Matrix matrix; /** * Constructor. * * @param shading the shading resources * @param matrix the pattern matrix concatenated with that of the parent content stream */ Type6ShadingPaint(PDShadingType6 shading, Matrix matrix) { this.shading = shading; this.matrix = matrix; } @Override public int getTransparency() { return 0; } @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { try { return new Type6ShadingContext(shading, cm, xform, matrix, deviceBounds); } catch (IOException e) { LOG.error("An error occurred while painting", e); return new Color(0, 0, 0, 0).createContext(cm, deviceBounds, userBounds, xform, hints); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type7ShadingContext.java000066400000000000000000000036331320103431700322540ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; /** * AWT PaintContext for tensor-product patch meshes (type 7) shading. This was * done as part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class Type7ShadingContext extends PatchMeshesShadingContext { /** * Constructor creates an instance to be used for fill operations. * * @param shading the shading type to be used * @param colorModel the color model to be used * @param xform transformation for user to device space * @param matrix the pattern matrix concatenated with that of the parent content stream * @param deviceBounds device bounds * @throws IOException if something went wrong */ Type7ShadingContext(PDShadingType7 shading, ColorModel colorModel, AffineTransform xform, Matrix matrix, Rectangle deviceBounds) throws IOException { super(shading, colorModel, xform, matrix, deviceBounds, 16); } @Override protected Patch generatePatch(Point2D[] points, float[][] color) { return new TensorPatch(points, color); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Type7ShadingPaint.java000066400000000000000000000044141320103431700317010ustar00rootroot00000000000000/* * Copyright 2014 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.Color; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.io.IOException; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT Paint for tensor-product patch meshes (Type 7) shading. This was done as * part of GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ class Type7ShadingPaint implements Paint { private static final Logger LOG = LoggerFactory.getLogger(Type7ShadingPaint.class); private final PDShadingType7 shading; private final Matrix matrix; /** * Constructor. * * @param shading the shading resources * @param matrix the pattern matrix concatenated with that of the parent content stream */ Type7ShadingPaint(PDShadingType7 shading, Matrix matrix) { this.shading = shading; this.matrix = matrix; } @Override public int getTransparency() { return 0; } @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { try { return new Type7ShadingContext(shading, cm, xform, matrix, deviceBounds); } catch (IOException e) { LOG.error("An error occurred while painting", e); return new Color(0, 0, 0, 0).createContext(cm, deviceBounds, userBounds, xform, hints); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/shading/Vertex.java000066400000000000000000000027171320103431700276600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.shading; import java.awt.geom.Point2D; /** * Vertex for Type 4 and Type 5 shadings. * * @author Tilman Hausherr */ class Vertex { public Point2D point; public float[] color; Vertex(Point2D p, float[] c) { point = p; color = c.clone(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (float f : color) { if (sb.length() > 0) { sb.append(' '); } sb.append(String.format("%3.2f", f)); } return "Vertex{ " + point + ", colors=[" + sb + "] }"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/state/000077500000000000000000000000001320103431700252345ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/state/PDExtendedGraphicsState.java000066400000000000000000000447351320103431700325620ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.state; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.graphics.PDFontSetting; import org.sejda.sambox.pdmodel.graphics.PDLineDashPattern; import org.sejda.sambox.pdmodel.graphics.blend.BlendMode; /** * An extended graphics state dictionary. * * @author Ben Litchfield */ public class PDExtendedGraphicsState implements COSObjectable { private final COSDictionary dict; /** * Default constructor, creates blank graphics state. */ public PDExtendedGraphicsState() { dict = new COSDictionary(); dict.setItem(COSName.TYPE, COSName.EXT_G_STATE); } /** * Create a graphics state from an existing dictionary. * * @param dictionary The existing graphics state. */ public PDExtendedGraphicsState(COSDictionary dictionary) { dict = dictionary; } /** * This will implement the gs operator. * * @param gs The state to copy this dictionaries values into. * * @throws IOException If there is an error copying font information. */ public void copyIntoGraphicsState(PDGraphicsState gs) throws IOException { for (COSName key : dict.keySet()) { if (key.equals(COSName.LW)) { gs.setLineWidth(getLineWidth()); } else if (key.equals(COSName.LC)) { gs.setLineCap(getLineCapStyle()); } else if (key.equals(COSName.LJ)) { gs.setLineJoin(getLineJoinStyle()); } else if (key.equals(COSName.ML)) { gs.setMiterLimit(getMiterLimit()); } else if (key.equals(COSName.D)) { gs.setLineDashPattern(getLineDashPattern()); } else if (key.equals(COSName.RI)) { gs.setRenderingIntent(getRenderingIntent()); } else if (key.equals(COSName.OPM)) { gs.setOverprintMode(getOverprintMode().doubleValue()); } else if (key.equals(COSName.FONT)) { PDFontSetting setting = getFontSetting(); if (setting != null) { gs.getTextState().setFont(setting.getFont()); gs.getTextState().setFontSize(setting.getFontSize()); } } else if (key.equals(COSName.FL)) { gs.setFlatness(getFlatnessTolerance()); } else if (key.equals(COSName.SM)) { gs.setSmoothness(getSmoothnessTolerance()); } else if (key.equals(COSName.SA)) { gs.setStrokeAdjustment(getAutomaticStrokeAdjustment()); } else if (key.equals(COSName.CA)) { gs.setAlphaConstant(getStrokingAlphaConstant()); } else if (key.equals(COSName.CA_NS)) { gs.setNonStrokeAlphaConstants(getNonStrokingAlphaConstant()); } else if (key.equals(COSName.AIS)) { gs.setAlphaSource(getAlphaSourceFlag()); } else if (key.equals(COSName.TK)) { gs.getTextState().setKnockoutFlag(getTextKnockoutFlag()); } else if (key.equals(COSName.SMASK)) { PDSoftMask softmask = getSoftMask(); if (softmask != null) { // Softmask must know the CTM at the time the ExtGState is activated. Read // https://bugs.ghostscript.com/show_bug.cgi?id=691157#c7 for a good explanation. softmask.setInitialTransformationMatrix( gs.getCurrentTransformationMatrix().clone()); } gs.setSoftMask(softmask); } else if (key.equals(COSName.BM)) { gs.setBlendMode(getBlendMode()); } else if (key.equals(COSName.TR)) { if (dict.containsKey(COSName.TR2)) { // "If both TR and TR2 are present in the same graphics state parameter dictionary, // TR2 shall take precedence." continue; } gs.setTransfer(getTransfer()); } else if (key.equals(COSName.TR2)) { gs.setTransfer(getTransfer2()); } } } /** * This will get the underlying dictionary that this class acts on. * * @return The underlying dictionary for this class. */ @Override public COSDictionary getCOSObject() { return dict; } /** * This will get the line width. This will return null if there is no line width * * @return null or the LW value of the dictionary. */ public Float getLineWidth() { return getFloatItem(COSName.LW); } /** * This will set the line width. * * @param width The line width for the object. */ public void setLineWidth(Float width) { setFloatItem(COSName.LW, width); } /** * This will get the line cap style. * * @return null or the LC value of the dictionary. */ public int getLineCapStyle() { return dict.getInt(COSName.LC); } /** * This will set the line cap style for the graphics state. * * @param style The new line cap style to set. */ public void setLineCapStyle(int style) { dict.setInt(COSName.LC, style); } /** * This will get the line join style. * * @return null or the LJ value in the dictionary. */ public int getLineJoinStyle() { return dict.getInt(COSName.LJ); } /** * This will set the line join style. * * @param style The new line join style. */ public void setLineJoinStyle(int style) { dict.setInt(COSName.LJ, style); } /** * This will get the miter limit. * * @return null or the ML value in the dictionary. */ public Float getMiterLimit() { return getFloatItem(COSName.ML); } /** * This will set the miter limit for the graphics state. * * @param miterLimit The new miter limit value */ public void setMiterLimit(Float miterLimit) { setFloatItem(COSName.ML, miterLimit); } /** * This will get the dash pattern. * * @return null or the D value in the dictionary. */ public PDLineDashPattern getLineDashPattern() { PDLineDashPattern retval = null; COSArray dp = (COSArray) dict.getDictionaryObject(COSName.D); if (dp != null) { COSArray array = new COSArray(); dp.addAll(dp); dp.remove(dp.size() - 1); int phase = dp.getInt(dp.size() - 1); retval = new PDLineDashPattern(array, phase); } return retval; } /** * This will set the dash pattern for the graphics state. * * @param dashPattern The dash pattern */ public void setLineDashPattern(PDLineDashPattern dashPattern) { dict.setItem(COSName.D, dashPattern.getCOSObject()); } /** * This will get the rendering intent. * * @return null or the RI value in the dictionary. */ public RenderingIntent getRenderingIntent() { String ri = dict.getNameAsString("RI"); if (ri != null) { return RenderingIntent.fromString(ri); } return null; } /** * This will set the rendering intent for the graphics state. * * @param ri The new rendering intent */ public void setRenderingIntent(String ri) { dict.setName("RI", ri); } /** * This will get the overprint control. * * @return The overprint control or null if one has not been set. */ public boolean getStrokingOverprintControl() { return dict.getBoolean(COSName.OP, false); } /** * This will get the overprint control(OP). * * @param op The overprint control. */ public void setStrokingOverprintControl(boolean op) { dict.setBoolean(COSName.OP, op); } /** * This will get the overprint control for non stroking operations. If this value is null then the regular overprint * control value will be returned. * * @return The overprint control or null if one has not been set. */ public boolean getNonStrokingOverprintControl() { return dict.getBoolean(COSName.OP_NS, getStrokingOverprintControl()); } /** * This will get the overprint control(OP). * * @param op The overprint control. */ public void setNonStrokingOverprintControl(boolean op) { dict.setBoolean(COSName.OP_NS, op); } /** * This will get the overprint control mode. * * @return The overprint control mode or null if one has not been set. */ public Float getOverprintMode() { return getFloatItem(COSName.OPM); } /** * This will get the overprint mode(OPM). * * @param overprintMode The overprint mode */ public void setOverprintMode(Float overprintMode) { setFloatItem(COSName.OPM, overprintMode); } /** * This will get the font setting of the graphics state. * * @return The font setting. */ public PDFontSetting getFontSetting() { PDFontSetting setting = null; COSBase base = dict.getDictionaryObject(COSName.FONT); if (base instanceof COSArray) { COSArray font = (COSArray) base; setting = new PDFontSetting(font); } return setting; } /** * This will set the font setting for this graphics state. * * @param fs The new font setting. */ public void setFontSetting(PDFontSetting fs) { dict.setItem(COSName.FONT, fs); } /** * This will get the flatness tolerance. * * @return The flatness tolerance or null if one has not been set. */ public Float getFlatnessTolerance() { return getFloatItem(COSName.FL); } /** * This will get the flatness tolerance. * * @param flatness The new flatness tolerance */ public void setFlatnessTolerance(Float flatness) { setFloatItem(COSName.FL, flatness); } /** * This will get the smothness tolerance. * * @return The smothness tolerance or null if one has not been set. */ public Float getSmoothnessTolerance() { return getFloatItem(COSName.SM); } /** * This will get the smoothness tolerance. * * @param smoothness The new smoothness tolerance */ public void setSmoothnessTolerance(Float smoothness) { setFloatItem(COSName.SM, smoothness); } /** * This will get the automatic stroke adjustment flag. * * @return The automatic stroke adjustment flag or null if one has not been set. */ public boolean getAutomaticStrokeAdjustment() { return dict.getBoolean(COSName.SA, false); } /** * This will get the automatic stroke adjustment flag. * * @param sa The new automatic stroke adjustment flag. */ public void setAutomaticStrokeAdjustment(boolean sa) { dict.setBoolean(COSName.SA, sa); } /** * This will get the stroking alpha constant. * * @return The stroking alpha constant or null if one has not been set. */ public Float getStrokingAlphaConstant() { return getFloatItem(COSName.CA); } /** * This will get the stroking alpha constant. * * @param alpha The new stroking alpha constant. */ public void setStrokingAlphaConstant(Float alpha) { setFloatItem(COSName.CA, alpha); } /** * This will get the non stroking alpha constant. * * @return The non stroking alpha constant or null if one has not been set. */ public Float getNonStrokingAlphaConstant() { return getFloatItem(COSName.CA_NS); } /** * This will get the non stroking alpha constant. * * @param alpha The new non stroking alpha constant. */ public void setNonStrokingAlphaConstant(Float alpha) { setFloatItem(COSName.CA_NS, alpha); } /** * This will get the alpha source flag (“alpha is shape”), that specifies whether the current soft mask and alpha * constant shall be interpreted as shape values (true) or opacity values (false). * * @return The alpha source flag. */ public boolean getAlphaSourceFlag() { return dict.getBoolean(COSName.AIS, false); } /** * This will get the alpha source flag (“alpha is shape”), that specifies whether the current soft mask and alpha * constant shall be interpreted as shape values (true) or opacity values (false). * * @param alpha The alpha source flag. */ public void setAlphaSourceFlag(boolean alpha) { dict.setBoolean(COSName.AIS, alpha); } /** * Returns the blending mode stored in the COS dictionary * * @return the blending mode */ public BlendMode getBlendMode() { return BlendMode.getInstance(dict.getDictionaryObject(COSName.BM)); } /** * Returns the soft mask stored in the COS dictionaryO * * @return the soft mask */ public PDSoftMask getSoftMask() { return PDSoftMask.create(dict.getDictionaryObject(COSName.SMASK)); } /** * * /** This will get the text knockout flag. * * @return The text knockout flag. */ public boolean getTextKnockoutFlag() { return dict.getBoolean(COSName.TK, true); } /** * This will get the text knockout flag. * * @param tk The text knockout flag. */ public void setTextKnockoutFlag(boolean tk) { dict.setBoolean(COSName.TK, tk); } /** * This will get a float item from the dictionary. * * @param key The key to the item. * * @return The value for that item. */ private Float getFloatItem(COSName key) { Float retval = null; COSNumber value = (COSNumber) dict.getDictionaryObject(key); if (value != null) { retval = value.floatValue(); } return retval; } /** * This will set a float object. * * @param key The key to the data that we are setting. * @param value The value that we are setting. */ private void setFloatItem(COSName key, Float value) { if (value == null) { dict.removeItem(key); } else { dict.setItem(key, new COSFloat(value)); } } /** * This will get the transfer function of the /TR dictionary. * * @return The transfer function. According to the PDF specification, this is either a single function (which * applies to all process colorants) or an array of four functions (which apply to the process colorants * individually). The name Identity may be used to represent the identity function. */ public COSBase getTransfer() { COSBase base = dict.getDictionaryObject(COSName.TR); if (base instanceof COSArray && ((COSArray) base).size() != 4) { return null; } return base; } /** * This will set the transfer function of the /TR dictionary. * * @param transfer The transfer function. According to the PDF specification, this is either a single function * (which applies to all process colorants) or an array of four functions (which apply to the process colorants * individually). The name Identity may be used to represent the identity function. */ public void setTransfer(COSBase transfer) { dict.setItem(COSName.TR, transfer); } /** * This will get the transfer function of the /TR2 dictionary. * * @return The transfer function. According to the PDF specification, this is either a single function (which * applies to all process colorants) or an array of four functions (which apply to the process colorants * individually). The name Identity may be used to represent the identity function, and the name Default denotes the * transfer function that was in effect at the start of the page. */ public COSBase getTransfer2() { COSBase base = dict.getDictionaryObject(COSName.TR2); if (base instanceof COSArray && ((COSArray) base).size() != 4) { return null; } return base; } /** * This will set the transfer function of the /TR2 dictionary. * * @param transfer2 The transfer function. According to the PDF specification, this is either a single function * (which applies to all process colorants) or an array of four functions (which apply to the process colorants * individually). The name Identity may be used to represent the identity function, and the name Default denotes the * transfer function that was in effect at the start of the page. */ public void setTransfer2(COSBase transfer2) { dict.setItem(COSName.TR2, transfer2); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/state/PDGraphicsState.java000066400000000000000000000364311320103431700310730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.state; import java.awt.BasicStroke; import java.awt.Composite; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.graphics.PDLineDashPattern; import org.sejda.sambox.pdmodel.graphics.blend.BlendComposite; import org.sejda.sambox.pdmodel.graphics.blend.BlendMode; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.util.Matrix; /** * The current state of the graphics parameters when executing a content stream. * * @author Ben Litchfield */ public class PDGraphicsState implements Cloneable { private boolean isClippingPathDirty; private Area clippingPath; private Matrix currentTransformationMatrix = new Matrix(); private PDColor strokingColor = PDDeviceGray.INSTANCE.getInitialColor(); private PDColor nonStrokingColor = PDDeviceGray.INSTANCE.getInitialColor(); private PDColorSpace strokingColorSpace = PDDeviceGray.INSTANCE; private PDColorSpace nonStrokingColorSpace = PDDeviceGray.INSTANCE; private PDTextState textState = new PDTextState(); private float lineWidth = 1; private int lineCap = BasicStroke.CAP_BUTT; private int lineJoin = BasicStroke.JOIN_MITER; private float miterLimit = 10; private PDLineDashPattern lineDashPattern = new PDLineDashPattern(); private RenderingIntent renderingIntent; private boolean strokeAdjustment = false; private BlendMode blendMode = BlendMode.COMPATIBLE; private PDSoftMask softMask; private double alphaConstant = 1.0; private double nonStrokingAlphaConstant = 1.0; private boolean alphaSource = false; // DEVICE-DEPENDENT parameters private boolean overprint = false; private double overprintMode = 0; //black generation //undercolor removal private COSBase transfer = null; //halftone private double flatness = 1.0; private double smoothness = 0; /** * Constructor with a given page size to initialize the clipping path. * @param page the size of the page */ public PDGraphicsState(PDRectangle page) { clippingPath = new Area(page.toGeneralPath()); } /** * Get the value of the CTM. * * @return The current transformation matrix. */ public Matrix getCurrentTransformationMatrix() { return currentTransformationMatrix; } /** * Set the value of the CTM. * * @param value The current transformation matrix. */ public void setCurrentTransformationMatrix(Matrix value) { currentTransformationMatrix = value; } /** * Get the value of the line width. * * @return The current line width. */ public float getLineWidth() { return lineWidth; } /** * set the value of the line width. * * @param value The current line width. */ public void setLineWidth(float value) { lineWidth = value; } /** * Get the value of the line cap. * * @return The current line cap. */ public int getLineCap() { return lineCap; } /** * set the value of the line cap. * * @param value The current line cap. */ public void setLineCap(int value) { lineCap = value; } /** * Get the value of the line join. * * @return The current line join value. */ public int getLineJoin() { return lineJoin; } /** * Get the value of the line join. * * @param value The current line join */ public void setLineJoin(int value) { lineJoin = value; } /** * Get the value of the miter limit. * * @return The current miter limit. */ public float getMiterLimit() { return miterLimit; } /** * set the value of the miter limit. * * @param value The current miter limit. */ public void setMiterLimit(float value) { miterLimit = value; } /** * Get the value of the stroke adjustment parameter. * * @return The current stroke adjustment. */ public boolean isStrokeAdjustment() { return strokeAdjustment; } /** * set the value of the stroke adjustment. * * @param value The value of the stroke adjustment parameter. */ public void setStrokeAdjustment(boolean value) { strokeAdjustment = value; } /** * Get the value of the stroke alpha constants property. * * @return The value of the stroke alpha constants parameter. */ public double getAlphaConstant() { return alphaConstant; } /** * set the value of the stroke alpha constants property. * * @param value The value of the stroke alpha constants parameter. */ public void setAlphaConstant(double value) { alphaConstant = value; } /** * Get the value of the non-stroke alpha constants property. * * @return The value of the non-stroke alpha constants parameter. */ public double getNonStrokeAlphaConstants() { return nonStrokingAlphaConstant; } /** * set the value of the non-stroke alpha constants property. * * @param value The value of the non-stroke alpha constants parameter. */ public void setNonStrokeAlphaConstants(double value) { nonStrokingAlphaConstant = value; } /** * get the value of the stroke alpha source property. * * @return The value of the stroke alpha source parameter. */ public boolean isAlphaSource() { return alphaSource; } /** * set the value of the alpha source property. * * @param value The value of the alpha source parameter. */ public void setAlphaSource(boolean value) { alphaSource = value; } /** * returns the current softmask * * @return softMask */ public PDSoftMask getSoftMask() { return softMask; } /** * Sets the current soft mask * * @param softMask */ public void setSoftMask(PDSoftMask softMask) { this.softMask = softMask; } /** * Returns the current blend mode * * @return the current blend mode */ public BlendMode getBlendMode() { return blendMode; } /** * Sets the blend mode in the current graphics state * * @param blendMode */ public void setBlendMode(BlendMode blendMode) { this.blendMode = blendMode; } /** /** * get the value of the overprint property. * * @return The value of the overprint parameter. */ public boolean isOverprint() { return overprint; } /** * set the value of the overprint property. * * @param value The value of the overprint parameter. */ public void setOverprint(boolean value) { overprint = value; } /** * get the value of the overprint mode property. * * @return The value of the overprint mode parameter. */ public double getOverprintMode() { return overprintMode; } /** * set the value of the overprint mode property. * * @param value The value of the overprint mode parameter. */ public void setOverprintMode(double value) { overprintMode = value; } /** * get the value of the flatness property. * * @return The value of the flatness parameter. */ public double getFlatness() { return flatness; } /** * set the value of the flatness property. * * @param value The value of the flatness parameter. */ public void setFlatness(double value) { flatness = value; } /** * get the value of the smoothness property. * * @return The value of the smoothness parameter. */ public double getSmoothness() { return smoothness; } /** * set the value of the smoothness property. * * @param value The value of the smoothness parameter. */ public void setSmoothness(double value) { smoothness = value; } /** * This will get the graphics text state. * * @return The graphics text state. */ public PDTextState getTextState() { return textState; } /** * This will set the graphics text state. * * @param value The graphics text state. */ public void setTextState(PDTextState value) { textState = value; } /** * This will get the current line dash pattern. * * @return The line dash pattern. */ public PDLineDashPattern getLineDashPattern() { return lineDashPattern; } /** * This will set the current line dash pattern. * * @param value The new line dash pattern. */ public void setLineDashPattern(PDLineDashPattern value) { lineDashPattern = value; } /** * This will get the rendering intent. * * @see PDExtendedGraphicsState * * @return The rendering intent */ public RenderingIntent getRenderingIntent() { return renderingIntent; } /** * This will set the rendering intent. * * @param value The new rendering intent. */ public void setRenderingIntent(RenderingIntent value) { renderingIntent = value; } @Override public PDGraphicsState clone() { try { PDGraphicsState clone = (PDGraphicsState)super.clone(); clone.textState = textState.clone(); clone.currentTransformationMatrix = currentTransformationMatrix.clone(); clone.strokingColor = strokingColor; // immutable clone.nonStrokingColor = nonStrokingColor; // immutable clone.lineDashPattern = lineDashPattern; // immutable clone.clippingPath = clippingPath; // not cloned, see intersectClippingPath clone.isClippingPathDirty = false; return clone; } catch (CloneNotSupportedException e) { // should not happen throw new RuntimeException(e); } } /** * Returns the stroking color. * * @return stroking color */ public PDColor getStrokingColor() { return strokingColor; } /** * Sets the stroking color. * * @param color The new stroking color */ public void setStrokingColor(PDColor color) { strokingColor = color; } /** * Returns the non-stroking color. * * @return The non-stroking color */ public PDColor getNonStrokingColor() { return nonStrokingColor; } /** * Sets the non-stroking color. * * @param color The new non-stroking color */ public void setNonStrokingColor(PDColor color) { nonStrokingColor = color; } /** * Returns the stroking color space. * * @return The stroking color space. */ public PDColorSpace getStrokingColorSpace() { return strokingColorSpace; } /** * Sets the the stroking color space. * * @param colorSpace The new stroking color space. */ public void setStrokingColorSpace(PDColorSpace colorSpace) { strokingColorSpace = colorSpace; } /** * Returns the non-stroking color space. * * @return The non-stroking color space. */ public PDColorSpace getNonStrokingColorSpace() { return nonStrokingColorSpace; } /** * Sets the the non-stroking color space. * * @param colorSpace The new non-stroking color space. */ public void setNonStrokingColorSpace(PDColorSpace colorSpace) { nonStrokingColorSpace = colorSpace; } /** * Modify the current clipping path by intersecting it with the given path. * @param path path to intersect with the clipping path */ public void intersectClippingPath(GeneralPath path) { intersectClippingPath(new Area(path)); } /** * Modify the current clipping path by intersecting it with the given path. * @param area area to intersect with the clipping path */ public void intersectClippingPath(Area area) { // lazy cloning of clipping path for performance if (!isClippingPathDirty) { // deep copy (can't use clone() as it performs only a shallow copy) Area cloned = new Area(); cloned.add(clippingPath); clippingPath = cloned; isClippingPathDirty = true; } // intersection as usual clippingPath.intersect(area); } /** * This will get the current clipping path. Do not modify this Area object! * * @return The current clipping path. */ public Area getCurrentClippingPath() { return clippingPath; } public Composite getStrokingJavaComposite() { return BlendComposite.getInstance(blendMode, (float) alphaConstant); } public Composite getNonStrokingJavaComposite() { return BlendComposite.getInstance(blendMode, (float) nonStrokingAlphaConstant); } /** * This will get the transfer function. * * @return The transfer function. According to the PDF specification, this is either a single function (which * applies to all process colorants) or an array of four functions (which apply to the process colorants * individually). The name Identity may be used to represent the identity function, and the name Default denotes the * transfer function that was in effect at the start of the page. */ public COSBase getTransfer() { return transfer; } /** * This will set the transfer function. * * @param transfer The transfer function. According to the PDF specification, this is either a single function * (which applies to all process colorants) or an array of four functions (which apply to the process colorants * individually). The name Identity may be used to represent the identity function, and the name Default denotes the * transfer function that was in effect at the start of the page. */ public void setTransfer(COSBase transfer) { this.transfer = transfer; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/state/PDSoftMask.java000066400000000000000000000112761320103431700300610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.state; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.common.function.PDFunction; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.form.PDTransparencyGroup; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Soft mask. * * @author Kühn & Weyh Software, GmbH */ public final class PDSoftMask implements COSObjectable { private static final Logger LOG = LoggerFactory.getLogger(PDSoftMask.class); /** * Creates a new soft mask. * * @param dictionary SMask */ public static PDSoftMask create(COSBase dictionary) { if (dictionary instanceof COSName) { if (COSName.NONE.equals(dictionary)) { return null; } LOG.warn("Invalid SMask " + dictionary); return null; } else if (dictionary instanceof COSDictionary) { return new PDSoftMask((COSDictionary) dictionary); } else { LOG.warn("Invalid SMask " + dictionary); return null; } } private final COSDictionary dictionary; private COSName subType = null; private PDTransparencyGroup group = null; private COSArray backdropColor = null; private PDFunction transferFunction = null; /** * To allow a soft mask to know the CTM at the time of activation of the ExtGState. */ private Matrix ctm; /** * Creates a new soft mask. */ public PDSoftMask(COSDictionary dictionary) { super(); this.dictionary = dictionary; } @Override public COSDictionary getCOSObject() { return dictionary; } /** * Returns the subtype of the soft mask (Alpha, Luminosity) - S entry */ public COSName getSubType() { if (subType == null) { subType = (COSName) getCOSObject().getDictionaryObject(COSName.S); } return subType; } /** * Returns the G entry of the soft mask object * * @return form containing the transparency group * @throws IOException */ public PDTransparencyGroup getGroup() throws IOException { if (group == null) { COSBase cosGroup = getCOSObject().getDictionaryObject(COSName.G); if (cosGroup != null) { group = (PDTransparencyGroup) PDXObject.createXObject(cosGroup, null); } } return group; } /** * Returns the backdrop color. */ public COSArray getBackdropColor() { if (backdropColor == null) { backdropColor = getCOSObject().getDictionaryObject(COSName.BC, COSArray.class); } return backdropColor; } /** * Returns the transfer function. * * @throws IOException If we are unable to create the PDFunction object. */ public PDFunction getTransferFunction() throws IOException { if (transferFunction == null) { COSBase cosTF = getCOSObject().getDictionaryObject(COSName.TR); if (cosTF != null) { transferFunction = PDFunction.create(cosTF); } } return transferFunction; } /** * Set the CTM that is valid at the time the ExtGState was activated. * * @param ctm */ void setInitialTransformationMatrix(Matrix ctm) { this.ctm = ctm; } /** * Returns the CTM at the time the ExtGState was activated. * * @return */ public Matrix getInitialTransformationMatrix() { return ctm; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/state/PDTextState.java000066400000000000000000000117101320103431700302500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.state; import org.sejda.sambox.pdmodel.font.PDFont; /** * This class will hold the current state of the text parameters when executing a * content stream. * * @author Ben Litchfield */ public class PDTextState implements Cloneable { private float characterSpacing = 0; private float wordSpacing = 0; private float horizontalScaling = 100; private float leading = 0; private PDFont font; private float fontSize; private RenderingMode renderingMode = RenderingMode.FILL; private float rise = 0; private boolean knockout = true; /** * Get the value of the characterSpacing. * * @return The current characterSpacing. */ public float getCharacterSpacing() { return characterSpacing; } /** * Set the value of the characterSpacing. * * @param value The characterSpacing. */ public void setCharacterSpacing(float value) { characterSpacing = value; } /** * Get the value of the wordSpacing. * * @return The wordSpacing. */ public float getWordSpacing() { return wordSpacing; } /** * Set the value of the wordSpacing. * * @param value The wordSpacing. */ public void setWordSpacing(float value) { wordSpacing = value; } /** * Get the value of the horizontalScaling. The default is 100. This value * is the percentage value 0-100 and not 0-1. So for mathematical operations * you will probably need to divide by 100 first. * * @return The horizontalScaling. */ public float getHorizontalScaling() { return horizontalScaling; } /** * Set the value of the horizontalScaling. * * @param value The horizontalScaling. */ public void setHorizontalScaling(float value) { horizontalScaling = value; } /** * Get the value of the leading. * * @return The leading. */ public float getLeading() { return leading; } /** * Set the value of the leading. * * @param value The leading. */ public void setLeading(float value) { leading = value; } /** * Get the value of the font. * * @return The font. */ public PDFont getFont() { return font; } /** * Set the value of the font. * * @param value The font. */ public void setFont(PDFont value) { font = value; } /** * Get the value of the fontSize. * * @return The fontSize. */ public float getFontSize() { return fontSize; } /** * Set the value of the fontSize. * * @param value The fontSize. */ public void setFontSize(float value) { fontSize = value; } /** * Get the value of the renderingMode. * * @return The renderingMode. */ public RenderingMode getRenderingMode() { return renderingMode; } /** * Set the value of the renderingMode. * * @param renderingMode The renderingMode. */ public void setRenderingMode(RenderingMode renderingMode) { this.renderingMode = renderingMode; } /** * Get the value of the rise. * * @return The rise. */ public float getRise() { return rise; } /** * Set the value of the rise. * * @param value The rise. */ public void setRise(float value) { rise = value; } /** * Get the value of the knockout. * * @return The knockout. */ public boolean getKnockoutFlag() { return knockout; } /** * Set the value of the knockout. * * @param value The knockout. */ public void setKnockoutFlag(boolean value) { knockout = value; } @Override public PDTextState clone() { try { return (PDTextState)super.clone(); } catch (CloneNotSupportedException e) { // should not happen throw new RuntimeException(e); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/state/RenderingIntent.java000066400000000000000000000042151320103431700312000ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.state; /** * Rendering intent. * * @author John Hewson */ public enum RenderingIntent { /** * Absolute Colorimetric. */ ABSOLUTE_COLORIMETRIC("AbsoluteColorimetric"), /** * Relative Colorimetric. */ RELATIVE_COLORIMETRIC("RelativeColorimetric"), /** * Saturation. */ SATURATION("Saturation"), /** * Perceptual. */ PERCEPTUAL("Perceptual"); public static RenderingIntent fromString(String value) { if (value.equals("AbsoluteColorimetric")) { return ABSOLUTE_COLORIMETRIC; } else if (value.equals("RelativeColorimetric")) { return RELATIVE_COLORIMETRIC; } else if (value.equals("Saturation")) { return SATURATION; } else if (value.equals("Perceptual")) { return PERCEPTUAL; } // "If a conforming reader does not recognize the specified name, // it shall use the RelativeColorimetric intent by default." return RELATIVE_COLORIMETRIC; } private final String value; RenderingIntent(String value) { this.value = value; } /** * Returns the string value, as used in a PDF file. */ public String stringValue() { return value; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/graphics/state/RenderingMode.java000066400000000000000000000050601320103431700306220ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.graphics.state; /** * Text Rendering Mode. * * @author John Hewson */ public enum RenderingMode { /** * Fill text. */ FILL(0), /** * Stroke text. */ STROKE(1), /** * Fill, then stroke text. */ FILL_STROKE(2), /** * Neither fill nor stroke text (invisible) */ NEITHER(3), /** * Fill text and add to path for clipping. */ FILL_CLIP(4), /** * Stroke text and add to path for clipping. */ STROKE_CLIP(5), /** * Fill, then stroke text and add to path for clipping. */ FILL_STROKE_CLIP(6), /** * Add text to path for clipping. */ NEITHER_CLIP(7); private static final RenderingMode[] VALUES = RenderingMode.values(); public static RenderingMode fromInt(int value) { return VALUES[value]; } private final int value; RenderingMode(int value) { this.value = value; } /** * Returns the integer value of this mode, as used in a PDF file. */ public int intValue() { return value; } /** * Returns true is this mode fills text. */ public boolean isFill() { return this == FILL || this == FILL_STROKE || this == FILL_CLIP || this == FILL_STROKE_CLIP; } /** * Returns true is this mode strokes text. */ public boolean isStroke() { return this == STROKE || this == FILL_STROKE || this == STROKE_CLIP || this == FILL_STROKE_CLIP; } /** * Returns true is this mode clips text. */ public boolean isClip() { return this == FILL_CLIP || this == STROKE_CLIP || this == FILL_STROKE_CLIP || this == NEITHER_CLIP; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/000077500000000000000000000000001320103431700246315ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/000077500000000000000000000000001320103431700261065ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDAction.java000066400000000000000000000113571320103431700304210ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDestinationOrAction; /** * This represents an action that can be executed in a PDF document. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public abstract class PDAction implements PDDestinationOrAction { /** * The type of PDF object. */ public static final String TYPE = "Action"; /** * The action dictionary. */ protected COSDictionary action; /** * Default constructor. */ public PDAction() { action = new COSDictionary(); setType( TYPE ); } /** * Constructor. * * @param a The action dictionary. */ public PDAction( COSDictionary a ) { action = a; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return action; } /** * This will get the type of PDF object that the actions dictionary describes. * If present must be Action for an action dictionary. * * @return The Type of PDF object. */ public String getType() { return action.getNameAsString( COSName.TYPE ); } /** * This will set the type of PDF object that the actions dictionary describes. * If present must be Action for an action dictionary. * * @param type The new Type for the PDF object. */ public final void setType( String type ) { action.setName(COSName.TYPE, type ); } /** * This will get the type of action that the actions dictionary describes. * If present, must be Action for an action dictionary. * * @return The S entry of actions dictionary. */ public String getSubType() { return action.getNameAsString(COSName.S); } /** * This will set the type of action that the actions dictionary describes. * If present, must be Action for an action dictionary. * * @param s The new type of action. */ public void setSubType( String s ) { action.setName(COSName.S, s); } /** * This will get the next action, or sequence of actions, to be performed after this one. * The value is either a single action dictionary or an array of action dictionaries * to be performed in order. * * @return The Next action or sequence of actions. */ public List getNext() { List retval = null; COSBase next = action.getDictionaryObject(COSName.NEXT); if( next instanceof COSDictionary ) { PDAction pdAction = PDActionFactory.createAction( (COSDictionary) next ); retval = new COSArrayList(pdAction, next, action, COSName.NEXT); } else if( next instanceof COSArray ) { COSArray array = (COSArray)next; List actions = new ArrayList(); for( int i=0; i( actions, array ); } return retval; } /** * This will set the next action, or sequence of actions, to be performed after this one. * The value is either a single action dictionary or an array of action dictionaries * to be performed in order. * * @param next The Next action or sequence of actions. */ public void setNext( List next ) { action.setItem(COSName.NEXT, COSArrayList.converterToCOSArray(next)); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionFactory.java000066400000000000000000000064601320103431700317500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This class will take a dictionary and determine which type of action to create. * * @author Ben Litchfield * */ public final class PDActionFactory { /** * Utility Class. */ private PDActionFactory() { // utility class } /** * This will create the correct type of action based on the type specified in the dictionary. * * @param action An action dictionary. * * @return An action of the correct type. */ public static PDAction createAction(COSDictionary action) { if (action != null) { String type = action.getNameAsString(COSName.S); if (PDActionJavaScript.SUB_TYPE.equals(type)) { return new PDActionJavaScript(action); } if (PDActionGoTo.SUB_TYPE.equals(type)) { return new PDActionGoTo(action); } if (PDActionLaunch.SUB_TYPE.equals(type)) { return new PDActionLaunch(action); } if (PDActionRemoteGoTo.SUB_TYPE.equals(type)) { return new PDActionRemoteGoTo(action); } if (PDActionURI.SUB_TYPE.equals(type)) { return new PDActionURI(action); } if (PDActionNamed.SUB_TYPE.equals(type)) { return new PDActionNamed(action); } if (PDActionSound.SUB_TYPE.equals(type)) { return new PDActionSound(action); } if (PDActionMovie.SUB_TYPE.equals(type)) { return new PDActionMovie(action); } if (PDActionImportData.SUB_TYPE.equals(type)) { return new PDActionImportData(action); } if (PDActionResetForm.SUB_TYPE.equals(type)) { return new PDActionResetForm(action); } if (PDActionHide.SUB_TYPE.equals(type)) { return new PDActionHide(action); } if (PDActionSubmitForm.SUB_TYPE.equals(type)) { return new PDActionSubmitForm(action); } if (PDActionThread.SUB_TYPE.equals(type)) { return new PDActionThread(action); } } return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionGoTo.java000066400000000000000000000057641320103431700312170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDDestination; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDPageDestination; /** * This represents a go-to action that can be executed in a PDF document. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public class PDActionGoTo extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "GoTo"; /** * Default constructor. */ public PDActionGoTo() { super(); setSubType( SUB_TYPE ); } /** * Constructor. * * @param a The action dictionary. */ public PDActionGoTo( COSDictionary a ) { super( a ); } /** * This will get the destination to jump to. * * @return The D entry of the specific go-to action dictionary. * * @throws IOException If there is an error creating the destination. */ public PDDestination getDestination() throws IOException { return PDDestination.create(getCOSObject().getDictionaryObject(COSName.D)); } /** * This will set the destination to jump to. * * @param d The destination. * * @IllegalArgumentException if the destination is not a page dictionary object. */ public void setDestination( PDDestination d ) { if (d instanceof PDPageDestination) { PDPageDestination pageDest = (PDPageDestination) d; COSArray destArray = pageDest.getCOSObject(); if (destArray.size() >= 1) { COSBase page = destArray.getObject(0); if (!(page instanceof COSDictionary)) { throw new IllegalArgumentException("Destination of a GoTo action must be " + "a page dictionary object"); } } } getCOSObject().setItem(COSName.D, d); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionHide.java000066400000000000000000000045071320103431700312120ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This represents a thread action that can be executed in a PDF document. * * @author Evgeniy Muravitskiy */ public class PDActionHide extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "Hide"; /** * Default Constructor */ public PDActionHide() { setSubType(SUB_TYPE); } /** * Constructor * * @param a the action dictionary */ public PDActionHide(COSDictionary a) { super(a); } /** * The annotation or annotations to be hidden or shown * * @return The T entry of the specific thread action dictionary. */ public COSBase getT() { // Dictionary, String or Array return this.action.getDictionaryObject(COSName.T); } /** * @param t annotation or annotations */ public void setT(COSBase t) { this.action.setItem(COSName.T, t); } /** * A flag indicating whether to hide the annotation or show it * * @return true if annotation is hidden */ public boolean getH() { return this.action.getBoolean(COSName.H, true); } /** * @param h hide flag */ public void setH(boolean h) { this.action.setItem(COSName.H, COSBoolean.valueOf(h)); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionImportData.java000066400000000000000000000041661320103431700324060ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.filespecification.FileSpecifications; import org.sejda.sambox.pdmodel.common.filespecification.PDFileSpecification; /** * @author Timur Kamalov */ public class PDActionImportData extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "ImportData"; /** * Default constructor. */ public PDActionImportData() { action = new COSDictionary(); setSubType(SUB_TYPE); } /** * Constructor. * * @param a The action dictionary. */ public PDActionImportData(COSDictionary a) { super(a); } /** * This will get the file in which the destination is located. * * @return The F entry of the specific Submit-From action dictionary. */ public PDFileSpecification getFile() { return FileSpecifications.fileSpecificationFor(action.getDictionaryObject(COSName.F)); } /** * This will set the file in which the destination is located. * * @param fs The file specification. */ public void setFile(PDFileSpecification fs) { action.setItem(COSName.F, fs); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionJavaScript.java000066400000000000000000000044451320103431700324100ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; /** * This represents a JavaScript action. * * @author Michael Schwarzenberger */ public class PDActionJavaScript extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "JavaScript"; /** * Constructor #1. */ public PDActionJavaScript() { super(); setSubType(SUB_TYPE); } /** * Constructor. * * @param js Some javascript code. */ public PDActionJavaScript(String js) { this(); setAction(js); } /** * Constructor #2. * * @param a The action dictionary. */ public PDActionJavaScript(COSDictionary a) { super(a); } /** * @param sAction The JavaScript. */ public void setAction(String sAction) { action.setString("JS", sAction); } /** * @return The Javascript Code. */ public String getAction() { COSBase base = action.getDictionaryObject(COSName.JS); if (base instanceof COSString) { return ((COSString) base).getString(); } else if (base instanceof COSStream) { return ((COSStream) base).asTextString(); } return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionLaunch.java000066400000000000000000000164351320103431700315560ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.filespecification.FileSpecifications; import org.sejda.sambox.pdmodel.common.filespecification.PDFileSpecification; /** * This represents a launch action that can be executed in a PDF document. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public class PDActionLaunch extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "Launch"; /** * Default constructor. */ public PDActionLaunch() { super(); setSubType( SUB_TYPE ); } /** * Constructor. * * @param a The action dictionary. */ public PDActionLaunch( COSDictionary a ) { super( a ); } /** * This will get the application to be launched or the document to be opened or printed. It is required if none of * the entries Win, Mac or Unix is present. If this entry is absent and the viewer application does not understand * any of the alternative entries it should do nothing. * * @return The F entry of the specific launch action dictionary. * */ public PDFileSpecification getFile() { return FileSpecifications.fileSpecificationFor(getCOSObject().getDictionaryObject(COSName.F)); } /** * This will set the application to be launched or the document * to be opened or printed. It is required if none of the entries * Win, Mac or Unix is present. If this entry is absent and the * viewer application does not understand any of the alternative * entries it should do nothing. * * @param fs The file specification. */ public void setFile( PDFileSpecification fs ) { getCOSObject().setItem(COSName.F, fs); } /** * This will get a dictionary containing Windows-specific launch parameters. * * @return The Win entry of of the specific launch action dictionary. */ public PDWindowsLaunchParams getWinLaunchParams() { COSDictionary win = (COSDictionary)action.getDictionaryObject( "Win" ); PDWindowsLaunchParams retval = null; if( win != null ) { retval = new PDWindowsLaunchParams( win ); } return retval; } /** * This will set a dictionary containing Windows-specific launch parameters. * * @param win The action to be performed. */ public void setWinLaunchParams( PDWindowsLaunchParams win ) { action.setItem( "Win", win ); } /** * This will get the file name to be launched or the document to be opened * or printed, in standard Windows pathname format. If the name string includes * a backslash character (\), the backslash must itself be preceded by a backslash. * This value must be a single string; it is not a file specification. * * @return The F entry of the specific Windows launch parameter dictionary. */ public String getF() { return action.getString(COSName.F); } /** * This will set the file name to be launched or the document to be opened * or printed, in standard Windows pathname format. If the name string includes * a backslash character (\), the backslash must itself be preceded by a backslash. * This value must be a single string; it is not a file specification. * * @param f The file name to be launched. */ public void setF( String f ) { action.setString(COSName.F, f ); } /** * This will get the string specifying the default directory in standard DOS syntax. * * @return The D entry of the specific Windows launch parameter dictionary. */ public String getD() { return action.getString(COSName.D); } /** * This will set the string specifying the default directory in standard DOS syntax. * * @param d The default directory. */ public void setD( String d ) { action.setString(COSName.D, d ); } /** * This will get the string specifying the operation to perform: * open to open a document * print to print a document * If the F entry designates an application instead of a document, this entry * is ignored and the application is launched. Default value: open. * * @return The O entry of the specific Windows launch parameter dictionary. */ public String getO() { return action.getString(COSName.O); } /** * This will set the string specifying the operation to perform: * open to open a document * print to print a document * If the F entry designates an application instead of a document, this entry * is ignored and the application is launched. Default value: open. * * @param o The operation to perform. */ public void setO( String o ) { action.setString(COSName.O, o ); } /** * This will get a parameter string to be passed to the application designated by the F entry. * This entry should be omitted if F designates a document. * * @return The P entry of the specific Windows launch parameter dictionary. */ public String getP() { return action.getString(COSName.P); } /** * This will set a parameter string to be passed to the application designated by the F entry. * This entry should be omitted if F designates a document. * * @param p The parameter string. */ public void setP( String p ) { action.setString(COSName.P, p ); } /** * This will specify whether to open the destination document in a new window. * If this flag is false, the destination document will replace the current * document in the same window. If this entry is absent, the viewer application * should behave in accordance with the current user preference. This entry is * ignored if the file designated by the F entry is not a PDF document. * * @return A flag specifying whether to open the destination document in a new window. */ public boolean shouldOpenInNewWindow() { return action.getBoolean( "NewWindow", true ); } /** * This will specify the destination document to open in a new window. * * @param value The flag value. */ public void setOpenInNewWindow( boolean value ) { action.setBoolean( "NewWindow", value ); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionMovie.java000066400000000000000000000041131320103431700314110ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * @author Timur Kamalov */ public class PDActionMovie extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "Movie"; /** * Default constructor. */ public PDActionMovie() { action = new COSDictionary(); setSubType(SUB_TYPE); } /** * Constructor. * * @param a The action dictionary. */ public PDActionMovie(COSDictionary a) { super(a); } /** * This will get the type of action that the actions dictionary describes. It must be Movie for a Movie action. * * @return The S entry of the specific Movie action dictionary. * @deprecated use {@link #getSubType() }. */ @Deprecated public String getS() { return action.getNameAsString(COSName.S); } /** * This will set the type of action that the actions dictionary describes. It must be Movie for a Movie action. * * @param s The Movie action. * @deprecated use {@link #getSubType() }. */ @Deprecated public void setS(String s) { action.setName(COSName.S, s); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionNamed.java000066400000000000000000000035311320103431700313610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; /** * This represents a named action in a PDF document. */ public class PDActionNamed extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "Named"; /** * Default constructor. */ public PDActionNamed() { action = new COSDictionary(); setSubType(SUB_TYPE); } /** * Constructor. * * @param a The action dictionary. */ public PDActionNamed(COSDictionary a) { super(a); } /** * This will get the name of the action to be performed. * * @return The name of the action to be performed. */ public String getN() { return action.getNameAsString("N"); } /** * This will set the name of the action to be performed. * * @param name The name of the action to be performed. */ public void setN(String name) { action.setName("N", name); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionRemoteGoTo.java000066400000000000000000000112761320103431700323660ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.filespecification.FileSpecifications; import org.sejda.sambox.pdmodel.common.filespecification.PDFileSpecification; /** * This represents a remote go-to action that can be executed in a PDF document. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public class PDActionRemoteGoTo extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "GoToR"; /** * Default constructor. */ public PDActionRemoteGoTo() { action = new COSDictionary(); setSubType(SUB_TYPE); } /** * Constructor. * * @param a The action dictionary. */ public PDActionRemoteGoTo(COSDictionary a) { super(a); } /** * This will get the type of action that the actions dictionary describes. It must be GoToR for a remote go-to * action. * * @return The S entry of the specific remote go-to action dictionary. * @deprecated use {@link #getSubType() }. */ @Deprecated public String getS() { return action.getNameAsString(COSName.S); } /** * This will set the type of action that the actions dictionary describes. It must be GoToR for a remote go-to * action. * * @param s The remote go-to action. * @deprecated use {@link #getSubType() }. */ @Deprecated public void setS(String s) { action.setName(COSName.S, s); } /** * This will get the file in which the destination is located. * * @return The F entry of the specific remote go-to action dictionary. * */ public PDFileSpecification getFile() { return FileSpecifications.fileSpecificationFor(action.getDictionaryObject(COSName.F)); } /** * This will set the file in which the destination is located. * * @param fs The file specification. */ public void setFile(PDFileSpecification fs) { action.setItem(COSName.F, fs); } /** * This will get the destination to jump to. If the value is an array defining an explicit destination, its first * element must be a page number within the remote document rather than an indirect reference to a page object in * the current document. The first page is numbered 0. * * @return The D entry of the specific remote go-to action dictionary. */ // Array or String. public COSBase getD() { return action.getDictionaryObject(COSName.D); } /** * This will set the destination to jump to. If the value is an array defining an explicit destination, its first * element must be a page number within the remote document rather than an indirect reference to a page object in * the current document. The first page is numbered 0. * * @param d The destination. */ // In case the value is an array. public void setD(COSBase d) { action.setItem(COSName.D, d); } /** * This will specify whether to open the destination document in a new window. If this flag is false, the * destination document will replace the current document in the same window. If this entry is absent, the viewer * application should behave in accordance with the current user preference. * * @return A flag specifying whether to open the destination document in a new window. */ public boolean shouldOpenInNewWindow() { return action.getBoolean("NewWindow", true); } /** * This will specify the destination document to open in a new window. * * @param value The flag value. */ public void setOpenInNewWindow(boolean value) { action.setBoolean("NewWindow", value); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionResetForm.java000066400000000000000000000046521320103431700322500ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * @author Timur Kamalov */ public class PDActionResetForm extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "ResetForm"; /** * Default constructor. */ public PDActionResetForm() { action = new COSDictionary(); setSubType(SUB_TYPE); } /** * Constructor. * * @param a The action dictionary. */ public PDActionResetForm(COSDictionary a) { super(a); } /** * An array identifying which fields to include in the submission or which to exclude, depending * on the setting of the Include/Exclude flag in the Flags entry * * @return the array of fields */ public COSArray getFields() { COSBase retval = this.action.getDictionaryObject(COSName.FIELDS); return retval instanceof COSArray ? (COSArray) retval : null; } /** * @param array the array of fields */ public void setFields(COSArray array) { this.action.setItem(COSName.FIELDS, array); } /** * A set of flags specifying various characteristics of the action * * @return the flags */ public int getFlags() { return this.action.getInt(COSName.FLAGS, 0); } /** * @param flags the flags */ public void setFlags(int flags) { this.action.setInt(COSName.FLAGS, flags); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionSound.java000066400000000000000000000040471320103431700314300ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This represents a Sound action that can be executed in a PDF document * * @author Timur Kamalov */ public class PDActionSound extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "Sound"; /** * Default constructor. */ public PDActionSound() { action = new COSDictionary(); setSubType(SUB_TYPE); } /** * Constructor. * * @param a The action dictionary. */ public PDActionSound(COSDictionary a) { super(a); } /** * This will get the type of action that the actions dictionary describes. It must be Sound for * a Sound action. * * @return The S entry of the specific Sound action dictionary. */ public String getS() { return action.getNameAsString(COSName.S); } /** * This will set the type of action that the actions dictionary describes. It must be Sound for * a Sound action. * * @param s The Sound action. */ public void setS(String s) { action.setName(COSName.S, s); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionSubmitForm.java000066400000000000000000000062311320103431700324240ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.filespecification.FileSpecifications; import org.sejda.sambox.pdmodel.common.filespecification.PDFileSpecification; /** * This represents a Submit-Form action that can be executed in a PDF document. * * @author Evgeniy Muravitskiy */ public class PDActionSubmitForm extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "SubmitForm"; /** * Default Constructor */ public PDActionSubmitForm() { setSubType(SUB_TYPE); } /** * Constructor * * @param a the action dictionary */ public PDActionSubmitForm(COSDictionary a) { super(a); } /** * This will get the file in which the destination is located. * * @return The F entry of the specific Submit-From action dictionary. */ public PDFileSpecification getFile() { return FileSpecifications.fileSpecificationFor(action.getDictionaryObject(COSName.F)); } /** * This will set the file in which the destination is located. * * @param fs The file specification. */ public void setFile(PDFileSpecification fs) { action.setItem(COSName.F, fs); } /** * An array identifying which fields to include in the submission or which to exclude, depending * on the setting of the Include/Exclude flag in the Flags entry * * @return the array of fields */ public COSArray getFields() { COSBase retval = this.action.getDictionaryObject(COSName.FIELDS); return retval instanceof COSArray ? (COSArray) retval : null; } /** * @param array the array of fields */ public void setFields(COSArray array) { this.action.setItem(COSName.FIELDS, array); } /** * A set of flags specifying various characteristics of the action * * @return the flags */ public int getFlags() { return this.action.getInt(COSName.FLAGS, 0); } /** * @param flags the flags */ public void setFlags(int flags) { this.action.setInt(COSName.FLAGS, flags); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionThread.java000066400000000000000000000055471320103431700315550ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.filespecification.FileSpecifications; import org.sejda.sambox.pdmodel.common.filespecification.PDFileSpecification; /** * * This represents a thread action that can be executed in a PDF document. * * @author Evgeniy Muravitskiy */ public class PDActionThread extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "Thread"; /** * Default constructor. */ public PDActionThread() { setSubType(SUB_TYPE); } /** * Constructor. * * @param a The action dictionary. */ public PDActionThread(COSDictionary a) { super(a); } /** * @return The D entry of the specific thread action dictionary. */ // Dictionary, Integer or String. public COSBase getD() { return action.getDictionaryObject(COSName.D); } /** * @param d The destination. */ public void setD(COSBase d) { action.setItem(COSName.D, d); } /** * This will get the file in which the destination is located. * * @return The F entry of the specific thread action dictionary. */ public PDFileSpecification getFile() { return FileSpecifications.fileSpecificationFor(action.getDictionaryObject(COSName.F)); } /** * This will set the file in which the destination is located. * * @param fs The file specification. */ public void setFile(PDFileSpecification fs) { action.setItem(COSName.F, fs); } /** * @return The B entry of the specific thread action dictionary. */ // Dictionary or Integer. public COSBase getB() { return action.getDictionaryObject(COSName.B); } /** * @param b The destination. */ public void setB(COSBase b) { action.setItem(COSName.B, b); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDActionURI.java000066400000000000000000000104701320103431700307740ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import static java.util.Objects.nonNull; import java.nio.charset.StandardCharsets; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; /** * This represents a URI action that can be executed in a PDF document. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public class PDActionURI extends PDAction { /** * This type of action this object represents. */ public static final String SUB_TYPE = "URI"; /** * Default constructor. */ public PDActionURI() { action = new COSDictionary(); setSubType(SUB_TYPE); } /** * Constructor. * * @param a The action dictionary. */ public PDActionURI(COSDictionary a) { super(a); } /** * This will get the type of action that the actions dictionary describes. It must be URI for a URI action. * * @return The S entry of the specific URI action dictionary. * @deprecated use {@link #getSubType() }. */ @Deprecated public String getS() { return action.getNameAsString(COSName.S); } /** * This will set the type of action that the actions dictionary describes. It must be URI for a URI action. * * @param s The URI action. * @deprecated use {@link #getSubType() }. */ @Deprecated public void setS(String s) { action.setName(COSName.S, s); } /** * This will get the uniform resource identifier to resolve. It should be encoded in 7-bit ASCII, but UTF-8 and * UTF-16 are supported too. * * @return The URI entry of the specific URI action dictionary or null if there isn't any. */ public String getURI() { COSString base = action.getDictionaryObject(COSName.URI, COSString.class); if (nonNull(base)) { byte[] bytes = base.getBytes(); if (bytes.length >= 2) { // UTF-16 (BE) if ((bytes[0] & 0xFF) == 0xFE && (bytes[1] & 0xFF) == 0xFF) { return action.getString(COSName.URI); } // UTF-16 (LE) if ((bytes[0] & 0xFF) == 0xFF && (bytes[1] & 0xFF) == 0xFE) { return action.getString(COSName.URI); } } return new String(bytes, StandardCharsets.UTF_8); } return null; } /** * This will set the uniform resource identifier to resolve, encoded in 7-bit ASCII. * * @param uri The uniform resource identifier. */ public void setURI(String uri) { action.setString(COSName.URI, uri); } /** * This will specify whether to track the mouse position when the URI is resolved. Default value: false. This entry * applies only to actions triggered by the user's clicking an annotation; it is ignored for actions associated with * outline items or with a document's OpenAction entry. * * @return A flag specifying whether to track the mouse position when the URI is resolved. */ public boolean shouldTrackMousePosition() { return this.action.getBoolean("IsMap", false); } /** * This will specify whether to track the mouse position when the URI is resolved. * * @param value The flag value. */ public void setTrackMousePosition(boolean value) { this.action.setBoolean("IsMap", value); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDAdditionalActions.java000066400000000000000000000041071320103431700325700ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This represents a dictionary of actions that occur due to events. * * @author Ben Litchfield */ public class PDAdditionalActions implements COSObjectable { private final COSDictionary actions; /** * Default constructor. */ public PDAdditionalActions() { actions = new COSDictionary(); } /** * Constructor. * * @param a The action dictionary. */ public PDAdditionalActions( COSDictionary a ) { actions = a; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return actions; } /** * Get the F action. * * @return The F action. */ public PDAction getF() { return PDActionFactory.createAction((COSDictionary) actions.getDictionaryObject(COSName.F)); } /** * Set the F action. * * @param action Get the F action. */ public void setF( PDAction action ) { actions.setItem(COSName.F, action); } } PDAnnotationAdditionalActions.java000066400000000000000000000235051320103431700345470ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This class represents an annotation's dictionary of actions that occur due to events. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public class PDAnnotationAdditionalActions implements COSObjectable { private final COSDictionary actions; /** * Default constructor. */ public PDAnnotationAdditionalActions() { actions = new COSDictionary(); } /** * Constructor. * * @param a The action dictionary. */ public PDAnnotationAdditionalActions(COSDictionary a) { actions = a; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return actions; } /** * This will get an action to be performed when the cursor enters the annotation's active area. * * @return The E entry of annotation's additional actions dictionary. */ public PDAction getE() { COSDictionary e = (COSDictionary) actions.getDictionaryObject(COSName.E); PDAction retval = null; if (e != null) { retval = PDActionFactory.createAction(e); } return retval; } /** * This will set an action to be performed when the cursor enters the annotation's active area. * * @param e The action to be performed. */ public void setE(PDAction e) { actions.setItem(COSName.E, e); } /** * This will get an action to be performed when the cursor exits the annotation's active area. * * @return The X entry of annotation's additional actions dictionary. */ public PDAction getX() { COSDictionary x = (COSDictionary) actions.getDictionaryObject("X"); PDAction retval = null; if (x != null) { retval = PDActionFactory.createAction(x); } return retval; } /** * This will set an action to be performed when the cursor exits the annotation's active area. * * @param x The action to be performed. */ public void setX(PDAction x) { actions.setItem("X", x); } /** * This will get an action to be performed when the mouse button is pressed inside the annotation's active area. The * name D stands for "down". * * @return The d entry of annotation's additional actions dictionary. */ public PDAction getD() { COSDictionary d = (COSDictionary) actions.getDictionaryObject(COSName.D); PDAction retval = null; if (d != null) { retval = PDActionFactory.createAction(d); } return retval; } /** * This will set an action to be performed when the mouse button is pressed inside the annotation's active area. The * name D stands for "down". * * @param d The action to be performed. */ public void setD(PDAction d) { actions.setItem(COSName.D, d); } /** * This will get an action to be performed when the mouse button is released inside the annotation's active area. * The name U stands for "up". * * @return The U entry of annotation's additional actions dictionary. */ public PDAction getU() { COSDictionary u = (COSDictionary) actions.getDictionaryObject("U"); PDAction retval = null; if (u != null) { retval = PDActionFactory.createAction(u); } return retval; } /** * This will set an action to be performed when the mouse button is released inside the annotation's active area. * The name U stands for "up". * * @param u The action to be performed. */ public void setU(PDAction u) { actions.setItem(COSName.U, u); } /** * This will get an action to be performed when the annotation receives the input focus. * * @return The Fo entry of annotation's additional actions dictionary. */ public PDAction getFo() { COSDictionary fo = (COSDictionary) actions.getDictionaryObject("Fo"); PDAction retval = null; if (fo != null) { retval = PDActionFactory.createAction(fo); } return retval; } /** * This will set an action to be performed when the annotation receives the input focus. * * @param fo The action to be performed. */ public void setFo(PDAction fo) { actions.setItem("Fo", fo); } /** * This will get an action to be performed when the annotation loses the input focus. The name Bl stands for * "blurred". * * @return The Bl entry of annotation's additional actions dictionary. */ public PDAction getBl() { COSDictionary bl = (COSDictionary) actions.getDictionaryObject("Bl"); PDAction retval = null; if (bl != null) { retval = PDActionFactory.createAction(bl); } return retval; } /** * This will set an action to be performed when the annotation loses the input focus. The name Bl stands for * "blurred". * * @param bl The action to be performed. */ public void setBl(PDAction bl) { actions.setItem("Bl", bl); } /** * This will get an action to be performed when the page containing the annotation is opened. The action is executed * after the O action in the page's additional actions dictionary and the OpenAction entry in the document catalog, * if such actions are present. * * @return The PO entry of annotation's additional actions dictionary. */ public PDAction getPO() { COSDictionary po = (COSDictionary) actions.getDictionaryObject("PO"); PDAction retval = null; if (po != null) { retval = PDActionFactory.createAction(po); } return retval; } /** * This will set an action to be performed when the page containing the annotation is opened. The action is executed * after the O action in the page's additional actions dictionary and the OpenAction entry in the document catalog, * if such actions are present. * * @param po The action to be performed. */ public void setPO(PDAction po) { actions.setItem("PO", po); } /** * This will get an action to be performed when the page containing the annotation is closed. The action is executed * before the C action in the page's additional actions dictionary, if present. * * @return The PC entry of annotation's additional actions dictionary. */ public PDAction getPC() { COSDictionary pc = (COSDictionary) actions.getDictionaryObject("PC"); PDAction retval = null; if (pc != null) { retval = PDActionFactory.createAction(pc); } return retval; } /** * This will set an action to be performed when the page containing the annotation is closed. The action is executed * before the C action in the page's additional actions dictionary, if present. * * @param pc The action to be performed. */ public void setPC(PDAction pc) { actions.setItem("PC", pc); } /** * This will get an action to be performed when the page containing the annotation becomes visible in the viewer * application's user interface. * * @return The PV entry of annotation's additional actions dictionary. */ public PDAction getPV() { COSDictionary pv = (COSDictionary) actions.getDictionaryObject("PV"); PDAction retval = null; if (pv != null) { retval = PDActionFactory.createAction(pv); } return retval; } /** * This will set an action to be performed when the page containing the annotation becomes visible in the viewer * application's user interface. * * @param pv The action to be performed. */ public void setPV(PDAction pv) { actions.setItem("PV", pv); } /** * This will get an action to be performed when the page containing the annotation is no longer visible in the * viewer application's user interface. * * @return The PI entry of annotation's additional actions dictionary. */ public PDAction getPI() { COSDictionary pi = (COSDictionary) actions.getDictionaryObject("PI"); PDAction retval = null; if (pi != null) { retval = PDActionFactory.createAction(pi); } return retval; } /** * This will set an action to be performed when the page containing the annotation is no longer visible in the * viewer application's user interface. * * @param pi The action to be performed. */ public void setPI(PDAction pi) { actions.setItem("PI", pi); } } PDDocumentCatalogAdditionalActions.java000066400000000000000000000132521320103431700355040ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSObjectable; /** * This class represents a document catalog's dictionary of actions * that occur due to events. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public class PDDocumentCatalogAdditionalActions implements COSObjectable { private final COSDictionary actions; /** * Default constructor. */ public PDDocumentCatalogAdditionalActions() { actions = new COSDictionary(); } /** * Constructor. * * @param a The action dictionary. */ public PDDocumentCatalogAdditionalActions( COSDictionary a ) { actions = a; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return actions; } /** * This will get a JavaScript action to be performed * before closing a document. * The name WC stands for "will close". * * @return The WC entry of document catalog's additional actions dictionary. */ public PDAction getWC() { COSDictionary wc = (COSDictionary)actions.getDictionaryObject( "WC" ); PDAction retval = null; if( wc != null ) { retval = PDActionFactory.createAction( wc ); } return retval; } /** * This will set a JavaScript action to be performed * before closing a document. * The name WC stands for "will close". * * @param wc The action to be performed. */ public void setWC( PDAction wc ) { actions.setItem( "WC", wc ); } /** * This will get a JavaScript action to be performed * before saving a document. * The name WS stands for "will save". * * @return The WS entry of document catalog's additional actions dictionary. */ public PDAction getWS() { COSDictionary ws = (COSDictionary)actions.getDictionaryObject( "WS" ); PDAction retval = null; if( ws != null ) { retval = PDActionFactory.createAction( ws ); } return retval; } /** * This will set a JavaScript action to be performed * before saving a document. * The name WS stands for "will save". * * @param ws The action to be performed. */ public void setWS( PDAction ws ) { actions.setItem( "WS", ws ); } /** * This will get a JavaScript action to be performed * after saving a document. * The name DS stands for "did save". * * @return The DS entry of document catalog's additional actions dictionary. */ public PDAction getDS() { COSDictionary ds = (COSDictionary)actions.getDictionaryObject( "DS" ); PDAction retval = null; if( ds != null ) { retval = PDActionFactory.createAction( ds ); } return retval; } /** * This will set a JavaScript action to be performed * after saving a document. * The name DS stands for "did save". * * @param ds The action to be performed. */ public void setDS( PDAction ds ) { actions.setItem( "DS", ds ); } /** * This will get a JavaScript action to be performed * before printing a document. * The name WP stands for "will print". * * @return The WP entry of document catalog's additional actions dictionary. */ public PDAction getWP() { COSDictionary wp = (COSDictionary)actions.getDictionaryObject( "WP" ); PDAction retval = null; if( wp != null ) { retval = PDActionFactory.createAction( wp ); } return retval; } /** * This will set a JavaScript action to be performed * before printing a document. * The name WP stands for "will print". * * @param wp The action to be performed. */ public void setWP( PDAction wp ) { actions.setItem( "WP", wp ); } /** * This will get a JavaScript action to be performed * after printing a document. * The name DP stands for "did print". * * @return The DP entry of document catalog's additional actions dictionary. */ public PDAction getDP() { COSDictionary dp = (COSDictionary)actions.getDictionaryObject( "DP" ); PDAction retval = null; if( dp != null ) { retval = PDActionFactory.createAction( dp ); } return retval; } /** * This will set a JavaScript action to be performed * after printing a document. * The name DP stands for "did print". * * @param dp The action to be performed. */ public void setDP( PDAction dp ) { actions.setItem( "DP", dp ); } } PDFormFieldAdditionalActions.java000066400000000000000000000132761320103431700343100ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSObjectable; /** * This class represents a form field's dictionary of actions * that occur due to events. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public class PDFormFieldAdditionalActions implements COSObjectable { private final COSDictionary actions; /** * Default constructor. */ public PDFormFieldAdditionalActions() { actions = new COSDictionary(); } /** * Constructor. * * @param a The action dictionary. */ public PDFormFieldAdditionalActions( COSDictionary a ) { actions = a; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return actions; } /** * This will get a JavaScript action to be performed when the user * types a keystroke into a text field or combo box or modifies the * selection in a scrollable list box. This allows the keystroke to * be checked for validity and rejected or modified. * * @return The K entry of form field's additional actions dictionary. */ public PDAction getK() { COSDictionary k = (COSDictionary)actions.getDictionaryObject( "K" ); PDAction retval = null; if( k != null ) { retval = PDActionFactory.createAction( k ); } return retval; } /** * This will set a JavaScript action to be performed when the user * types a keystroke into a text field or combo box or modifies the * selection in a scrollable list box. This allows the keystroke to * be checked for validity and rejected or modified. * * @param k The action to be performed. */ public void setK( PDAction k ) { actions.setItem( "K", k ); } /** * This will get a JavaScript action to be performed before * the field is formatted to display its current value. This * allows the field's value to be modified before formatting. * * @return The F entry of form field's additional actions dictionary. */ public PDAction getF() { COSDictionary f = (COSDictionary)actions.getDictionaryObject( "F" ); PDAction retval = null; if( f != null ) { retval = PDActionFactory.createAction( f ); } return retval; } /** * This will set a JavaScript action to be performed before * the field is formatted to display its current value. This * allows the field's value to be modified before formatting. * * @param f The action to be performed. */ public void setF( PDAction f ) { actions.setItem( "F", f ); } /** * This will get a JavaScript action to be performed * when the field's value is changed. This allows the * new value to be checked for validity. * The name V stands for "validate". * * @return The V entry of form field's additional actions dictionary. */ public PDAction getV() { COSDictionary v = (COSDictionary)actions.getDictionaryObject( "V" ); PDAction retval = null; if( v != null ) { retval = PDActionFactory.createAction( v ); } return retval; } /** * This will set a JavaScript action to be performed * when the field's value is changed. This allows the * new value to be checked for validity. * The name V stands for "validate". * * @param v The action to be performed. */ public void setV( PDAction v ) { actions.setItem( "V", v ); } /** * This will get a JavaScript action to be performed in order to recalculate * the value of this field when that of another field changes. The order in which * the document's fields are recalculated is defined by the CO entry in the * interactive form dictionary. * The name C stands for "calculate". * * @return The C entry of form field's additional actions dictionary. */ public PDAction getC() { COSDictionary c = (COSDictionary)actions.getDictionaryObject( "C" ); PDAction retval = null; if( c != null ) { retval = PDActionFactory.createAction( c ); } return retval; } /** * This will set a JavaScript action to be performed in order to recalculate * the value of this field when that of another field changes. The order in which * the document's fields are recalculated is defined by the CO entry in the * interactive form dictionary. * The name C stands for "calculate". * * @param c The action to be performed. */ public void setC( PDAction c ) { actions.setItem( "C", c ); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDPageAdditionalActions.java000066400000000000000000000067211320103431700333710ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This class represents a page object's dictionary of actions that occur due to events. * * @author Ben Litchfield * @author Panagiotis Toumasis */ public class PDPageAdditionalActions implements COSObjectable { private final COSDictionary actions; /** * Default constructor. */ public PDPageAdditionalActions() { actions = new COSDictionary(); } /** * Constructor. * * @param a The action dictionary. */ public PDPageAdditionalActions(COSDictionary a) { actions = a; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSDictionary getCOSObject() { return actions; } /** * This will get an action to be performed when the page is opened. This action is independent of any that may be * defined by the OpenAction entry in the document catalog, and is executed after such an action. * * @return The O entry of page object's additional actions dictionary. */ public PDAction getO() { COSDictionary o = (COSDictionary) actions.getDictionaryObject(COSName.O); if (o != null) { return PDActionFactory.createAction(o); } return null; } /** * This will set an action to be performed when the page is opened. This action is independent of any that may be * defined by the OpenAction entry in the document catalog, and is executed after such an action. * * @param o The action to be performed. */ public void setO(PDAction o) { actions.setItem(COSName.O, o); } /** * This will get an action to be performed when the page is closed. This action applies to the page being closed, * and is executed before any other page opened. * * @return The C entry of page object's additional actions dictionary. */ public PDAction getC() { COSDictionary c = (COSDictionary) actions.getDictionaryObject(COSName.C); if (c != null) { return PDActionFactory.createAction(c); } return null; } /** * This will set an action to be performed when the page is closed. This action applies to the page being closed, * and is executed before any other page opened. * * @param c The action to be performed. */ public void setC(PDAction c) { actions.setItem(COSName.C, c); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDURIDictionary.java000066400000000000000000000055751320103431700316760ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSObjectable; /** * This is the implementation of an URI dictionary. * */ public class PDURIDictionary implements COSObjectable { private final COSDictionary uriDictionary; /** * Constructor. * */ public PDURIDictionary() { this.uriDictionary = new COSDictionary(); } /** * Constructor. * * @param dictionary the corresponding dictionary */ public PDURIDictionary(COSDictionary dictionary) { this.uriDictionary = dictionary; } /** * Returns the corresponding dictionary. * @return the dictionary */ @Override public COSDictionary getCOSObject() { return this.uriDictionary; } /** * This will get the base URI to be used in resolving relative URI references. * URI actions within the document may specify URIs in partial form, to be interpreted * relative to this base address. If no base URI is specified, such partial URIs * will be interpreted relative to the location of the document itself. * The use of this entry is parallel to that of the body element <BASE>, as described * in the HTML 4.01 Specification. * * @return The URI entry of the specific URI dictionary. */ public String getBase() { return this.getCOSObject().getString("Base"); } /** * This will set the base URI to be used in resolving relative URI references. * URI actions within the document may specify URIs in partial form, to be interpreted * relative to this base address. If no base URI is specified, such partial URIs * will be interpreted relative to the location of the document itself. * The use of this entry is parallel to that of the body element <BASE>, as described * in the HTML 4.01 Specification. * * @param base The the base URI to be used. */ public void setBase(String base) { this.getCOSObject().setString("Base", base); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/action/PDWindowsLaunchParams.java000066400000000000000000000074451320103431700331400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.action; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * Launch paramaters for the windows OS. * * @author Ben Litchfield */ public class PDWindowsLaunchParams implements COSObjectable { /** * The open operation for the launch. */ public static final String OPERATION_OPEN = "open"; /** * The print operation for the lanuch. */ public static final String OPERATION_PRINT = "print"; /** * The params dictionary. */ protected COSDictionary params; /** * Default constructor. */ public PDWindowsLaunchParams() { params = new COSDictionary(); } /** * Constructor. * * @param p The params dictionary. */ public PDWindowsLaunchParams( COSDictionary p ) { params = p; } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSBase getCOSObject() { return params; } /** * The file to launch. * * @return The executable/document to launch. */ public String getFilename() { return params.getString(COSName.F); } /** * Set the file to launch. * * @param file The executable/document to launch. */ public void setFilename( String file ) { params.setString(COSName.F, file); } /** * The dir to launch from. * * @return The dir of the executable/document to launch. */ public String getDirectory() { return params.getString(COSName.D); } /** * Set the dir to launch from. * * @param dir The dir of the executable/document to launch. */ public void setDirectory( String dir ) { params.setString(COSName.D, dir); } /** * Get the operation to perform on the file. This method will not return null, * OPERATION_OPEN is the default. * * @return The operation to perform for the file. * @see PDWindowsLaunchParams#OPERATION_OPEN * @see PDWindowsLaunchParams#OPERATION_PRINT */ public String getOperation() { return params.getString(COSName.O, OPERATION_OPEN); } /** * Set the operation to perform.. * * @param op The operation to perform on the file. */ public void setOperation( String op ) { params.setString(COSName.D, op); } /** * A parameter to pass the executable. * * @return The parameter to pass the executable. */ public String getExecuteParam() { return params.getString(COSName.P); } /** * Set the parameter to pass the executable. * * @param param The parameter for the executable. */ public void setExecuteParam( String param ) { params.setString(COSName.P, param); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/000077500000000000000000000000001320103431700270035ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAnnotation.java000066400000000000000000000522331320103431700322110ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Optional.of; import static java.util.Optional.ofNullable; import static org.sejda.util.RequireUtils.requireArg; import java.util.Calendar; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDPropertyList; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceCMYK; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A PDF annotation. * * @author Ben Litchfield */ public abstract class PDAnnotation extends PDDictionaryWrapper { /** * Log instance. */ private static final Logger LOG = LoggerFactory.getLogger(PDAnnotation.class); /** * An annotation flag. */ public static final int FLAG_INVISIBLE = 1 << 0; /** * An annotation flag. */ public static final int FLAG_HIDDEN = 1 << 1; /** * An annotation flag. */ public static final int FLAG_PRINTED = 1 << 2; /** * An annotation flag. */ public static final int FLAG_NO_ZOOM = 1 << 3; /** * An annotation flag. */ public static final int FLAG_NO_ROTATE = 1 << 4; /** * An annotation flag. */ public static final int FLAG_NO_VIEW = 1 << 5; /** * An annotation flag. */ public static final int FLAG_READ_ONLY = 1 << 6; /** * An annotation flag. */ public static final int FLAG_LOCKED = 1 << 7; /** * An annotation flag. */ public static final int FLAG_TOGGLE_NO_VIEW = 1 << 8; /** * Create the annotation of the expected type from the given dictionary. * * @return The correctly typed annotation object. */ public static T createAnnotation(COSDictionary dictionary, Class expectedType) { PDAnnotation annotation = createAnnotation(dictionary); if (nonNull(annotation)) { return of(annotation).filter(i -> expectedType.isInstance(i)).map(expectedType::cast) .orElseGet(() -> { LOG.warn("Expected annotation type {} but got {}", expectedType, annotation.getClass()); return null; }); } return null; } /** * Create the correct annotation from the base COS object. * * @param base The COS object that is the annotation. * @return The correctly typed annotation object. */ public static PDAnnotation createAnnotation(COSBase base) { requireArg(base instanceof COSDictionary, "Illegal annotation type " + base); COSDictionary annotDic = (COSDictionary) base; String subtype = annotDic.getNameAsString(COSName.SUBTYPE); if (PDAnnotationFileAttachment.SUB_TYPE.equals(subtype)) { return new PDAnnotationFileAttachment(annotDic); } else if (PDAnnotationLine.SUB_TYPE.equals(subtype)) { return new PDAnnotationLine(annotDic); } else if (PDAnnotationLink.SUB_TYPE.equals(subtype)) { return new PDAnnotationLink(annotDic); } else if (PDAnnotationPopup.SUB_TYPE.equals(subtype)) { return new PDAnnotationPopup(annotDic); } else if (PDAnnotationRubberStamp.SUB_TYPE.equals(subtype)) { return new PDAnnotationRubberStamp(annotDic); } else if (PDAnnotationSquareCircle.SUB_TYPE_SQUARE.equals(subtype) || PDAnnotationSquareCircle.SUB_TYPE_CIRCLE.equals(subtype)) { return new PDAnnotationSquareCircle(annotDic); } else if (PDAnnotationText.SUB_TYPE.equals(subtype)) { return new PDAnnotationText(annotDic); } else if (PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT.equals(subtype) || PDAnnotationTextMarkup.SUB_TYPE_UNDERLINE.equals(subtype) || PDAnnotationTextMarkup.SUB_TYPE_SQUIGGLY.equals(subtype) || PDAnnotationTextMarkup.SUB_TYPE_STRIKEOUT.equals(subtype)) { // see 12.5.6.10 Text Markup Annotations return new PDAnnotationTextMarkup(annotDic); } else if (PDAnnotationLink.SUB_TYPE.equals(subtype)) { return new PDAnnotationLink(annotDic); } else if (COSName.WIDGET.getName().equals(subtype)) { return new PDAnnotationWidget(annotDic); } else if (PDAnnotationMarkup.SUB_TYPE_FREETEXT.equals(subtype) || PDAnnotationMarkup.SUB_TYPE_POLYGON.equals(subtype) || PDAnnotationMarkup.SUB_TYPE_POLYLINE.equals(subtype) || PDAnnotationMarkup.SUB_TYPE_CARET.equals(subtype) || PDAnnotationMarkup.SUB_TYPE_INK.equals(subtype) || PDAnnotationMarkup.SUB_TYPE_SOUND.equals(subtype)) { return new PDAnnotationMarkup(annotDic); } LOG.warn("Unsupported annotation subtype " + subtype); // TODO not yet implemented: // Movie, Screen, PrinterMark, TrapNet, Watermark, 3D, Redact return new PDAnnotationUnknown(annotDic); } public PDAnnotation() { getCOSObject().setItem(COSName.TYPE, COSName.ANNOT); } public PDAnnotation(COSDictionary dictionary) { super(dictionary); getCOSObject().setItem(COSName.TYPE, COSName.ANNOT); } /** * The annotation rectangle, defining the location of the annotation on the page in default user space units. This * is usually required and should not return null on valid PDF documents. But where this is a parent form field with * children, such as radio button collections then the rectangle will be null. * * @return The Rect value of this annotation. */ public PDRectangle getRectangle() { COSArray rectArray = getCOSObject().getDictionaryObject(COSName.RECT, COSArray.class); if (rectArray != null) { if (rectArray.size() == 4 && rectArray.getObject(0) instanceof COSNumber && rectArray.getObject(1) instanceof COSNumber && rectArray.getObject(2) instanceof COSNumber && rectArray.getObject(3) instanceof COSNumber) { return new PDRectangle(rectArray); } LOG.warn(rectArray + " is not a rectangle array, returning null"); } return null; } /** * This will set the rectangle for this annotation. * * @param rectangle The new rectangle values. */ public void setRectangle(PDRectangle rectangle) { getCOSObject().setItem(COSName.RECT, rectangle.getCOSObject()); } /** * This will get the flags for this field. * * @return flags The set of flags. */ public int getAnnotationFlags() { return getCOSObject().getInt(COSName.F, 0); } /** * This will set the flags for this field. * * @param flags The new flags. */ public void setAnnotationFlags(int flags) { getCOSObject().setInt(COSName.F, flags); } /** * Returns the annotations appearance state, which selects the applicable appearance stream from an appearance * subdictionary. */ public COSName getAppearanceState() { return ofNullable(getCOSObject().getDictionaryObject(COSName.AS, COSName.class)) .orElse(null); } /** * This will set the annotations appearance state name. * * @param as The name of the appearance stream. */ public void setAppearanceState(String as) { getCOSObject().setName(COSName.AS, as); } /** * This will get the appearance dictionary associated with this annotation. This may return null. * * @return This annotations appearance. */ public PDAppearanceDictionary getAppearance() { return ofNullable(getCOSObject().getDictionaryObject(COSName.AP, COSDictionary.class)) .map(PDAppearanceDictionary::new).orElse(null); } /** * This will set the appearance associated with this annotation. * * @param appearance The appearance dictionary for this annotation. */ public void setAppearance(PDAppearanceDictionary appearance) { getCOSObject().setItem(COSName.AP, appearance); } /** * Returns the appearance stream for this annotation, if any. The annotation state is taken into account, if * present. */ public PDAppearanceStream getNormalAppearanceStream() { PDAppearanceDictionary appearanceDict = getAppearance(); if (appearanceDict == null) { return null; } PDAppearanceEntry normalAppearance = appearanceDict.getNormalAppearance(); if (normalAppearance == null) { return null; } if (normalAppearance.isSubDictionary()) { COSName state = getAppearanceState(); return normalAppearance.getSubDictionary().get(state); } return normalAppearance.getAppearanceStream(); } /** * Get the invisible flag. * * @return The invisible flag. */ public boolean isInvisible() { return getCOSObject().getFlag(COSName.F, FLAG_INVISIBLE); } /** * Set the invisible flag. * * @param invisible The new invisible flag. */ public void setInvisible(boolean invisible) { getCOSObject().setFlag(COSName.F, FLAG_INVISIBLE, invisible); } /** * Get the hidden flag. * * @return The hidden flag. */ public boolean isHidden() { return getCOSObject().getFlag(COSName.F, FLAG_HIDDEN); } /** * Set the hidden flag. * * @param hidden The new hidden flag. */ public void setHidden(boolean hidden) { getCOSObject().setFlag(COSName.F, FLAG_HIDDEN, hidden); } /** * Get the printed flag. * * @return The printed flag. */ public boolean isPrinted() { return getCOSObject().getFlag(COSName.F, FLAG_PRINTED); } /** * Set the printed flag. * * @param printed The new printed flag. */ public void setPrinted(boolean printed) { getCOSObject().setFlag(COSName.F, FLAG_PRINTED, printed); } /** * Get the noZoom flag. * * @return The noZoom flag. */ public boolean isNoZoom() { return getCOSObject().getFlag(COSName.F, FLAG_NO_ZOOM); } /** * Set the noZoom flag. * * @param noZoom The new noZoom flag. */ public void setNoZoom(boolean noZoom) { getCOSObject().setFlag(COSName.F, FLAG_NO_ZOOM, noZoom); } /** * Get the noRotate flag. * * @return The noRotate flag. */ public boolean isNoRotate() { return getCOSObject().getFlag(COSName.F, FLAG_NO_ROTATE); } /** * Set the noRotate flag. * * @param noRotate The new noRotate flag. */ public void setNoRotate(boolean noRotate) { getCOSObject().setFlag(COSName.F, FLAG_NO_ROTATE, noRotate); } /** * Get the noView flag. * * @return The noView flag. */ public boolean isNoView() { return getCOSObject().getFlag(COSName.F, FLAG_NO_VIEW); } /** * Set the noView flag. * * @param noView The new noView flag. */ public void setNoView(boolean noView) { getCOSObject().setFlag(COSName.F, FLAG_NO_VIEW, noView); } /** * Get the readOnly flag. * * @return The readOnly flag. */ public boolean isReadOnly() { return getCOSObject().getFlag(COSName.F, FLAG_READ_ONLY); } /** * Set the readOnly flag. * * @param readOnly The new readOnly flag. */ public void setReadOnly(boolean readOnly) { getCOSObject().setFlag(COSName.F, FLAG_READ_ONLY, readOnly); } /** * Get the locked flag. * * @return The locked flag. */ public boolean isLocked() { return getCOSObject().getFlag(COSName.F, FLAG_LOCKED); } /** * Set the locked flag. * * @param locked The new locked flag. */ public void setLocked(boolean locked) { getCOSObject().setFlag(COSName.F, FLAG_LOCKED, locked); } /** * Get the toggleNoView flag. * * @return The toggleNoView flag. */ public boolean isToggleNoView() { return getCOSObject().getFlag(COSName.F, FLAG_TOGGLE_NO_VIEW); } /** * Set the toggleNoView flag. * * @param toggleNoView The new toggleNoView flag. */ public void setToggleNoView(boolean toggleNoView) { getCOSObject().setFlag(COSName.F, FLAG_TOGGLE_NO_VIEW, toggleNoView); } /** * Get the "contents" of the field. * * @return the value of the contents. */ public String getContents() { return getCOSObject().getString(COSName.CONTENTS); } /** * Set the "contents" of the field. * * @param value the value of the contents. */ public void setContents(String value) { getCOSObject().setString(COSName.CONTENTS, value); } /** * This will retrieve the date and time the annotation was modified. * * @return the modified date/time (often in date format, but can be an arbitary string). */ public String getModifiedDate() { return getCOSObject().getString(COSName.M); } /** * This will set the date and time the annotation was modified. * * @param m the date and time the annotation was created. Date values used in a PDF shall conform to a standard date * format, which closely follows that of the international standard ASN.1 (Abstract Syntax Notation One), defined in * ISO/IEC 8824. A date shall be a text string of the form (D:YYYYMMDDHHmmSSOHH'mm). Alternatively, use * {@link #setModifiedDate(java.util.Calendar)} */ public void setModifiedDate(String m) { getCOSObject().setString(COSName.M, m); } /** * This will set the date and time the annotation was modified. * * @param c the date and time the annotation was created. */ public void setModifiedDate(Calendar c) { getCOSObject().setDate(COSName.M, c); } /** * This will get the name, a string intended to uniquely identify each annotation within a page. Not to be confused * with some annotations Name entry which impact the default image drawn for them. * * @return The identifying name for the Annotation. */ public String getAnnotationName() { return getCOSObject().getString(COSName.NM); } /** * This will set the name, a string intended to uniquely identify each annotation within a page. Not to be confused * with some annotations Name entry which impact the default image drawn for them. * * @param nm The identifying name for the annotation. */ public void setAnnotationName(String nm) { getCOSObject().setString(COSName.NM, nm); } /** * This will get the key of this annotation in the structural parent tree. * * @return the integer key of the annotation's entry in the structural parent tree */ public int getStructParent() { return getCOSObject().getInt(COSName.STRUCT_PARENT, 0); } /** * This will set the key for this annotation in the structural parent tree. * * @param structParent The new key for this annotation. */ public void setStructParent(int structParent) { getCOSObject().setInt(COSName.STRUCT_PARENT, structParent); } /** * This will get the optional content group or optional content membership dictionary for the annotation. * * @return The optional content group or optional content membership dictionary or null if there is none. */ public PDPropertyList getOptionalContent() { COSDictionary base = getCOSObject().getDictionaryObject(COSName.OC, COSDictionary.class); if (nonNull(base)) { return PDPropertyList.create(base); } return null; } /** * Sets the optional content group or optional content membership dictionary for the annotation. * * @param oc The optional content group or optional content membership dictionary. */ public void setOptionalContent(PDPropertyList oc) { getCOSObject().setItem(COSName.OC, oc); } /** * This will retrieve the border array. If none is available then it will return the default, which is [0 0 1]. The * array consists of at least three numbers defining the horizontal corner radius, vertical corner radius, and * border width. The array may have a fourth element, an optional dash array defining a pattern of dashes and gaps * that shall be used in drawing the border. If the array has less than three elements, it will be filled with 0. * * @return the border array. */ public COSArray getBorder() { COSArray border = getCOSObject().getDictionaryObject(COSName.BORDER, COSArray.class); if (isNull(border)) { return new COSArray(COSInteger.ZERO, COSInteger.ZERO, COSInteger.ONE); } border.growToSize(3, COSInteger.ZERO); return border; } /** * This will set the border array. * * @param borderArray the border array to set. */ public void setBorder(COSArray borderArray) { getCOSObject().setItem(COSName.BORDER, borderArray); } /** * This will set the color used in drawing various elements. As of PDF 1.6 these are : Background of icon when * closed Title bar of popup window Border of a link annotation * * Colour is in DeviceRGB colourspace * * @param c colour in the DeviceRGB colourspace * */ public void setColor(PDColor c) { getCOSObject().setItem(COSName.C, c.toComponentsCOSArray()); } /** * This will retrieve the color used in drawing various elements. As of PDF 1.6 these are : *
    *
  • Background of icon when closed
  • *
  • Title bar of popup window
  • *
  • Border of a link annotation
  • *
* * @return Color object representing the colour * */ public PDColor getColor() { return getColor(COSName.C); } public PDColor getColor(COSName itemName) { COSArray color = getCOSObject().getDictionaryObject(itemName, COSArray.class); if (nonNull(color) && color.size() > 0) { switch (color.size()) { case 1: return new PDColor(color, PDDeviceGray.INSTANCE); case 3: return new PDColor(color, PDDeviceRGB.INSTANCE); case 4: return new PDColor(color, PDDeviceCMYK.INSTANCE); } } return null; } /** * This will retrieve the subtype of the annotation. * * @return the subtype */ public String getSubtype() { return this.getCOSObject().getNameAsString(COSName.SUBTYPE); } /** * This will set the corresponding page for this annotation. * * @param page is the corresponding page */ public void setPage(PDPage page) { this.getCOSObject().setItem(COSName.P, page); } /** * This will retrieve the corresponding page of this annotation. * * @return the corresponding page */ public PDPage getPage() { COSDictionary p = this.getCOSObject().getDictionaryObject(COSName.P, COSDictionary.class); if (nonNull(p)) { return new PDPage(p); } return null; } } PDAnnotationFileAttachment.java000066400000000000000000000064071320103431700347450ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.filespecification.FileSpecifications; import org.sejda.sambox.pdmodel.common.filespecification.PDFileSpecification; /** * This is the class that represents a file attachement. * * @author Ben Litchfield */ public class PDAnnotationFileAttachment extends PDAnnotationMarkup { /** * See get/setAttachmentName. */ public static final String ATTACHMENT_NAME_PUSH_PIN = "PushPin"; /** * See get/setAttachmentName. */ public static final String ATTACHMENT_NAME_GRAPH = "Graph"; /** * See get/setAttachmentName. */ public static final String ATTACHMENT_NAME_PAPERCLIP = "Paperclip"; /** * See get/setAttachmentName. */ public static final String ATTACHMENT_NAME_TAG = "Tag"; /** * The type of annotation. */ public static final String SUB_TYPE = "FileAttachment"; /** * Constructor. */ public PDAnnotationFileAttachment() { getCOSObject().setItem(COSName.SUBTYPE, COSName.getPDFName(SUB_TYPE)); } /** * Creates a Link annotation from a COSDictionary, expected to be a correct object definition. * * @param field the PDF objet to represent as a field. */ public PDAnnotationFileAttachment(COSDictionary field) { super(field); } /** * Return the attached file. * * @return The attached file. */ public PDFileSpecification getFile() { return FileSpecifications.fileSpecificationFor(getCOSObject().getDictionaryObject("FS")); } /** * Set the attached file. * * @param file The file that is attached. */ public void setFile(PDFileSpecification file) { getCOSObject().setItem("FS", file); } /** * This is the name used to draw the type of attachment. See the ATTACHMENT_NAME_XXX constants. * * @return The name that describes the visual cue for the attachment. */ public String getAttachmentName() { return getCOSObject().getNameAsString("Name", ATTACHMENT_NAME_PUSH_PIN); } /** * Set the name used to draw the attachement icon. See the ATTACHMENT_NAME_XXX constants. * * @param name The name of the visual icon to draw. */ public void setAttachementName(String name) { getCOSObject().setName("Name", name); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAnnotationLine.java000066400000000000000000000312571320103431700330240ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.graphics.color.PDColor; /** * This is the class that represents a line annotation. Introduced in PDF 1.3 specification * * @author Paul King */ public class PDAnnotationLine extends PDAnnotationMarkup { /* * The various values for intent (get/setIT, see the PDF 1.6 reference Table 8.22 */ /** * Constant for annotation intent of Arrow. */ public static final String IT_LINE_ARROW = "LineArrow"; /** * Constant for annotation intent of a dimension line. */ public static final String IT_LINE_DIMENSION = "LineDimension"; /* * The various values for line ending styles, see the PDF 1.6 reference Table 8.23 */ /** * Constant for a square line ending. */ public static final String LE_SQUARE = "Square"; /** * Constant for a circle line ending. */ public static final String LE_CIRCLE = "Circle"; /** * Constant for a diamond line ending. */ public static final String LE_DIAMOND = "Diamond"; /** * Constant for a open arrow line ending. */ public static final String LE_OPEN_ARROW = "OpenArrow"; /** * Constant for a closed arrow line ending. */ public static final String LE_CLOSED_ARROW = "ClosedArrow"; /** * Constant for no line ending. */ public static final String LE_NONE = "None"; /** * Constant for a butt line ending. */ public static final String LE_BUTT = "Butt"; /** * Constant for a reversed open arrow line ending. */ public static final String LE_R_OPEN_ARROW = "ROpenArrow"; /** * Constant for a revered closed arrow line ending. */ public static final String LE_R_CLOSED_ARROW = "RClosedArrow"; /** * Constant for a slash line ending. */ public static final String LE_SLASH = "Slash"; /** * The type of annotation. */ public static final String SUB_TYPE = "Line"; /** * Constructor. */ public PDAnnotationLine() { getCOSObject().setItem(COSName.SUBTYPE, COSName.getPDFName(SUB_TYPE)); // Dictionary value L is mandatory, fill in with arbitary value setLine(new float[] { 0, 0, 0, 0 }); } /** * Creates a Line annotation from a COSDictionary, expected to be a correct object definition. * * @param field the PDF object to represent as a field. */ public PDAnnotationLine(COSDictionary field) { super(field); } /** * This will set start and end coordinates of the line (or leader line if LL entry is set). * * @param l array of 4 floats [x1, y1, x2, y2] line start and end points in default user space. */ public void setLine(float[] l) { COSArray newL = new COSArray(); newL.setFloatArray(l); getCOSObject().setItem(COSName.L, newL); } /** * This will retrieve the start and end coordinates of the line (or leader line if LL entry is set). * * @return array of floats [x1, y1, x2, y2] line start and end points in default user space. */ public float[] getLine() { return ofNullable(getCOSObject().getDictionaryObject(COSName.L, COSArray.class)) .map(COSArray::toFloatArray).orElse(null); } /** * This will set the line ending style for the start point, see the LE_ constants for the possible values. * * @param style The new style. */ public void setStartPointEndingStyle(String style) { if (style == null) { style = LE_NONE; } COSArray array = getCOSObject().getDictionaryObject(COSName.LE, COSArray.class); if (array == null || array.size() == 0) { array = new COSArray(); array.add(COSName.getPDFName(style)); array.add(COSName.getPDFName(LE_NONE)); getCOSObject().setItem(COSName.LE, array); } else { array.set(0, COSName.getPDFName(style)); } } /** * This will retrieve the line ending style for the start point, possible values shown in the LE_ constants section. * * @return The ending style for the start point. */ public String getStartPointEndingStyle() { COSArray array = getCOSObject().getDictionaryObject(COSName.LE, COSArray.class); if (nonNull(array)) { return array.getName(0); } return LE_NONE; } /** * This will set the line ending style for the end point, see the LE_ constants for the possible values. * * @param style The new style. */ public void setEndPointEndingStyle(String style) { if (style == null) { style = LE_NONE; } COSArray array = getCOSObject().getDictionaryObject(COSName.LE, COSArray.class); if (array == null || array.size() < 2) { array = new COSArray(); array.add(COSName.getPDFName(LE_NONE)); array.add(COSName.getPDFName(style)); getCOSObject().setItem(COSName.LE, array); } else { array.set(1, COSName.getPDFName(style)); } } /** * This will retrieve the line ending style for the end point, possible values shown in the LE_ constants section. * * @return The ending style for the end point. */ public String getEndPointEndingStyle() { COSArray array = getCOSObject().getDictionaryObject(COSName.LE, COSArray.class); if (nonNull(array) && array.size() >= 2) { return array.getName(1); } return LE_NONE; } /** * This will set interior color of the line endings defined in the LE entry. color is in DeviceRGB color space. * * @param ic color in the DeviceRGB color space. */ public void setInteriorColor(PDColor ic) { getCOSObject().setItem(COSName.IC, ic.toComponentsCOSArray()); } /** * This will retrieve the interior color of the line endings defined in the LE entry. color is in DeviceRGB color * space. * * @return object representing the color. */ public PDColor getInteriorColor() { return getColor(COSName.IC); } /** * This will set if the contents are shown as a caption to the line. * * @param cap Boolean value. */ public void setCaption(boolean cap) { getCOSObject().setBoolean(COSName.CAP, cap); } /** * This will retrieve if the contents are shown as a caption or not. * * @return boolean if the content is shown as a caption. */ public boolean getCaption() { return getCOSObject().getBoolean(COSName.CAP, false); } /** * This will set the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @param bs the border style dictionary to set. * */ @Override public void setBorderStyle(PDBorderStyleDictionary bs) { this.getCOSObject().setItem(COSName.BS, bs); } /** * This will retrieve the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @return the border style dictionary. */ @Override public PDBorderStyleDictionary getBorderStyle() { COSDictionary bs = this.getCOSObject().getDictionaryObject(COSName.BS, COSDictionary.class); if (nonNull(bs)) { return new PDBorderStyleDictionary(bs); } return null; } /** * This will retrieve the length of the leader line. * * @return the length of the leader line */ public float getLeaderLineLength() { return this.getCOSObject().getFloat(COSName.LL); } /** * This will set the length of the leader line. * * @param leaderLineLength length of the leader line */ public void setLeaderLineLength(float leaderLineLength) { this.getCOSObject().setFloat(COSName.LL, leaderLineLength); } /** * This will retrieve the length of the leader line extensions. * * @return the length of the leader line extensions */ public float getLeaderLineExtensionLength() { return this.getCOSObject().getFloat(COSName.LLE); } /** * This will set the length of the leader line extensions. * * @param leaderLineExtensionLength length of the leader line extensions */ public void setLeaderLineExtensionLength(float leaderLineExtensionLength) { this.getCOSObject().setFloat(COSName.LLE, leaderLineExtensionLength); } /** * This will retrieve the length of the leader line offset. * * @return the length of the leader line offset */ public float getLeaderLineOffsetLength() { return this.getCOSObject().getFloat(COSName.LLO); } /** * This will set the length of the leader line offset. * * @param leaderLineOffsetLength length of the leader line offset */ public void setLeaderLineOffsetLength(float leaderLineOffsetLength) { this.getCOSObject().setFloat(COSName.LLO, leaderLineOffsetLength); } /** * This will retrieve the caption positioning. * * @return the caption positioning */ public String getCaptionPositioning() { return this.getCOSObject().getString(COSName.CP); } /** * This will set the caption positioning. Allowed values are: "Inline" and "Top" * * @param captionPositioning caption positioning */ public void setCaptionPositioning(String captionPositioning) { this.getCOSObject().setString(COSName.CP, captionPositioning); } /** * This will set the horizontal offset of the caption. * * @param offset the horizontal offset of the caption */ public void setCaptionHorizontalOffset(float offset) { COSArray array = (COSArray) this.getCOSObject().getDictionaryObject(COSName.CO); if (array == null) { array = new COSArray(); array.setFloatArray(new float[] { offset, 0.f }); this.getCOSObject().setItem(COSName.CO, array); } else { array.set(0, new COSFloat(offset)); } } /** * This will retrieve the horizontal offset of the caption. * * @return the horizontal offset of the caption */ public float getCaptionHorizontalOffset() { COSArray array = this.getCOSObject().getDictionaryObject(COSName.CO, COSArray.class); if (nonNull(array)) { return array.toFloatArray()[0]; } return 0.f; } /** * This will set the vertical offset of the caption. * * @param offset vertical offset of the caption */ public void setCaptionVerticalOffset(float offset) { COSArray array = this.getCOSObject().getDictionaryObject(COSName.CO, COSArray.class); if (array == null) { array = new COSArray(); array.setFloatArray(new float[] { 0.f, offset }); this.getCOSObject().setItem(COSName.CO, array); } else { array.set(1, new COSFloat(offset)); } } /** * This will retrieve the vertical offset of the caption. * * @return the vertical offset of the caption */ public float getCaptionVerticalOffset() { COSArray array = this.getCOSObject().getDictionaryObject(COSName.CO, COSArray.class); if (array != null) { return array.toFloatArray()[1]; } return 0.f; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAnnotationLink.java000066400000000000000000000155161320103431700330320ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Optional.ofNullable; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.interactive.action.PDAction; import org.sejda.sambox.pdmodel.interactive.action.PDActionFactory; import org.sejda.sambox.pdmodel.interactive.action.PDActionURI; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDDestination; /** * This is the class that represents a link annotation. * * @author Ben Litchfield * @author Paul King */ public class PDAnnotationLink extends PDAnnotation { /** * Constant values of the Text as defined in the PDF 1.6 reference Table 8.19. */ public static final String HIGHLIGHT_MODE_NONE = "N"; /** * Constant values of the Text as defined in the PDF 1.6 reference Table 8.19. */ public static final String HIGHLIGHT_MODE_INVERT = "I"; /** * Constant values of the Text as defined in the PDF 1.6 reference Table 8.19. */ public static final String HIGHLIGHT_MODE_OUTLINE = "O"; /** * Constant values of the Text as defined in the PDF 1.6 reference Table 8.19. */ public static final String HIGHLIGHT_MODE_PUSH = "P"; /** * The type of annotation. */ public static final String SUB_TYPE = "Link"; /** * Constructor. */ public PDAnnotationLink() { super(); getCOSObject().setItem(COSName.SUBTYPE, COSName.getPDFName(SUB_TYPE)); } /** * Creates a Link annotation from a COSDictionary, expected to be a correct object definition. * * @param field the PDF objet to represent as a field. */ public PDAnnotationLink(COSDictionary field) { super(field); } /** * Get the action to be performed when this annotation is to be activated. * * @return The action to be performed when this annotation is activated. * * TODO not all annotations have an A entry */ public PDAction getAction() { COSDictionary action = (COSDictionary) this.getCOSObject().getDictionaryObject(COSName.A); return PDActionFactory.createAction(action); } /** * Set the annotation action. As of PDF 1.6 this is only used for Widget Annotations * * @param action The annotation action. TODO not all annotations have an A entry */ public void setAction(PDAction action) { this.getCOSObject().setItem(COSName.A, action); } /** * This will set the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @param bs the border style dictionary to set. TODO not all annotations may have a BS entry * */ public void setBorderStyle(PDBorderStyleDictionary bs) { this.getCOSObject().setItem(COSName.BS, bs); } /** * This will retrieve the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @return the border style dictionary. */ public PDBorderStyleDictionary getBorderStyle() { COSBase bs = this.getCOSObject().getDictionaryObject(COSName.BS); if (bs instanceof COSDictionary) { return new PDBorderStyleDictionary((COSDictionary) bs); } return null; } /** * Get the destination to be displayed when the annotation is activated. Either this or the A should be set but not * both. * * @return The destination for this annotation. * * @throws IOException If there is an error creating the destination. */ public PDDestination getDestination() throws IOException { return PDDestination.create(getCOSObject().getDictionaryObject(COSName.DEST)); } /** * The new destination value. * * @param dest The updated destination. */ public void setDestination(PDDestination dest) { getCOSObject().setItem(COSName.DEST, dest); } /** * Set the highlight mode for when the mouse is depressed. See the HIGHLIGHT_MODE_XXX constants. * * @return The string representation of the highlight mode. */ public String getHighlightMode() { return getCOSObject().getNameAsString(COSName.H, HIGHLIGHT_MODE_INVERT); } /** * Set the highlight mode. See the HIGHLIGHT_MODE_XXX constants. * * @param mode The new highlight mode. */ public void setHighlightMode(String mode) { getCOSObject().setName(COSName.H, mode); } /** * This will set the previous URI action, in case it needs to be retrieved at later date. * * @param pa The previous URI. */ public void setPreviousURI(PDActionURI pa) { getCOSObject().setItem("PA", pa); } /** * This will set the previous URI action, in case it's needed. * * @return The previous URI. */ public PDActionURI getPreviousURI() { COSDictionary pa = (COSDictionary) getCOSObject().getDictionaryObject("PA"); if (pa != null) { return new PDActionURI(pa); } return null; } /** * This will set the set of quadpoints which encompass the areas of this annotation which will activate. * * @param quadPoints an array representing the set of area covered. */ public void setQuadPoints(float[] quadPoints) { COSArray newQuadPoints = new COSArray(); newQuadPoints.setFloatArray(quadPoints); getCOSObject().setItem(COSName.QUADPOINTS, newQuadPoints); } /** * This will retrieve the set of quadpoints which encompass the areas of this annotation which will activate. * * @return An array of floats representing the quad points. */ public float[] getQuadPoints() { return ofNullable(getCOSObject().getDictionaryObject(COSName.QUADPOINTS, COSArray.class)) .map(COSArray::toFloatArray).orElse(null); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAnnotationMarkup.java000066400000000000000000000235041320103431700333700ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Optional.ofNullable; import java.io.IOException; import java.util.Calendar; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; /** * This class represents the additonal fields of a Markup type Annotation. See section 12.5.6 of ISO32000-1:2008 * (starting with page 390) for details on annotation types. * * @author Paul King */ public class PDAnnotationMarkup extends PDAnnotation { /** * Constant for a FreeText type of annotation. */ public static final String SUB_TYPE_FREETEXT = "FreeText"; /** * Constant for an Polygon type of annotation. */ public static final String SUB_TYPE_POLYGON = "Polygon"; /** * Constant for an PolyLine type of annotation. */ public static final String SUB_TYPE_POLYLINE = "PolyLine"; /** * Constant for an Caret type of annotation. */ public static final String SUB_TYPE_CARET = "Caret"; /** * Constant for an Ink type of annotation. */ public static final String SUB_TYPE_INK = "Ink"; /** * Constant for an Sound type of annotation. */ public static final String SUB_TYPE_SOUND = "Sound"; /* * The various values of the reply type as defined in the PDF 1.7 reference Table 170 */ /** * Constant for an annotation reply type. */ public static final String RT_REPLY = "R"; /** * Constant for an annotation reply type. */ public static final String RT_GROUP = "Group"; public PDAnnotationMarkup() { } /** * Constructor. * * @param dict The annotations dictionary. */ public PDAnnotationMarkup(COSDictionary dict) { super(dict); } /** * Retrieve the string used as the title of the popup window shown when open and active (by convention this * identifies who added the annotation). * * @return The title of the popup. */ public String getTitlePopup() { return getCOSObject().getString(COSName.T); } /** * Set the string used as the title of the popup window shown when open and active (by convention this identifies * who added the annotation). * * @param t The title of the popup. */ public void setTitlePopup(String t) { getCOSObject().setString(COSName.T, t); } /** * This will retrieve the popup annotation used for entering/editing the text for this annotation. * * @return the popup annotation. */ public PDAnnotationPopup getPopup() { return ofNullable(getCOSObject().getDictionaryObject(COSName.POPUP, COSDictionary.class)) .map(PDAnnotationPopup::new).orElse(null); } /** * This will set the popup annotation used for entering/editing the text for this annotation. * * @param popup the popup annotation. */ public void setPopup(PDAnnotationPopup popup) { getCOSObject().setItem(COSName.POPUP, popup); } /** * This will retrieve the constant opacity value used when rendering the annotation (excluing any popup). * * @return the constant opacity value. */ public float getConstantOpacity() { return getCOSObject().getFloat(COSName.CA, 1); } /** * This will set the constant opacity value used when rendering the annotation (excluing any popup). * * @param ca the constant opacity value. */ public void setConstantOpacity(float ca) { getCOSObject().setFloat(COSName.CA, ca); } /** * This will retrieve the rich text stream which is displayed in the popup window. * * @return the rich text stream. */ public String getRichContents() { COSBase base = getCOSObject().getDictionaryObject(COSName.RC); if (base instanceof COSString) { return ((COSString) base).getString(); } else if (base instanceof COSStream) { return ((COSStream) base).asTextString(); } return null; } /** * This will set the rich text stream which is displayed in the popup window. * * @param rc the rich text stream. */ public void setRichContents(String rc) { getCOSObject().setItem(COSName.RC, COSString.parseLiteral(rc)); } /** * This will retrieve the date and time the annotation was created. * * @return the creation date/time. * @throws IOException if there is a format problem when converting the date. */ public Calendar getCreationDate() { return getCOSObject().getDate(COSName.CREATION_DATE); } /** * This will set the date and time the annotation was created. * * @param creationDate the date and time the annotation was created. */ public void setCreationDate(Calendar creationDate) { getCOSObject().setDate(COSName.CREATION_DATE, creationDate); } /** * This will retrieve the annotation to which this one is "In Reply To" the actual relationship is specified by the * RT entry. * * @return the other annotation or null if there is none. * @throws IOException if there is an error creating the other annotation. */ public PDAnnotation getInReplyTo() { return ofNullable(getCOSObject().getDictionaryObject("IRT", COSDictionary.class)) .map(PDAnnotation::createAnnotation).orElse(null); } /** * This will set the annotation to which this one is "In Reply To" the actual relationship is specified by the RT * entry. * * @param irt the annotation this one is "In Reply To". */ public void setInReplyTo(PDAnnotation irt) { getCOSObject().setItem("IRT", irt); } /** * This will retrieve the short description of the subject of the annotation. * * @return the subject. */ public String getSubject() { return getCOSObject().getString(COSName.SUBJ); } /** * This will set the short description of the subject of the annotation. * * @param subj short description of the subject. */ public void setSubject(String subj) { getCOSObject().setString(COSName.SUBJ, subj); } /** * This will retrieve the Reply Type (relationship) with the annotation in the IRT entry See the RT_* constants for * the available values. * * @return the relationship. */ public String getReplyType() { return getCOSObject().getNameAsString("RT", RT_REPLY); } /** * This will set the Reply Type (relationship) with the annotation in the IRT entry See the RT_* constants for the * available values. * * @param rt the reply type. */ public void setReplyType(String rt) { getCOSObject().setName("RT", rt); } /** * This will retrieve the intent of the annotation The values and meanings are specific to the actual annotation See * the IT_* constants for the annotation classes. * * @return the intent */ public String getIntent() { return getCOSObject().getNameAsString(COSName.IT); } /** * This will set the intent of the annotation The values and meanings are specific to the actual annotation See the * IT_* constants for the annotation classes. * * @param it the intent */ public void setIntent(String it) { getCOSObject().setName(COSName.IT, it); } /** * This will return the external data dictionary. * * @return the external data dictionary */ public PDExternalDataDictionary getExternalData() { COSBase exData = this.getCOSObject().getDictionaryObject("ExData"); if (exData instanceof COSDictionary) { return new PDExternalDataDictionary((COSDictionary) exData); } return null; } /** * This will set the external data dictionary. * * @param externalData the external data dictionary */ public void setExternalData(PDExternalDataDictionary externalData) { this.getCOSObject().setItem("ExData", externalData); } /** * This will set the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @param bs the border style dictionary to set. * */ public void setBorderStyle(PDBorderStyleDictionary bs) { this.getCOSObject().setItem(COSName.BS, bs); } /** * This will retrieve the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @return the border style dictionary. */ public PDBorderStyleDictionary getBorderStyle() { COSDictionary bs = this.getCOSObject().getDictionaryObject(COSName.BS, COSDictionary.class); if (bs != null) { return new PDBorderStyleDictionary(bs); } return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAnnotationPopup.java000066400000000000000000000055651320103431700332430ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Objects.nonNull; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This is the class that represents a popup annotation. Introduced in PDF 1.3 specification * * @author Paul King */ public class PDAnnotationPopup extends PDAnnotation { public static final String SUB_TYPE = "Popup"; /** * Constructor. */ public PDAnnotationPopup() { getCOSObject().setItem(COSName.SUBTYPE, COSName.getPDFName(SUB_TYPE)); } /** * Creates a popup annotation from a COSDictionary, expected to be a correct object definition. * * @param field the PDF objet to represent as a field. */ public PDAnnotationPopup(COSDictionary field) { super(field); } /** * This will set inital state of the annotation, open or closed. * * @param open Boolean value, true = open false = closed. */ public void setOpen(boolean open) { getCOSObject().setBoolean("Open", open); } /** * This will retrieve the initial state of the annotation, open Or closed (default closed). * * @return The initial state, true = open false = closed. */ public boolean getOpen() { return getCOSObject().getBoolean("Open", false); } /** * This will set the markup annotation which this popup relates to. * * @param annot the markup annotation. */ public void setParent(PDAnnotationMarkup annot) { getCOSObject().setItem(COSName.PARENT, annot.getCOSObject()); } /** * This will retrieve the markup annotation which this popup relates to. * * @return The parent markup annotation or null. */ public PDAnnotationMarkup getParent() { COSDictionary parent = getCOSObject().getDictionaryObject(COSName.PARENT, COSDictionary.class); if (nonNull(parent)) { return PDAnnotation.createAnnotation(parent, PDAnnotationMarkup.class); } return null; } } PDAnnotationRubberStamp.java000066400000000000000000000102331320103431700342730ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This is the class that represents a rubber stamp annotation. Introduced in PDF 1.3 specification * * @author Paul King */ public class PDAnnotationRubberStamp extends PDAnnotationMarkup { /* * The various values of the rubber stamp as defined in the PDF 1.6 reference Table 8.28 */ /** * Constant for the name of a rubber stamp. */ public static final String NAME_APPROVED = "Approved"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_EXPERIMENTAL = "Experimental"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_NOT_APPROVED = "NotApproved"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_AS_IS = "AsIs"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_EXPIRED = "Expired"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_NOT_FOR_PUBLIC_RELEASE = "NotForPublicRelease"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_FOR_PUBLIC_RELEASE = "ForPublicRelease"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_DRAFT = "Draft"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_FOR_COMMENT = "ForComment"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_TOP_SECRET = "TopSecret"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_DEPARTMENTAL = "Departmental"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_CONFIDENTIAL = "Confidential"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_FINAL = "Final"; /** * Constant for the name of a rubber stamp. */ public static final String NAME_SOLD = "Sold"; /** * The type of annotation. */ public static final String SUB_TYPE = "Stamp"; /** * Constructor. */ public PDAnnotationRubberStamp() { super(); getCOSObject().setItem(COSName.SUBTYPE, COSName.getPDFName(SUB_TYPE)); } /** * Creates a Rubber Stamp annotation from a COSDictionary, expected to be a correct object definition. * * @param field the PDF objet to represent as a field. */ public PDAnnotationRubberStamp(COSDictionary field) { super(field); } /** * This will set the name (and hence appearance, AP taking precedence) For this annotation. See the NAME_XXX * constants for valid values. * * @param name The name of the rubber stamp. */ public void setName(String name) { getCOSObject().setName(COSName.NAME, name); } /** * This will retrieve the name (and hence appearance, AP taking precedence) For this annotation. The default is * DRAFT. * * @return The name of this rubber stamp, see the NAME_XXX constants. */ public String getName() { return getCOSObject().getNameAsString(COSName.NAME, NAME_DRAFT); } } PDAnnotationSquareCircle.java000066400000000000000000000133011320103431700344260ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Optional.ofNullable; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.graphics.color.PDColor; /** * This is the class that represents a rectangular or eliptical annotation Introduced in PDF 1.3 specification . * * @author Paul King */ public class PDAnnotationSquareCircle extends PDAnnotationMarkup { /** * Constant for a Rectangular type of annotation. */ public static final String SUB_TYPE_SQUARE = "Square"; /** * Constant for an Eliptical type of annotation. */ public static final String SUB_TYPE_CIRCLE = "Circle"; /** * Creates a Circle or Square annotation of the specified sub type. * * @param subType the subtype the annotation represents. */ public PDAnnotationSquareCircle(String subType) { setSubtype(subType); } /** * Creates a Line annotation from a COSDictionary, expected to be a correct object definition. * * @param field the PDF objet to represent as a field. */ public PDAnnotationSquareCircle(COSDictionary field) { super(field); } /** * This will set interior color of the drawn area color is in DeviceRGB colo rspace. * * @param ic color in the DeviceRGB color space. * */ public void setInteriorColor(PDColor ic) { getCOSObject().setItem(COSName.IC, ic.toComponentsCOSArray()); } /** * This will retrieve the interior color of the drawn area color is in DeviceRGB color space. * * @return object representing the color. */ public PDColor getInteriorColor() { return getColor(COSName.IC); } /** * This will set the border effect dictionary, specifying effects to be applied when drawing the line. * * @param be The border effect dictionary to set. * */ public void setBorderEffect(PDBorderEffectDictionary be) { getCOSObject().setItem(COSName.BE, be); } /** * This will retrieve the border effect dictionary, specifying effects to be applied used in drawing the line. * * @return The border effect dictionary */ public PDBorderEffectDictionary getBorderEffect() { return ofNullable(getCOSObject().getDictionaryObject(COSName.BE, COSDictionary.class)) .map(PDBorderEffectDictionary::new).orElse(null); } /** * This will set the rectangle difference rectangle. Giving the difference between the annotations rectangle and * where the drawing occurs. (To take account of any effects applied through the BE entry forexample) * * @param rd the rectangle difference * */ public void setRectDifference(PDRectangle rd) { getCOSObject().setItem(COSName.RD, rd); } /** * This will get the rectangle difference rectangle. Giving the difference between the annotations rectangle and * where the drawing occurs. (To take account of any effects applied through the BE entry forexample) * * @return the rectangle difference */ public PDRectangle getRectDifference() { return ofNullable(getCOSObject().getDictionaryObject(COSName.RD, COSArray.class)) .map(PDRectangle::new).orElse(null); } /** * This will set the sub type (and hence appearance, AP taking precedence) For this annotation. See the SUB_TYPE_XXX * constants for valid values. * * @param subType The subtype of the annotation */ public void setSubtype(String subType) { getCOSObject().setName(COSName.SUBTYPE, subType); } /** * This will retrieve the sub type (and hence appearance, AP taking precedence) For this annotation. * * @return The subtype of this annotation, see the SUB_TYPE_XXX constants. */ @Override public String getSubtype() { return getCOSObject().getNameAsString(COSName.SUBTYPE); } /** * This will set the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @param bs the border style dictionary to set. TODO not all annotations may have a BS entry * */ @Override public void setBorderStyle(PDBorderStyleDictionary bs) { this.getCOSObject().setItem(COSName.BS, bs); } /** * This will retrieve the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @return the border style dictionary. TODO not all annotations may have a BS entry */ @Override public PDBorderStyleDictionary getBorderStyle() { return ofNullable(getCOSObject().getDictionaryObject(COSName.BS, COSDictionary.class)) .map(PDBorderStyleDictionary::new).orElse(null); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAnnotationText.java000066400000000000000000000112511320103431700330510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This is the class that represents a text annotation. * * @author Paul King */ public class PDAnnotationText extends PDAnnotationMarkup { /* * The various values of the Text as defined in the PDF 1.7 reference Table 172 */ /** * Constant for the name of a text annotation. */ public static final String NAME_COMMENT = "Comment"; /** * Constant for the name of a text annotation. */ public static final String NAME_KEY = "Key"; /** * Constant for the name of a text annotation. */ public static final String NAME_NOTE = "Note"; /** * Constant for the name of a text annotation. */ public static final String NAME_HELP = "Help"; /** * Constant for the name of a text annotation. */ public static final String NAME_NEW_PARAGRAPH = "NewParagraph"; /** * Constant for the name of a text annotation. */ public static final String NAME_PARAGRAPH = "Paragraph"; /** * Constant for the name of a text annotation. */ public static final String NAME_INSERT = "Insert"; /** * The type of annotation. */ public static final String SUB_TYPE = "Text"; /** * Constructor. */ public PDAnnotationText() { getCOSObject().setItem(COSName.SUBTYPE, COSName.getPDFName(SUB_TYPE)); } /** * Creates a Text annotation from a COSDictionary, expected to be a correct object definition. * * @param field the PDF object to represent as a field. */ public PDAnnotationText(COSDictionary field) { super(field); } /** * This will set initial state of the annotation, open or closed. * * @param open Boolean value, true = open false = closed */ public void setOpen(boolean open) { getCOSObject().setBoolean(COSName.getPDFName("Open"), open); } /** * This will retrieve the initial state of the annotation, open Or closed (default closed). * * @return The initial state, true = open false = closed */ public boolean getOpen() { return getCOSObject().getBoolean(COSName.getPDFName("Open"), false); } /** * This will set the name (and hence appearance, AP taking precedence) For this annotation. See the NAME_XXX * constants for valid values. * * @param name The name of the annotation */ public void setName(String name) { getCOSObject().setName(COSName.NAME, name); } /** * This will retrieve the name (and hence appearance, AP taking precedence) For this annotation. The default is * NOTE. * * @return The name of this annotation, see the NAME_XXX constants. */ public String getName() { return getCOSObject().getNameAsString(COSName.NAME, NAME_NOTE); } /** * This will retrieve the annotation state. * * @return the annotation state */ public String getState() { return this.getCOSObject().getString(COSName.STATE); } /** * This will set the annotation state. * * @param state the annotation state */ public void setState(String state) { this.getCOSObject().setString(COSName.STATE, state); } /** * This will retrieve the annotation state model. * * @return the annotation state model */ public String getStateModel() { return this.getCOSObject().getString(COSName.STATE_MODEL); } /** * This will set the annotation state model. Allowed values are "Marked" and "Review" * * @param stateModel the annotation state model */ public void setStateModel(String stateModel) { this.getCOSObject().setString(COSName.STATE_MODEL, stateModel); } } PDAnnotationTextMarkup.java000066400000000000000000000073601320103431700341600ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Optional.ofNullable; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This is the abstract class that represents a text markup annotation Introduced in PDF 1.3 specification, except * Squiggly lines in 1.4. * * @author Paul King */ public class PDAnnotationTextMarkup extends PDAnnotationMarkup { /** * The types of annotation. */ public static final String SUB_TYPE_HIGHLIGHT = "Highlight"; /** * The types of annotation. */ public static final String SUB_TYPE_UNDERLINE = "Underline"; /** * The types of annotation. */ public static final String SUB_TYPE_SQUIGGLY = "Squiggly"; /** * The types of annotation. */ public static final String SUB_TYPE_STRIKEOUT = "StrikeOut"; /** * Creates a TextMarkup annotation of the specified sub type. * * @param subType the subtype the annotation represents */ public PDAnnotationTextMarkup(String subType) { setSubtype(subType); // Quad points are required, set and empty array setQuadPoints(new float[0]); } /** * Creates a TextMarkup annotation from a COSDictionary, expected to be a correct object definition. * * @param field the PDF objet to represent as a field. */ public PDAnnotationTextMarkup(COSDictionary field) { super(field); } /** * This will set the set of quadpoints which encompass the areas of this annotation. * * @param quadPoints an array representing the set of area covered */ public void setQuadPoints(float[] quadPoints) { COSArray newQuadPoints = new COSArray(); newQuadPoints.setFloatArray(quadPoints); getCOSObject().setItem(COSName.QUADPOINTS, newQuadPoints); } /** * This will retrieve the set of quadpoints which encompass the areas of this annotation. * * @return An array of floats representing the quad points or null if not present. */ public float[] getQuadPoints() { return ofNullable(getCOSObject().getDictionaryObject(COSName.QUADPOINTS, COSArray.class)) .map(COSArray::toFloatArray).orElse(null); } /** * This will set the sub type (and hence appearance, AP taking precedence) For this annotation. See the SUB_TYPE_XXX * constants for valid values. * * @param subType The subtype of the annotation */ public void setSubtype(String subType) { getCOSObject().setName(COSName.SUBTYPE, subType); } /** * This will retrieve the sub type (and hence appearance, AP taking precedence) For this annotation. * * @return The subtype of this annotation, see the SUB_TYPE_XXX constants. */ @Override public String getSubtype() { return getCOSObject().getNameAsString(COSName.SUBTYPE); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAnnotationUnknown.java000066400000000000000000000025451320103431700335720ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import org.sejda.sambox.cos.COSDictionary; /** * This is the class that represents an arbitary Unknown Annotation type. * * @author Paul King */ public class PDAnnotationUnknown extends PDAnnotation { /** * Creates an arbitary annotation from a COSDictionary, expected to be a correct object definition for some sort of * annotation. * * @param dic The dictionary which represents this Annotation. */ public PDAnnotationUnknown(COSDictionary dic) { super(dic); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAnnotationWidget.java000066400000000000000000000161461320103431700333600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Objects.nonNull; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.interactive.action.PDAction; import org.sejda.sambox.pdmodel.interactive.action.PDActionFactory; import org.sejda.sambox.pdmodel.interactive.action.PDAnnotationAdditionalActions; /** * This is the class that represents a widget. * * @author Ben Litchfield */ public class PDAnnotationWidget extends PDAnnotation { public PDAnnotationWidget() { getCOSObject().setName(COSName.SUBTYPE, COSName.WIDGET.getName()); } public PDAnnotationWidget(COSDictionary field) { super(field); getCOSObject().setName(COSName.SUBTYPE, COSName.WIDGET.getName()); } /** * Returns the highlighting mode. Default value: I *
*
N
*
(None) No highlighting.
*
I
*
(Invert) Invert the contents of the annotation rectangle.
*
O
*
(Outline) Invert the annotation's border.
*
P
*
(Push) Display the annotation's down appearance, if any. If no down appearance is defined, the contents of * the annotation rectangle shall be offset to appear as if it were pushed below the surface of the page
*
T
*
(Toggle) Same as P (which is preferred).
*
* * @return the highlighting mode */ public String getHighlightingMode() { return this.getCOSObject().getNameAsString(COSName.H, "I"); } /** * Sets the highlighting mode. *
*
N
*
(None) No highlighting.
*
I
*
(Invert) Invert the contents of the annotation rectangle.
*
O
*
(Outline) Invert the annotation's border.
*
P
*
(Push) Display the annotation's down appearance, if any. If no down appearance is defined, the contents of * the annotation rectangle shall be offset to appear as if it were pushed below the surface of the page
*
T
*
(Toggle) Same as P (which is preferred).
*
* * @param highlightingMode the highlighting mode the defined values */ public void setHighlightingMode(String highlightingMode) { if ((highlightingMode == null) || "N".equals(highlightingMode) || "I".equals(highlightingMode) || "O".equals(highlightingMode) || "P".equals(highlightingMode) || "T".equals(highlightingMode)) { this.getCOSObject().setName(COSName.H, highlightingMode); } else { throw new IllegalArgumentException( "Valid values for highlighting mode are " + "'N', 'N', 'O', 'P' or 'T'"); } } /** * Returns the appearance characteristics dictionary. * * @return the appearance characteristics dictionary */ public PDAppearanceCharacteristicsDictionary getAppearanceCharacteristics() { COSDictionary mk = this.getCOSObject().getDictionaryObject(COSName.MK, COSDictionary.class); if (nonNull(mk)) { return new PDAppearanceCharacteristicsDictionary(mk); } return null; } /** * Sets the appearance characteristics dictionary. * * @param appearanceCharacteristics the appearance characteristics dictionary */ public void setAppearanceCharacteristics( PDAppearanceCharacteristicsDictionary appearanceCharacteristics) { this.getCOSObject().setItem(COSName.MK, appearanceCharacteristics); } /** * Get the action to be performed when this annotation is to be activated. * * @return The action to be performed when this annotation is activated. */ public PDAction getAction() { return PDActionFactory .createAction((COSDictionary) this.getCOSObject().getDictionaryObject(COSName.A)); } /** * Set the annotation action. As of PDF 1.6 this is only used for Widget Annotations * * @param action The annotation action. */ public void setAction(PDAction action) { this.getCOSObject().setItem(COSName.A, action); } /** * Get the additional actions for this field. This will return null if there are no additional actions for this * field. As of PDF 1.6 this is only used for Widget Annotations. * * @return The actions of the field. */ public PDAnnotationAdditionalActions getActions() { COSDictionary aa = (COSDictionary) this.getCOSObject().getDictionaryObject("AA"); if (aa != null) { return new PDAnnotationAdditionalActions(aa); } return null; } /** * Set the actions of the field. * * @param actions The field actions. */ public void setActions(PDAnnotationAdditionalActions actions) { this.getCOSObject().setItem("AA", actions); } /** * This will set the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @param bs the border style dictionary to set. * */ public void setBorderStyle(PDBorderStyleDictionary bs) { this.getCOSObject().setItem("BS", bs); } /** * This will retrieve the border style dictionary, specifying the width and dash pattern used in drawing the line. * * @return the border style dictionary. */ public PDBorderStyleDictionary getBorderStyle() { COSDictionary bs = this.getCOSObject().getDictionaryObject(COSName.BS, COSDictionary.class); if (bs != null) { return new PDBorderStyleDictionary(bs); } return null; } // TODO where to get acroForm from? // public PDField getParent() throws IOException // { // COSBase parent = this.getCOSObject().getDictionaryObject(COSName.PARENT); // if (parent instanceof COSDictionary) // { // PDAcroForm acroForm = null; // return PDFieldFactory.createField(acroForm, (COSDictionary) parent); // } // return null; // } } PDAppearanceCharacteristicsDictionary.java000066400000000000000000000134611320103431700371410ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Objects.nonNull; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceCMYK; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; /** * This class represents an appearance characteristics dictionary. * */ public class PDAppearanceCharacteristicsDictionary extends PDDictionaryWrapper { public PDAppearanceCharacteristicsDictionary(COSDictionary dict) { super(dict); } /** * This will retrieve the rotation of the annotation widget. It must be a multiple of 90. Default is 0 * * @return the rotation */ public int getRotation() { return this.getCOSObject().getInt(COSName.R, 0); } /** * This will set the rotation. * * @param rotation the rotation as a multiple of 90 */ public void setRotation(int rotation) { this.getCOSObject().setInt(COSName.R, rotation); } /** * This will retrieve the border color. * * @return the border color. */ public PDColor getBorderColour() { return getColor(COSName.BC); } /** * This will set the border color. * * @param c the border color */ public void setBorderColour(PDColor c) { this.getCOSObject().setItem(COSName.BC, c.toComponentsCOSArray()); } /** * This will retrieve the background color. * * @return the background color. */ public PDColor getBackground() { return getColor(COSName.BG); } /** * This will set the background color. * * @param c the background color */ public void setBackground(PDColor c) { this.getCOSObject().setItem(COSName.BG, c.toComponentsCOSArray()); } /** * This will retrieve the normal caption. * * @return the normal caption. */ public String getNormalCaption() { return this.getCOSObject().getString("CA"); } /** * This will set the normal caption. * * @param caption the normal caption */ public void setNormalCaption(String caption) { this.getCOSObject().setString("CA", caption); } /** * This will retrieve the rollover caption. * * @return the rollover caption. */ public String getRolloverCaption() { return this.getCOSObject().getString("RC"); } /** * This will set the rollover caption. * * @param caption the rollover caption */ public void setRolloverCaption(String caption) { this.getCOSObject().setString("RC", caption); } /** * This will retrieve the alternate caption. * * @return the alternate caption. */ public String getAlternateCaption() { return this.getCOSObject().getString("AC"); } /** * This will set the alternate caption. * * @param caption the alternate caption */ public void setAlternateCaption(String caption) { this.getCOSObject().setString("AC", caption); } /** * This will retrieve the normal icon. * * @return the normal icon. */ public PDFormXObject getNormalIcon() { COSStream i = this.getCOSObject().getDictionaryObject("I", COSStream.class); if (nonNull(i)) { return new PDFormXObject(i); } return null; } /** * This will retrieve the rollover icon. * * @return the rollover icon */ public PDFormXObject getRolloverIcon() { COSStream i = this.getCOSObject().getDictionaryObject("RI", COSStream.class); if (nonNull(i)) { return new PDFormXObject(i); } return null; } /** * This will retrieve the alternate icon. * * @return the alternate icon. */ public PDFormXObject getAlternateIcon() { COSStream i = this.getCOSObject().getDictionaryObject("IX", COSStream.class); if (nonNull(i)) { return new PDFormXObject(i); } return null; } private PDColor getColor(COSName itemName) { COSArray c = this.getCOSObject().getDictionaryObject(itemName, COSArray.class); if (nonNull(c)) { switch (c.size()) { case 1: return new PDColor(c, PDDeviceGray.INSTANCE); case 3: return new PDColor(c, PDDeviceRGB.INSTANCE); case 4: return new PDColor(c, PDDeviceCMYK.INSTANCE); default: break; } } return null; } } PDAppearanceDictionary.java000066400000000000000000000132711320103431700341040ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import static java.util.Objects.nonNull; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * An appearance dictionary specifying how the annotation shall be presented visually on the page. * * @author Ben Litchfield */ public class PDAppearanceDictionary implements COSObjectable { private final COSDictionary dictionary; /** * Constructor for embedding. */ public PDAppearanceDictionary() { dictionary = new COSDictionary(); // the N entry is required. dictionary.setItem(COSName.N, new COSDictionary()); } /** * Constructor for reading. * * @param dictionary The annotations dictionary. */ public PDAppearanceDictionary(COSDictionary dictionary) { this.dictionary = dictionary; } @Override public COSDictionary getCOSObject() { return dictionary; } /** * This will return a list of appearances. In the case where there is only one appearance the map will contain one * entry whose key is the string "default". * * @return A list of key(java.lang.String) value(PDAppearanceStream) pairs */ public PDAppearanceEntry getNormalAppearance() { COSBase entry = dictionary.getDictionaryObject(COSName.N); if (nonNull(entry)) { return new PDAppearanceEntry(entry); } return null; } /** * This will set a list of appearances. If you would like to set the single appearance then you should use the key * "default", and when the PDF is written back to the filesystem then there will only be one stream. * * @param entry appearance stream or subdictionary */ public void setNormalAppearance(PDAppearanceEntry entry) { dictionary.setItem(COSName.N, entry); } /** * This will set the normal appearance when there is only one appearance to be shown. * * @param ap The appearance stream to show. */ public void setNormalAppearance(PDAppearanceStream ap) { dictionary.setItem(COSName.N, ap); } /** * This will return a list of appearances. In the case where there is only one appearance the map will contain one * entry whose key is the string "default". If there is no rollover appearance then the normal appearance will be * returned. Which means that this method will never return null. * * @return A list of key(java.lang.String) value(PDAppearanceStream) pairs */ public PDAppearanceEntry getRolloverAppearance() { COSBase entry = dictionary.getDictionaryObject(COSName.R); if (nonNull(entry)) { return new PDAppearanceEntry(entry); } return getNormalAppearance(); } /** * This will set a list of appearances. If you would like to set the single appearance then you should use the key * "default", and when the PDF is written back to the filesystem then there will only be one stream. * * @param entry appearance stream or subdictionary */ public void setRolloverAppearance(PDAppearanceEntry entry) { dictionary.setItem(COSName.R, entry); } /** * This will set the rollover appearance when there is rollover appearance to be shown. * * @param ap The appearance stream to show. */ public void setRolloverAppearance(PDAppearanceStream ap) { dictionary.setItem(COSName.R, ap); } /** * This will return a list of appearances. In the case where there is only one appearance the map will contain one * entry whose key is the string "default". If there is no down appearance then the normal appearance will be * returned. Which means that this method will never return null. * * @return A list of key(java.lang.String) value(PDAppearanceStream) pairs */ public PDAppearanceEntry getDownAppearance() { COSBase entry = dictionary.getDictionaryObject(COSName.D); if (nonNull(entry)) { return new PDAppearanceEntry(entry); } return getNormalAppearance(); } /** * This will set a list of appearances. If you would like to set the single appearance then you should use the key * "default", and when the PDF is written back to the filesystem then there will only be one stream. * * @param entry appearance stream or subdictionary */ public void setDownAppearance(PDAppearanceEntry entry) { dictionary.setItem(COSName.D, entry); } /** * This will set the down appearance when there is down appearance to be shown. * * @param ap The appearance stream to show. */ public void setDownAppearance(PDAppearanceStream ap) { dictionary.setItem(COSName.D, ap); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAppearanceEntry.java000066400000000000000000000063061320103431700331600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.common.COSDictionaryMap; /** * An entry in an appearance dictionary. May contain either a single appearance stream or an appearance subdictionary. * * @author John Hewson */ public class PDAppearanceEntry implements COSObjectable { private COSBase entry; /** * * @param entry */ public PDAppearanceEntry(COSBase entry) { this.entry = entry; } @Override public COSBase getCOSObject() { return entry; } /** * Returns true if this entry is an appearance subdictionary. */ public boolean isSubDictionary() { return !(this.entry instanceof COSStream); } /** * Returns true if this entry is an appearance stream. */ public boolean isStream() { return this.entry instanceof COSStream; } /** * Returns the entry as an appearance stream. * * @throws IllegalStateException if this entry is not an appearance stream */ public PDAppearanceStream getAppearanceStream() { if (!isStream()) { throw new IllegalStateException(); } return new PDAppearanceStream((COSStream) entry); } /** * Returns the entry as an appearance subdictionary. * * @throws IllegalStateException if this entry is not an appearance subdictionary */ public Map getSubDictionary() { if (!isSubDictionary()) { throw new IllegalStateException(); } COSDictionary dict = (COSDictionary) entry; Map map = new HashMap(); for (COSName name : dict.keySet()) { COSBase value = dict.getDictionaryObject(name); // the file from PDFBOX-1599 contains /null as its entry, so we skip non-stream entries if (value instanceof COSStream) { map.put(name, new PDAppearanceStream((COSStream) value)); } } return new COSDictionaryMap(map, dict); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/PDAppearanceStream.java000066400000000000000000000026741320103431700333160ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; /** * An appearance stream is a form XObject, a self-contained content stream that shall be rendered inside the annotation * rectangle. * * @author Ben Litchfield * @author John Hewson */ public class PDAppearanceStream extends PDFormXObject { public PDAppearanceStream() { super(); } /** * Creates a Form XObject for reading. * * @param stream The XObject stream */ public PDAppearanceStream(COSStream stream) { super(stream); } } PDBorderEffectDictionary.java000066400000000000000000000056141320103431700344010ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSObjectable; /** * This class represents a PDF /BE entry the border effect dictionary. * * @author Paul King */ public class PDBorderEffectDictionary implements COSObjectable { /* * The various values of the effect applied to the border as defined in the PDF 1.6 reference Table 8.14 */ /** * Constant for the name for no effect. */ public static final String STYLE_SOLID = "S"; /** * Constant for the name of a cloudy effect. */ public static final String STYLE_CLOUDY = "C"; private final COSDictionary dictionary; /** * Constructor. */ public PDBorderEffectDictionary() { dictionary = new COSDictionary(); } /** * Constructor. * * @param dict a border style dictionary. */ public PDBorderEffectDictionary(COSDictionary dict) { dictionary = dict; } /** * returns the dictionary. * * @return the dictionary */ @Override public COSDictionary getCOSObject() { return dictionary; } /** * This will set the intensity of the applied effect. * * @param i the intensity of the effect values 0 to 2 */ public void setIntensity(float i) { getCOSObject().setFloat("I", i); } /** * This will retrieve the intensity of the applied effect. * * @return the intensity value 0 to 2 */ public float getIntensity() { return getCOSObject().getFloat("I", 0); } /** * This will set the border effect, see the STYLE_* constants for valid values. * * @param s the border effect to use */ public void setStyle(String s) { getCOSObject().setName("S", s); } /** * This will retrieve the border effect, see the STYLE_* constants for valid values. * * @return the effect of the border */ public String getStyle() { return getCOSObject().getNameAsString("S", STYLE_SOLID); } } PDBorderStyleDictionary.java000066400000000000000000000105051320103431700343000ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.graphics.PDLineDashPattern; /** * This class represents a PDF /BS entry the border style dictionary. * * @author Paul King */ public class PDBorderStyleDictionary implements COSObjectable { /* * The various values of the style for the border as defined in the PDF 1.6 reference Table 8.13 */ /** * Constant for the name of a solid style. */ public static final String STYLE_SOLID = "S"; /** * Constant for the name of a dashed style. */ public static final String STYLE_DASHED = "D"; /** * Constant for the name of a beveled style. */ public static final String STYLE_BEVELED = "B"; /** * Constant for the name of a inset style. */ public static final String STYLE_INSET = "I"; /** * Constant for the name of a underline style. */ public static final String STYLE_UNDERLINE = "U"; private final COSDictionary dictionary; /** * Constructor. */ public PDBorderStyleDictionary() { dictionary = new COSDictionary(); } /** * Constructor. * * @param dict a border style dictionary. */ public PDBorderStyleDictionary(COSDictionary dict) { dictionary = dict; } /** * returns the dictionary. * * @return the dictionary */ @Override public COSDictionary getCOSObject() { return dictionary; } /** * This will set the border width in points, 0 = no border. * * @param w float the width in points */ public void setWidth(float w) { // PDFBOX-3929 workaround if (w == (int) w) { getCOSObject().setInt("W", (int) w); } else { getCOSObject().setFloat("W", w); } } /** * This will retrieve the border width in points, 0 = no border. * * @return flaot the width of the border in points */ public float getWidth() { return getCOSObject().getFloat("W", 1); } /** * This will set the border style, see the STYLE_* constants for valid values. * * @param s the border style to use */ public void setStyle(String s) { getCOSObject().setName("S", s); } /** * This will retrieve the border style, see the STYLE_* constants for valid values. * * @return the style of the border */ public String getStyle() { return getCOSObject().getNameAsString("S", STYLE_SOLID); } /** * This will set the dash style used for drawing the border. * * @param dashArray the dash style to use */ public void setDashStyle(COSArray dashArray) { COSArray array = null; if (dashArray != null) { array = dashArray; } getCOSObject().setItem("D", array); } /** * This will retrieve the dash style used for drawing the border. * * @return the dash style of the border */ public PDLineDashPattern getDashStyle() { COSArray d = (COSArray) getCOSObject().getDictionaryObject("D"); if (d == null) { d = new COSArray(); d.add(COSInteger.THREE); getCOSObject().setItem("D", d); } return new PDLineDashPattern(d, 0); } } PDExternalDataDictionary.java000066400000000000000000000050101320103431700344110ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/annotation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.annotation; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This class represents an external data dictionary. * */ public class PDExternalDataDictionary implements COSObjectable { private final COSDictionary dataDictionary; /** * Constructor. */ public PDExternalDataDictionary() { this.dataDictionary = new COSDictionary(); this.dataDictionary.setName(COSName.TYPE, "ExData"); } /** * Constructor. * * @param dictionary Dictionary */ public PDExternalDataDictionary(COSDictionary dictionary) { this.dataDictionary = dictionary; } /** * returns the dictionary. * * @return the dictionary */ @Override public COSDictionary getCOSObject() { return this.dataDictionary; } /** * returns the type of the external data dictionary. It must be "ExData", if present * * @return the type of the external data dictionary */ public String getType() { return this.getCOSObject().getNameAsString(COSName.TYPE, "ExData"); } /** * returns the subtype of the external data dictionary. * * @return the subtype of the external data dictionary */ public String getSubtype() { return this.getCOSObject().getNameAsString(COSName.SUBTYPE); } /** * This will set the subtype of the external data dictionary. * * @param subtype the subtype of the external data dictionary */ public void setSubtype(String subtype) { this.getCOSObject().setName(COSName.SUBTYPE, subtype); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/000077500000000000000000000000001320103431700305275ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/000077500000000000000000000000001320103431700330505ustar00rootroot00000000000000PDDestination.java000066400000000000000000000070231320103431700363430ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.destination; import static java.util.Objects.nonNull; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.common.PDDestinationOrAction; /** * This represents a destination in a PDF document. * * @author Ben Litchfield */ public abstract class PDDestination implements PDDestinationOrAction { /** * This will create a new destination depending on the type of COSBase that is passed in. * * @param base The base level object. * * @return A new destination. * * @throws IOException If the base cannot be converted to a Destination. */ public static PDDestination create(COSBase base) throws IOException { if (nonNull(base)) { if (base instanceof COSString) { return new PDNamedDestination((COSString) base); } if (base instanceof COSName) { return new PDNamedDestination((COSName) base); } if (base instanceof COSArray && ((COSArray) base).size() > 1 && ((COSArray) base).getObject(1) instanceof COSName) { COSArray array = (COSArray) base; String typeString = ((COSName) array.getObject(1)).getName(); if (typeString.equals(PDPageFitDestination.TYPE) || typeString.equals(PDPageFitDestination.TYPE_BOUNDED)) { return new PDPageFitDestination(array); } if (typeString.equals(PDPageFitHeightDestination.TYPE) || typeString.equals(PDPageFitHeightDestination.TYPE_BOUNDED)) { return new PDPageFitHeightDestination(array); } if (typeString.equals(PDPageFitRectangleDestination.TYPE)) { return new PDPageFitRectangleDestination(array); } if (typeString.equals(PDPageFitWidthDestination.TYPE) || typeString.equals(PDPageFitWidthDestination.TYPE_BOUNDED)) { return new PDPageFitWidthDestination(array); } if (typeString.equals(PDPageXYZDestination.TYPE)) { return new PDPageXYZDestination(array); } throw new IOException("Unknown destination type: " + typeString); } throw new IOException("Cannot convert " + base + " to a destination"); } return null; } } PDNamedDestination.java000066400000000000000000000057661320103431700373240ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.destination; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; /** * This represents a destination to a page by referencing it with a name. * * @author Ben Litchfield */ public class PDNamedDestination extends PDDestination { private COSBase namedDestination; /** * Constructor. * * @param dest The named destination. */ public PDNamedDestination( COSString dest ) { namedDestination = dest; } /** * Constructor. * * @param dest The named destination. */ public PDNamedDestination( COSName dest ) { namedDestination = dest; } /** * Default constructor. */ public PDNamedDestination() { //default, so do nothing } /** * Default constructor. * * @param dest The named destination. */ public PDNamedDestination( String dest ) { namedDestination = COSString.parseLiteral(dest); } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ public COSBase getCOSObject() { return namedDestination; } /** * This will get the name of the destination. * * @return The name of the destination. */ public String getNamedDestination() { String retval = null; if( namedDestination instanceof COSString ) { retval = ((COSString)namedDestination).getString(); } else if( namedDestination instanceof COSName ) { retval = ((COSName)namedDestination).getName(); } return retval; } /** * Set the named destination. * * @param dest The new named destination. * * @throws IOException If there is an error setting the named destination. */ public void setNamedDestination(String dest) { if (dest == null) { namedDestination = null; } else { namedDestination = COSString.parseLiteral(dest); } } } PDPageDestination.java000066400000000000000000000124311320103431700371370ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.destination; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.PDPageTree; /** * This represents a destination to a page, see subclasses for specific parameters. * * @author Ben Litchfield */ public abstract class PDPageDestination extends PDDestination { /** * Storage for the page destination. */ protected COSArray array; /** * Constructor to create empty page destination. * */ protected PDPageDestination() { array = new COSArray(); } /** * Constructor to create empty page destination. * * @param arr A page destination array. */ protected PDPageDestination(COSArray arr) { array = arr; } /** * This will get the page for this destination. A page destination can either reference a page (for a local * destination) or a page number (when doing a remote destination to another PDF). If this object is referencing by * page number then this method will return null and {@link #getPageNumber()} should be used. * * @return The page for this destination. */ public PDPage getPage() { PDPage retval = null; if (array.size() > 0) { COSBase page = array.getObject(0); if (page instanceof COSDictionary) { retval = new PDPage((COSDictionary) page); } } return retval; } /** * Set the page for a local destination. For an external destination, call {@link #setPageNumber(int) * setPageNumber(int pageNumber)}. * * @param page The page for a local destination. */ public void setPage(PDPage page) { array.set(0, page); } /** * This will get the page number for this destination. A page destination can either reference a page (for a local * destination) or a page number (when doing a remote destination to another PDF). If this object is referencing by * page number then this method will return that number, otherwise -1 will be returned. * * @return The zero-based page number for this destination. */ public int getPageNumber() { int retval = -1; if (array.size() > 0) { COSBase page = array.getObject(0); if (page instanceof COSNumber) { retval = ((COSNumber) page).intValue(); } } return retval; } /** * Returns the page number for this destination, regardless of whether this is a page number or a reference to a * page. * * @see org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem * @return the 0-based page number, or -1 if the destination type is unknown. */ public int retrievePageNumber() { int retval = -1; if (array.size() > 0) { COSBase page = array.getObject(0); if (page instanceof COSNumber) { retval = ((COSNumber) page).intValue(); } else if (page instanceof COSDictionary) { COSBase parent = page; while (((COSDictionary) parent).getDictionaryObject(COSName.PARENT, COSName.P) != null) { parent = ((COSDictionary) parent).getDictionaryObject(COSName.PARENT, COSName.P); } // now parent is the pages node PDPageTree pages = new PDPageTree((COSDictionary) parent); return pages.indexOf(new PDPage((COSDictionary) page)); } } return retval; } /** * Set the page number for a remote destination. For an internal destination, call {@link #setPage(PDPage) * setPage(PDPage page)}. * * @param pageNumber The page for a remote destination. */ public void setPageNumber(int pageNumber) { array.set(0, COSInteger.get(pageNumber)); } /** * Convert this standard java object to a COS object. * * @return The cos object that matches this Java object. */ @Override public COSArray getCOSObject() { return array; } } PDPageFitDestination.java000066400000000000000000000050111320103431700375760ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.destination; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSName; /** * This represents a destination to a page and the page contents will be magnified to just * fit on the screen. * * @author Ben Litchfield */ public class PDPageFitDestination extends PDPageDestination { /** * The type of this destination. */ protected static final String TYPE = "Fit"; /** * The type of this destination. */ protected static final String TYPE_BOUNDED = "FitB"; /** * Default constructor. * */ public PDPageFitDestination() { super(); array.growToSize(2); array.set(1, COSName.getPDFName(TYPE)); } /** * Constructor from an existing destination array. * * @param arr The destination array. */ public PDPageFitDestination( COSArray arr ) { super( arr ); } /** * A flag indicating if this page destination should just fit bounding box of the PDF. * * @return true If the destination should fit just the bounding box. */ public boolean fitBoundingBox() { return TYPE_BOUNDED.equals( array.getName( 1 ) ); } /** * Set if this page destination should just fit the bounding box. The default is false. * * @param fitBoundingBox A flag indicating if this should fit the bounding box. */ public void setFitBoundingBox( boolean fitBoundingBox ) { array.growToSize( 2 ); if( fitBoundingBox ) { array.set(1, COSName.getPDFName(TYPE_BOUNDED)); } else { array.set(1, COSName.getPDFName(TYPE)); } } } PDPageFitHeightDestination.java000066400000000000000000000063101320103431700407320ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.destination; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; /** * This represents a destination to a page at a x location and the height is magnified * to just fit on the screen. * * @author Ben Litchfield */ public class PDPageFitHeightDestination extends PDPageDestination { /** * The type of this destination. */ protected static final String TYPE = "FitV"; /** * The type of this destination. */ protected static final String TYPE_BOUNDED = "FitBV"; /** * Default constructor. * */ public PDPageFitHeightDestination() { super(); array.growToSize(3); array.set(1, COSName.getPDFName(TYPE)); } /** * Constructor from an existing destination array. * * @param arr The destination array. */ public PDPageFitHeightDestination( COSArray arr ) { super( arr ); } /** * Get the left x coordinate. A return value of -1 implies that the current x-coordinate * will be used. * * @return The left x coordinate. */ public int getLeft() { return array.getInt( 2 ); } /** * Set the left x-coordinate, a value of -1 implies that the current x-coordinate * will be used. * @param x The left x coordinate. */ public void setLeft( int x ) { array.growToSize( 3 ); if( x == -1 ) { array.set(2, null); } else { array.set(2, COSInteger.get(x)); } } /** * A flag indicating if this page destination should just fit bounding box of the PDF. * * @return true If the destination should fit just the bounding box. */ public boolean fitBoundingBox() { return TYPE_BOUNDED.equals( array.getName( 1 ) ); } /** * Set if this page destination should just fit the bounding box. The default is false. * * @param fitBoundingBox A flag indicating if this should fit the bounding box. */ public void setFitBoundingBox( boolean fitBoundingBox ) { array.growToSize( 2 ); if( fitBoundingBox ) { array.set(1, COSName.getPDFName(TYPE_BOUNDED)); } else { array.set(1, COSName.getPDFName(TYPE)); } } } PDPageFitRectangleDestination.java000066400000000000000000000103221320103431700414240ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.destination; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; /** * This represents a destination to a page at a y location and the width is magnified * to just fit on the screen. * * @author Ben Litchfield */ public class PDPageFitRectangleDestination extends PDPageDestination { /** * The type of this destination. */ protected static final String TYPE = "FitR"; /** * Default constructor. * */ public PDPageFitRectangleDestination() { super(); array.growToSize(6); array.set(1, COSName.getPDFName(TYPE)); } /** * Constructor from an existing destination array. * * @param arr The destination array. */ public PDPageFitRectangleDestination( COSArray arr ) { super( arr ); } /** * Get the left x coordinate. A return value of -1 implies that the current x-coordinate * will be used. * * @return The left x coordinate. */ public int getLeft() { return array.getInt( 2 ); } /** * Set the left x-coordinate, a value of -1 implies that the current x-coordinate * will be used. * @param x The left x coordinate. */ public void setLeft( int x ) { array.growToSize( 3 ); if( x == -1 ) { array.set(2, null); } else { array.set(2, COSInteger.get(x)); } } /** * Get the bottom y coordinate. A return value of -1 implies that the current y-coordinate * will be used. * * @return The bottom y coordinate. */ public int getBottom() { return array.getInt( 3 ); } /** * Set the bottom y-coordinate, a value of -1 implies that the current y-coordinate * will be used. * @param y The bottom y coordinate. */ public void setBottom( int y ) { array.growToSize( 6 ); if( y == -1 ) { array.set(3, null); } else { array.set(3, COSInteger.get(y)); } } /** * Get the right x coordinate. A return value of -1 implies that the current x-coordinate * will be used. * * @return The right x coordinate. */ public int getRight() { return array.getInt( 4 ); } /** * Set the right x-coordinate, a value of -1 implies that the current x-coordinate * will be used. * @param x The right x coordinate. */ public void setRight( int x ) { array.growToSize( 6 ); if( x == -1 ) { array.set(4, null); } else { array.set(4, COSInteger.get(x)); } } /** * Get the top y coordinate. A return value of -1 implies that the current y-coordinate * will be used. * * @return The top y coordinate. */ public int getTop() { return array.getInt( 5 ); } /** * Set the top y-coordinate, a value of -1 implies that the current y-coordinate * will be used. * @param y The top ycoordinate. */ public void setTop( int y ) { array.growToSize( 6 ); if( y == -1 ) { array.set(5, null); } else { array.set(5, COSInteger.get(y)); } } } PDPageFitWidthDestination.java000066400000000000000000000062771320103431700406150ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.destination; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; /** * This represents a destination to a page at a y location and the width is magnified * to just fit on the screen. * * @author Ben Litchfield */ public class PDPageFitWidthDestination extends PDPageDestination { /** * The type of this destination. */ protected static final String TYPE = "FitH"; /** * The type of this destination. */ protected static final String TYPE_BOUNDED = "FitBH"; /** * Default constructor. * */ public PDPageFitWidthDestination() { super(); array.growToSize(3); array.set(1, COSName.getPDFName(TYPE)); } /** * Constructor from an existing destination array. * * @param arr The destination array. */ public PDPageFitWidthDestination( COSArray arr ) { super( arr ); } /** * Get the top y coordinate. A return value of -1 implies that the current y-coordinate * will be used. * * @return The top y coordinate. */ public int getTop() { return array.getInt( 2 ); } /** * Set the top y-coordinate, a value of -1 implies that the current y-coordinate * will be used. * @param y The top ycoordinate. */ public void setTop( int y ) { array.growToSize( 3 ); if( y == -1 ) { array.set(2, null); } else { array.set(2, COSInteger.get(y)); } } /** * A flag indicating if this page destination should just fit bounding box of the PDF. * * @return true If the destination should fit just the bounding box. */ public boolean fitBoundingBox() { return TYPE_BOUNDED.equals( array.getName( 1 ) ); } /** * Set if this page destination should just fit the bounding box. The default is false. * * @param fitBoundingBox A flag indicating if this should fit the bounding box. */ public void setFitBoundingBox( boolean fitBoundingBox ) { array.growToSize( 2 ); if( fitBoundingBox ) { array.set(1, COSName.getPDFName(TYPE_BOUNDED)); } else { array.set(1, COSName.getPDFName(TYPE)); } } } PDPageXYZDestination.java000066400000000000000000000075551320103431700375650ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.destination; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; /** * This represents a destination to a page at an x,y coordinate with a zoom setting. * The default x,y,z will be whatever is the current value in the viewer application and * are not required. * * @author Ben Litchfield */ public class PDPageXYZDestination extends PDPageDestination { /** * The type of this destination. */ protected static final String TYPE = "XYZ"; /** * Default constructor. * */ public PDPageXYZDestination() { super(); array.growToSize(5); array.set(1, COSName.getPDFName(TYPE)); } /** * Constructor from an existing destination array. * * @param arr The destination array. */ public PDPageXYZDestination( COSArray arr ) { super( arr ); } /** * Get the left x coordinate. Return values of 0 or -1 imply that the current x-coordinate * will be used. * * @return The left x coordinate. */ public int getLeft() { return array.getInt( 2 ); } /** * Set the left x-coordinate, values 0 or -1 imply that the current x-coordinate * will be used. * @param x The left x coordinate. */ public void setLeft( int x ) { array.growToSize( 3 ); if( x == -1 ) { array.set(2, null); } else { array.set(2, COSInteger.get(x)); } } /** * Get the top y coordinate. Return values of 0 or -1 imply that the current y-coordinate * will be used. * * @return The top y coordinate. */ public int getTop() { return array.getInt( 3 ); } /** * Set the top y-coordinate, values 0 or -1 imply that the current y-coordinate * will be used. * @param y The top ycoordinate. */ public void setTop( int y ) { array.growToSize( 4 ); if( y == -1 ) { array.set(3, null); } else { array.set(3, COSInteger.get(y)); } } /** * Get the zoom value. Return values of 0 or -1 imply that the current zoom * will be used. * * @return The zoom value for the page. */ public float getZoom() { COSBase obj = array.getObject(4); if (obj instanceof COSNumber) { return ((COSNumber) obj).floatValue(); } return -1; } /** * Set the zoom value for the page, values 0 or -1 imply that the current zoom * will be used. * @param zoom The zoom value. */ public void setZoom( float zoom ) { array.growToSize( 5 ); if( zoom == -1 ) { array.set(4, null); } else { array.set( 4, new COSFloat(zoom) ); } } } package.html000066400000000000000000000017361320103431700352610ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/destination The destination package allows destinations into a pdf document to be specified. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/outline/000077500000000000000000000000001320103431700322065ustar00rootroot00000000000000PDDocumentOutline.java000066400000000000000000000035331320103431700363400ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/outline/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.outline; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This represents an outline in a pdf document. * * @author Ben Litchfield */ public final class PDDocumentOutline extends PDOutlineNode { public PDDocumentOutline() { getCOSObject().setName(COSName.TYPE, COSName.OUTLINES.getName()); } /** * Constructor for an existing document outline. * * @param dic The storage dictionary. */ public PDDocumentOutline(COSDictionary dic) { super(dic); getCOSObject().setName(COSName.TYPE, COSName.OUTLINES.getName()); } @Override public boolean isNodeOpen() { return true; } @Override public void openNode() { // The root of the outline hierarchy is not an OutlineItem and cannot be opened or closed } @Override public void closeNode() { // The root of the outline hierarchy is not an OutlineItem and cannot be opened or closed } } PDOutlineItem.java000066400000000000000000000274631320103431700354700ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/outline/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.outline; import java.awt.Color; import java.io.IOException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.documentinterchange.logicalstructure.PDStructureElement; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; import org.sejda.sambox.pdmodel.interactive.action.PDAction; import org.sejda.sambox.pdmodel.interactive.action.PDActionFactory; import org.sejda.sambox.pdmodel.interactive.action.PDActionGoTo; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDDestination; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDPageDestination; import org.sejda.sambox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination; /** * This represents an outline item in a pdf document. The items at each level of the hierarchy form an iterable linked * list, chained together through their Prev and Next entries. * * @author Ben Litchfield */ public final class PDOutlineItem extends PDOutlineNode { private static final int ITALIC_FLAG = 1; private static final int BOLD_FLAG = 2; public PDOutlineItem() { super(); } /** * Constructor for an existing outline item. * * @param dic The storage dictionary. */ public PDOutlineItem(COSDictionary dic) { super(dic); } /** * Insert a single sibling after this node. * * @param newSibling The item to insert. * @throws IllegalArgumentException if the given sibling node is part of a list (i.e. if it has a previous or a next * sibling) */ public void insertSiblingAfter(PDOutlineItem newSibling) { requireSingleNode(newSibling); PDOutlineNode parent = getParent(); newSibling.setParent(parent); PDOutlineItem next = getNextSibling(); setNextSibling(newSibling); newSibling.setPreviousSibling(this); if (next != null) { newSibling.setNextSibling(next); next.setPreviousSibling(newSibling); } else if (parent != null) { getParent().setLastChild(newSibling); } updateParentOpenCountForAddedChild(newSibling); } /** * Insert a single sibling before this node. * * @param newSibling The item to insert. * @throws IllegalArgumentException if the given sibling node is part of a list (i.e. if it has a previous or a next * sibling) */ public void insertSiblingBefore(PDOutlineItem newSibling) { requireSingleNode(newSibling); PDOutlineNode parent = getParent(); newSibling.setParent(parent); PDOutlineItem previous = getPreviousSibling(); setPreviousSibling(newSibling); newSibling.setNextSibling(this); if (previous != null) { previous.setNextSibling(newSibling); newSibling.setPreviousSibling(previous); } else if (parent != null) { getParent().setFirstChild(newSibling); } updateParentOpenCountForAddedChild(newSibling); } /** * Return the previous sibling or null if there is no sibling. * * @return The previous sibling. */ public PDOutlineItem getPreviousSibling() { return getOutlineItem(COSName.PREV); } /** * Set the previous sibling, this will be maintained by this class. * * @param outlineNode The new previous sibling. */ void setPreviousSibling(PDOutlineNode outlineNode) { getCOSObject().setItem(COSName.PREV, outlineNode); } /** * @return The next sibling or null if there is no next sibling. */ public PDOutlineItem getNextSibling() { return getOutlineItem(COSName.NEXT); } /** * Set the next sibling, this will be maintained by this class. * * @param outlineNode The new next sibling. */ void setNextSibling(PDOutlineNode outlineNode) { getCOSObject().setItem(COSName.NEXT, outlineNode); } /** * Get the title of this node. * * @return The title of this node. */ public String getTitle() { return getCOSObject().getString(COSName.TITLE); } /** * Set the title for this node. * * @param title The new title for this node. */ public void setTitle(String title) { getCOSObject().setString(COSName.TITLE, title); } /** * Get the page destination of this node. * * @return The page destination of this node. * @throws IOException If there is an error creating the destination. */ public PDDestination getDestination() throws IOException { return PDDestination.create(getCOSObject().getDictionaryObject(COSName.DEST)); } /** * Set the page destination for this node. * * @param dest The new page destination for this node. */ public void setDestination(PDDestination dest) { getCOSObject().setItem(COSName.DEST, dest); } /** * A convenience method that will create an XYZ destination using only the defaults. * * @param page The page to refer to. */ public void setDestination(PDPage page) { PDPageXYZDestination dest = null; if (page != null) { dest = new PDPageXYZDestination(); dest.setPage(page); } setDestination(dest); } /** * This method will attempt to find the page in this PDF document that this outline points to. If the outline does * not point to anything then this method will return null. If the outline is an action that is not a GoTo action * then this method will also return null. * * @param doc The document to get the page from. * * @return The page that this outline will go to when activated or null if it does not point to anything. * @throws IOException If there is an error when trying to find the page. */ public PDPage findDestinationPage(PDDocument doc) throws IOException { PDDestination dest = getDestination(); if (dest == null) { PDAction outlineAction = getAction(); if (outlineAction instanceof PDActionGoTo) { dest = ((PDActionGoTo) outlineAction).getDestination(); } } if (dest == null) { return null; } PDPageDestination pageDestination = null; if (dest instanceof PDNamedDestination) { pageDestination = doc.getDocumentCatalog().findNamedDestinationPage( (PDNamedDestination) dest); if (pageDestination == null) { return null; } } else if (dest instanceof PDPageDestination) { pageDestination = (PDPageDestination) dest; } else { throw new IOException("Error: Unknown destination type " + dest); } PDPage page = pageDestination.getPage(); if (page == null) { // Malformed PDF: local destinations must have a page object, // not a page number, these are meant for remote destinations. int pageNumber = pageDestination.getPageNumber(); if (pageNumber != -1) { page = doc.getPage(pageNumber); } } return page; } /** * Get the action of this node. * * @return The action of this node. */ public PDAction getAction() { return PDActionFactory.createAction((COSDictionary) getCOSObject().getDictionaryObject( COSName.A)); } /** * Set the action for this node. * * @param action The new action for this node. */ public void setAction(PDAction action) { getCOSObject().setItem(COSName.A, action); } /** * Get the structure element of this node. * * @return The structure element of this node. */ public PDStructureElement getStructureElement() { PDStructureElement se = null; COSDictionary dic = (COSDictionary) getCOSObject().getDictionaryObject(COSName.SE); if (dic != null) { se = new PDStructureElement(dic); } return se; } /** * Set the structure element for this node. * * @param structureElement The new structure element for this node. */ public void setStructureElement(PDStructureElement structureElement) { getCOSObject().setItem(COSName.SE, structureElement); } /** * Get the RGB text color of this node. Default is black and this method will never return null. * * @return The structure element of this node. */ public PDColor getTextColor() { COSArray csValues = (COSArray) getCOSObject().getDictionaryObject(COSName.C); if (csValues == null) { csValues = new COSArray(); csValues.growToSize(3, new COSFloat(0)); getCOSObject().setItem(COSName.C, csValues); } return new PDColor(csValues, PDDeviceRGB.INSTANCE); } /** * Set the RGB text color for this node. * * @param textColor The text color for this node. */ public void setTextColor(PDColor textColor) { getCOSObject().setItem(COSName.C, textColor.toComponentsCOSArray()); } /** * Set the RGB text color for this node. * * @param textColor The text color for this node. */ public void setTextColor(Color textColor) { COSArray array = new COSArray(); array.add(new COSFloat(textColor.getRed() / 255f)); array.add(new COSFloat(textColor.getGreen() / 255f)); array.add(new COSFloat(textColor.getBlue() / 255f)); getCOSObject().setItem(COSName.C, array); } /** * A flag telling if the text should be italic. * * @return The italic flag. */ public boolean isItalic() { return getCOSObject().getFlag(COSName.F, ITALIC_FLAG); } /** * Set the italic property of the text. * * @param italic The new italic flag. */ public void setItalic(boolean italic) { getCOSObject().setFlag(COSName.F, ITALIC_FLAG, italic); } /** * A flag telling if the text should be bold. * * @return The bold flag. */ public boolean isBold() { return getCOSObject().getFlag(COSName.F, BOLD_FLAG); } /** * Set the bold property of the text. * * @param bold The new bold flag. */ public void setBold(boolean bold) { getCOSObject().setFlag(COSName.F, BOLD_FLAG, bold); } } PDOutlineItemIterator.java000066400000000000000000000035571320103431700372000ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/outline/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.outline; import java.util.Iterator; /** * Iterator over the linked list of {@link PDOutlineItem} siblings. * * @author Andrea Vacondio * */ class PDOutlineItemIterator implements Iterator { private PDOutlineItem currentItem; private final PDOutlineItem startingItem; PDOutlineItemIterator(PDOutlineItem startingItem) { this.startingItem = startingItem; } @Override public boolean hasNext() { return startingItem != null && (currentItem == null || (currentItem.getNextSibling() != null && !startingItem .equals(currentItem.getNextSibling()))); } @Override public PDOutlineItem next() { if (currentItem == null) { currentItem = startingItem; } else { currentItem = currentItem.getNextSibling(); } return currentItem; } @Override public void remove() { throw new UnsupportedOperationException(); } } PDOutlineNode.java000066400000000000000000000207121320103431700354450ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/outline/* * 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. */ package org.sejda.sambox.pdmodel.interactive.documentnavigation.outline; import static java.util.Optional.ofNullable; import java.util.Iterator; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; /** * Base class for a node in the outline of a PDF document. * * @author Ben Litchfield */ public abstract class PDOutlineNode extends PDDictionaryWrapper { public PDOutlineNode() { super(); } /** * @param dict The dictionary storage. */ public PDOutlineNode(COSDictionary dict) { super(dict); } /** * @return The parent of this node or null if there is no parent. */ PDOutlineNode getParent() { COSDictionary item = getCOSObject().getDictionaryObject(COSName.PARENT, COSDictionary.class); if (item != null) { if (COSName.OUTLINES.equals(item.getCOSName(COSName.TYPE))) { return new PDDocumentOutline(item); } return new PDOutlineItem(item); } return null; } void setParent(PDOutlineNode parent) { getCOSObject().setItem(COSName.PARENT, parent); } /** * Adds the given node to the bottom of the children list. * * @param newChild The node to add. * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next * sibling) */ public void addLast(PDOutlineItem newChild) { requireSingleNode(newChild); append(newChild); updateParentOpenCountForAddedChild(newChild); } /** * Adds the given node to the top of the children list. * * @param newChild The node to add. * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next * sibling) */ public void addFirst(PDOutlineItem newChild) { requireSingleNode(newChild); prepend(newChild); updateParentOpenCountForAddedChild(newChild); } /** * @param node * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next * sibling) */ void requireSingleNode(PDOutlineItem node) { if (node.getNextSibling() != null || node.getPreviousSibling() != null) { throw new IllegalArgumentException("A single node with no siblings is required"); } } /** * Appends the child to the linked list of children. This method only adjust pointers but doesn't take care of the * Count key in the parent hierarchy. * * @param newChild */ private void append(PDOutlineItem newChild) { newChild.setParent(this); if (!hasChildren()) { setFirstChild(newChild); } else { PDOutlineItem previousLastChild = getLastChild(); previousLastChild.setNextSibling(newChild); newChild.setPreviousSibling(previousLastChild); } setLastChild(newChild); } /** * Prepends the child to the linked list of children. This method only adjust pointers but doesn't take care of the * Count key in the parent hierarchy. * * @param newChild */ private void prepend(PDOutlineItem newChild) { newChild.setParent(this); if (!hasChildren()) { setLastChild(newChild); } else { PDOutlineItem previousFirstChild = getFirstChild(); newChild.setNextSibling(previousFirstChild); previousFirstChild.setPreviousSibling(newChild); } setFirstChild(newChild); } void updateParentOpenCountForAddedChild(PDOutlineItem newChild) { int delta = 1; if (newChild.isNodeOpen()) { delta += newChild.getOpenCount(); } newChild.updateParentOpenCount(delta); } /** * @return true if the node has at least one child */ public boolean hasChildren() { return getFirstChild() != null; } PDOutlineItem getOutlineItem(COSName name) { return ofNullable(getCOSObject().getDictionaryObject(name, COSDictionary.class)) .map(PDOutlineItem::new).orElse(null); } /** * @return The first child or null if there is no child. */ public PDOutlineItem getFirstChild() { return getOutlineItem(COSName.FIRST); } /** * Set the first child, this will be maintained by this class. * * @param outlineNode The new first child. */ void setFirstChild(PDOutlineNode outlineNode) { getCOSObject().setItem(COSName.FIRST, outlineNode); } /** * @return The last child or null if there is no child. */ public PDOutlineItem getLastChild() { return getOutlineItem(COSName.LAST); } /** * Set the last child, this will be maintained by this class. * * @param outlineNode The new last child. */ void setLastChild(PDOutlineNode outlineNode) { getCOSObject().setItem(COSName.LAST, outlineNode); } /** * Get the number of open nodes or a negative number if this node is closed. See PDF Reference 32000-1:2008 table * 152 and 153 for more details. This value is updated as you append children and siblings. * * @return The Count attribute of the outline dictionary. */ public int getOpenCount() { return getCOSObject().getInt(COSName.COUNT, 0); } /** * Set the open count. This number is automatically managed for you when you add items to the outline. * * @param openCount The new open count. */ void setOpenCount(int openCount) { getCOSObject().setInt(COSName.COUNT, openCount); } /** * This will set this node to be open when it is shown in the viewer. By default, when a new node is created it will * be closed. This will do nothing if the node is already open. */ public void openNode() { // if the node is already open then do nothing. if (!isNodeOpen()) { switchNodeCount(); } } /** * Close this node. * */ public void closeNode() { if (isNodeOpen()) { switchNodeCount(); } } private void switchNodeCount() { int openCount = getOpenCount(); setOpenCount(-openCount); updateParentOpenCount(-openCount); } /** * @return true if this node count is greater than zero, false otherwise. */ public boolean isNodeOpen() { return getOpenCount() > 0; } /** * The count parameter needs to be updated when you add, remove, open or close outline items. * * @param delta The amount to update by. */ void updateParentOpenCount(int delta) { PDOutlineNode parent = getParent(); if (parent != null) { if (parent.isNodeOpen()) { parent.setOpenCount(parent.getOpenCount() + delta); parent.updateParentOpenCount(delta); } else { parent.setOpenCount(parent.getOpenCount() - delta); } } } /** * @return An {@link Iterable} view of the items children */ public Iterable children() { return new Iterable() { @Override public Iterator iterator() { return new PDOutlineItemIterator(getFirstChild()); } }; } } package.html000066400000000000000000000017241320103431700344140ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/outline The outline package allows for a PDF outline(bookmarks) to be created. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/documentnavigation/package.html000066400000000000000000000017331320103431700330140ustar00rootroot00000000000000 A package to allow access to document level navigation within a PDF document. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/000077500000000000000000000000001320103431700255745ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/AppearanceGeneratorHelper.java000066400000000000000000000735641320103431700335240ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import static java.util.Arrays.asList; import static java.util.Objects.nonNull; import static org.sejda.io.CountingWritableByteChannel.from; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.input.ContentStreamParser; import org.sejda.sambox.output.ContentStreamWriter; import org.sejda.sambox.pdmodel.PDPageContentStream; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.interactive.action.PDFormFieldAdditionalActions; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationWidget; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceDictionary; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceEntry; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceStream; import org.sejda.sambox.pdmodel.interactive.annotation.PDBorderStyleDictionary; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Create the AcroForms field appearance helper. * * @author Stephan Gerhard * @author Ben Litchfield */ class AppearanceGeneratorHelper { private static final Logger LOG = LoggerFactory.getLogger(AppearanceGeneratorHelper.class); private static final Operator BMC = Operator.getOperator("BMC"); private static final Operator EMC = Operator.getOperator("EMC"); private final PDVariableText field; private PDDefaultAppearanceString defaultAppearance; private String value; /** * The highlight color * * The color setting is used by Adobe to display the highlight box for selected entries in a list box. * * Regardless of other settings in an existing appearance stream Adobe will always use this value. */ private static final int[] HIGHLIGHT_COLOR = { 153, 193, 215 }; /** * The scaling factor for font units to PDF units */ private static final int FONTSCALE = 1000; /** * The default font size used for multiline text */ private static final float DEFAULT_FONT_SIZE = 12; /** * The default padding applied by Acrobat to the fields bbox. */ private static final float DEFAULT_PADDING = 0.5f; /** * Constructs a COSAppearance from the given field. * * @param field the field which you wish to control the appearance of * @throws IOException */ AppearanceGeneratorHelper(PDVariableText field) throws IOException { this.field = field; validateAndEnsureAcroFormResources(); this.defaultAppearance = field.getDefaultAppearanceString(); } /* * Adobe Reader/Acrobat are adding resources which are at the field/widget level to the AcroForm level. */ private void validateAndEnsureAcroFormResources() { // add font resources which might be available at the field // level but are not at the AcroForm level to the AcroForm // to match Adobe Reader/Acrobat behavior if (field.getAcroForm().getDefaultResources() == null) { return; } PDResources acroFormResources = field.getAcroForm().getDefaultResources(); for (PDAnnotationWidget widget : field.getWidgets()) { if (widget.getNormalAppearanceStream() != null && widget.getNormalAppearanceStream().getResources() != null) { PDResources widgetResources = widget.getNormalAppearanceStream().getResources(); for (COSName fontResourceName : widgetResources.getFontNames()) { try { if (acroFormResources.getFont(fontResourceName) == null) { LOG.debug("Adding font resource " + fontResourceName + " from widget to AcroForm"); acroFormResources.put(fontResourceName, widgetResources.getFont(fontResourceName)); } } catch (IOException e) { LOG.warn("Unable to match field level font with AcroForm font"); } } } } } /** * This is the public method for setting the appearance stream. * * @param apValue the String value which the appearance should represent * @throws IOException If there is an error creating the stream. */ public void setAppearanceValue(String apValue) throws IOException { value = apValue; // Treat multiline field values in single lines as single lime values. // This is in line with how Adobe Reader behaves when enetring text // interactively but NOT how it behaves when the field value has been // set programmatically and Reader is forced to generate the appearance // using PDAcroForm.setNeedAppearances // see PDFBOX-3911 if (field instanceof PDTextField && !((PDTextField) field).isMultiline()) { value = apValue.replaceAll( "\\u000D\\u000A|[\\u000A\\u000B\\u000C\\u000D\\u0085\\u2028\\u2029]", " "); } for (PDAnnotationWidget widget : field.getWidgets()) { // some fields have the /Da at the widget level if the // widgets differ in layout. PDDefaultAppearanceString acroFormAppearance = defaultAppearance; if (widget.getCOSObject().getDictionaryObject(COSName.DA) != null) { defaultAppearance = getWidgetDefaultAppearanceString(widget); } PDRectangle rect = widget.getRectangle(); if (rect == null) { widget.getCOSObject().removeItem(COSName.AP); LOG.warn("widget of field {} has no rectangle, no appearance stream created", field.getFullyQualifiedName()); continue; } PDFormFieldAdditionalActions actions = field.getActions(); // in case all tests fail the field will be formatted by acrobat // when it is opened. See FreedomExpressions.pdf for an example of this. if (actions == null || actions.getF() == null || widget.getCOSObject().getDictionaryObject(COSName.AP) != null) { PDAppearanceDictionary appearanceDict = widget.getAppearance(); if (appearanceDict == null) { appearanceDict = new PDAppearanceDictionary(); widget.setAppearance(appearanceDict); } PDAppearanceEntry appearance = appearanceDict.getNormalAppearance(); // TODO support appearances other than "normal" PDAppearanceStream appearanceStream; if (nonNull(appearance) && appearance.isStream()) { appearanceStream = appearance.getAppearanceStream(); } else { appearanceStream = new PDAppearanceStream(); // Calculate the entries for the bounding box and the transformation matrix // settings for the appearance stream int rotation = resolveRotation(widget); Matrix matrix = Matrix.getRotateInstance(Math.toRadians(rotation), 0, 0); Point2D.Float point2D = matrix.transformPoint(rect.getWidth(), rect.getHeight()); PDRectangle bbox = new PDRectangle(Math.abs((float) point2D.getX()), Math.abs((float) point2D.getY())); appearanceStream.setBBox(bbox); appearanceStream.setMatrix(calculateMatrix(bbox, rotation)); appearanceStream.setFormType(1); appearanceStream.setResources(new PDResources()); appearanceDict.setNormalAppearance(appearanceStream); // TODO support appearances other than "normal" } /* * Adobe Acrobat always recreates the complete appearance stream if there is an appearance * characteristics entry (the widget dictionaries MK entry). In addition if there is no content yet also * create the apperance stream from the entries. */ if (widget.getAppearanceCharacteristics() != null || appearanceStream.getContentStream().getLength() == 0) { initializeAppearanceContent(widget, appearanceStream); } setAppearanceContent(widget, appearanceStream); } // restore the field level appearance; defaultAppearance = acroFormAppearance; } } private PDDefaultAppearanceString getWidgetDefaultAppearanceString(PDAnnotationWidget widget) throws IOException { COSString da = (COSString) widget.getCOSObject().getDictionaryObject(COSName.DA); PDResources dr = field.getAcroForm().getDefaultResources(); return new PDDefaultAppearanceString(da, dr); } private int resolveRotation(PDAnnotationWidget widget) { PDAppearanceCharacteristicsDictionary characteristicsDictionary = widget .getAppearanceCharacteristics(); if (characteristicsDictionary != null) { // 0 is the default value if the R key doesn't exist return characteristicsDictionary.getRotation(); } return 0; } /** * Initialize the content of the appearance stream. * * Get settings like border style, border width and colors to be used to draw a rectangle and background color * around the widget * * @param widget the field widget * @param appearanceStream the appearance stream to be used * @throws IOException in case we can't write to the appearance stream */ private void initializeAppearanceContent(PDAnnotationWidget widget, PDAppearanceStream appearanceStream) throws IOException { try (PDPageContentStream contents = new PDPageContentStream( field.getAcroForm().getDocument(), appearanceStream)) { PDAppearanceCharacteristicsDictionary appearanceCharacteristics = widget .getAppearanceCharacteristics(); // TODO: support more entries like patterns, etc. if (appearanceCharacteristics != null) { PDColor backgroundColour = appearanceCharacteristics.getBackground(); if (backgroundColour != null) { contents.setNonStrokingColor(backgroundColour); PDRectangle bbox = resolveBoundingBox(widget, appearanceStream); contents.addRect(bbox.getLowerLeftX(), bbox.getLowerLeftY(), bbox.getWidth(), bbox.getHeight()); contents.fill(); } float lineWidth = 0f; PDColor borderColour = appearanceCharacteristics.getBorderColour(); if (borderColour != null) { contents.setStrokingColor(borderColour); lineWidth = 1f; } PDBorderStyleDictionary borderStyle = widget.getBorderStyle(); if (borderStyle != null && borderStyle.getWidth() > 0) { lineWidth = borderStyle.getWidth(); } if (lineWidth > 0 && borderColour != null) { if (lineWidth != 1) { contents.setLineWidth(lineWidth); } PDRectangle bbox = resolveBoundingBox(widget, appearanceStream); PDRectangle clipRect = applyPadding(bbox, Math.max(DEFAULT_PADDING, lineWidth / 2)); contents.addRect(clipRect.getLowerLeftX(), clipRect.getLowerLeftY(), clipRect.getWidth(), clipRect.getHeight()); contents.closeAndStroke(); } } } } /** * Parses an appearance stream into tokens. */ private List tokenize(PDAppearanceStream appearanceStream) throws IOException { return new ContentStreamParser(appearanceStream).tokens(); } /** * Constructs and sets new contents for given appearance stream. */ private void setAppearanceContent(PDAnnotationWidget widget, PDAppearanceStream appearanceStream) throws IOException { // first copy any needed resources from the document’s DR dictionary into // the stream’s Resources dictionary defaultAppearance.copyNeededResourcesTo(appearanceStream); // then replace the existing contents of the appearance stream from /Tx BMC // to the matching EMC try (ContentStreamWriter writer = new ContentStreamWriter( from(appearanceStream.getCOSObject().createUnfilteredStream()))) { List tokens = tokenize(appearanceStream); int bmcIndex = tokens.indexOf(BMC); if (bmcIndex == -1) { // append to existing stream writer.writeTokens(tokens); writer.writeTokens(asList(COSName.TX, BMC)); } else { // prepend content before BMC writer.writeTokens(tokens.subList(0, bmcIndex + 1)); } // insert field contents insertGeneratedAppearance(widget, appearanceStream, writer); int emcIndex = tokens.indexOf(EMC); if (emcIndex == -1) { // append EMC writer.writeTokens(EMC); } else { // append contents after EMC writer.writeTokens(tokens.subList(emcIndex, tokens.size())); } } } /** * Generate and insert text content and clipping around it. */ private void insertGeneratedAppearance(PDAnnotationWidget widget, PDAppearanceStream appearanceStream, ContentStreamWriter writer) throws IOException { PDPageContentStream contents = new PDPageContentStream(field.getAcroForm().getDocument(), appearanceStream, writer); PDRectangle bbox = resolveBoundingBox(widget, appearanceStream); // Acrobat calculates the left and right padding dependent on the offset of the border edge // This calculation works for forms having been generated by Acrobat. // The minimum distance is always 1f even if there is no rectangle being drawn around. float borderWidth = 0; if (widget.getBorderStyle() != null) { borderWidth = widget.getBorderStyle().getWidth(); } PDRectangle clipRect = applyPadding(bbox, Math.max(1f, borderWidth)); PDRectangle contentRect = applyPadding(clipRect, Math.max(1f, borderWidth)); contents.saveGraphicsState(); // Acrobat always adds a clipping path contents.addRect(clipRect.getLowerLeftX(), clipRect.getLowerLeftY(), clipRect.getWidth(), clipRect.getHeight()); contents.clip(); // get the font // field's defined appearance font has priority // callers might have determined that the default font does not support rendering the field's value // so the font was substituted to another one, which has better unicode support // see PDVariableText.setAppearanceOverrideFont() PDFont font = field.getAppearanceFont(); // fallback to default appearance if (font == null) { font = defaultAppearance.getFont(); } // calculate the fontSize (because 0 = autosize) float fontSize = defaultAppearance.getFontSize(); if (fontSize == 0) { fontSize = calculateFontSize(font, contentRect); } // for a listbox generate the highlight rectangle for the selected // options if (field instanceof PDListBox) { insertGeneratedSelectionHighlight(contents, appearanceStream, font, fontSize); } // start the text output contents.beginText(); // write the /DA string defaultAppearance.writeTo(contents, fontSize); // calculate the y-position of the baseline float y; // calculate font metrics at font size float fontScaleY = fontSize / FONTSCALE; float fontBoundingBoxAtSize = font.getBoundingBox().getHeight() * fontScaleY; float fontCapAtSize = font.getFontDescriptor().getCapHeight() * fontScaleY; float fontDescentAtSize = font.getFontDescriptor().getDescent() * fontScaleY; if (field instanceof PDTextField && ((PDTextField) field).isMultiline()) { y = contentRect.getUpperRightY() - fontBoundingBoxAtSize; } else { // Adobe shows the text 'shiftet up' in case the caps don't fit into the clipping area if (fontCapAtSize > clipRect.getHeight()) { y = clipRect.getLowerLeftY() + -fontDescentAtSize; } else { // calculate the position based on the content rectangle y = clipRect.getLowerLeftY() + (clipRect.getHeight() - fontCapAtSize) / 2; // check to ensure that ascents and descents fit if (y - clipRect.getLowerLeftY() < -fontDescentAtSize) { float fontDescentBased = -fontDescentAtSize + contentRect.getLowerLeftY(); float fontCapBased = contentRect.getHeight() - contentRect.getLowerLeftY() - fontCapAtSize; y = Math.min(fontDescentBased, Math.max(y, fontCapBased)); } } } // show the text float x = contentRect.getLowerLeftX(); // special handling for comb boxes as these are like table cells with individual // chars if (shallComb()) { insertGeneratedCombAppearance(contents, appearanceStream, font, fontSize); } else if (field instanceof PDListBox) { insertGeneratedListboxAppearance(contents, appearanceStream, contentRect, font, fontSize); } else { PlainText textContent = new PlainText(value); AppearanceStyle appearanceStyle = new AppearanceStyle(); appearanceStyle.setFont(font); appearanceStyle.setFontSize(fontSize); // Adobe Acrobat uses the font's bounding box for the leading between the lines appearanceStyle.setLeading(font.getBoundingBox().getHeight() * fontScaleY); PlainTextFormatter formatter = new PlainTextFormatter.Builder(contents) .style(appearanceStyle).text(textContent).width(contentRect.getWidth()) .wrapLines(isMultiLine()).initialOffset(x, y).textAlign(field.getQ()).build(); formatter.format(); } contents.endTextIfRequired(); contents.restoreGraphicsState(); } private AffineTransform calculateMatrix(PDRectangle bbox, int rotation) { if (rotation == 0) { return new AffineTransform(); } float tx = 0, ty = 0; switch (rotation) { case 90: tx = bbox.getUpperRightY(); break; case 180: tx = bbox.getUpperRightY(); ty = bbox.getUpperRightX(); break; case 270: ty = bbox.getUpperRightX(); break; default: break; } Matrix matrix = Matrix.getRotateInstance(Math.toRadians(rotation), tx, ty); return matrix.createAffineTransform(); } private boolean isMultiLine() { return field instanceof PDTextField && ((PDTextField) field).isMultiline(); } /** * Determine if the appearance shall provide a comb output. * *

* May be set only if the MaxLen entry is present in the text field dictionary and if the Multiline, Password, and * FileSelect flags are clear. If set, the field shall be automatically divided into as many equally spaced * positions, or combs, as the value of MaxLen, and the text is laid out into those combs. *

* * @return the comb state */ private boolean shallComb() { return field instanceof PDTextField && ((PDTextField) field).isComb() && !((PDTextField) field).isMultiline() && !((PDTextField) field).isPassword() && !((PDTextField) field).isFileSelect(); } /** * Generate the appearance for comb fields. * * @param contents the content stream to write to * @param appearanceStream the appearance stream used * @param font the font to be used * @param fontSize the font size to be used * @throws IOException */ private void insertGeneratedCombAppearance(PDPageContentStream contents, PDAppearanceStream appearanceStream, PDFont font, float fontSize) throws IOException { // TODO: Currently the quadding is not taken into account // so the comb is always filled from left to right. int maxLen = ((PDTextField) field).getMaxLen(); int numChars = Math.min(value.length(), maxLen); PDRectangle paddingEdge = applyPadding(appearanceStream.getBBox(), 1); float combWidth = appearanceStream.getBBox().getWidth() / maxLen; float ascentAtFontSize = font.getFontDescriptor().getAscent() / FONTSCALE * fontSize; float baselineOffset = paddingEdge.getLowerLeftY() + (appearanceStream.getBBox().getHeight() - ascentAtFontSize) / 2; float prevCharWidth = 0f; float xOffset = combWidth / 2; contents.saveGraphicsState(); contents.setFont(font, fontSize); for (int i = 0; i < numChars; i++) { String combString = value.substring(i, i + 1); float currCharWidth = font.getStringWidth(combString) / FONTSCALE * fontSize / 2; xOffset = xOffset + prevCharWidth / 2 - currCharWidth / 2; contents.newLineAtOffset(xOffset, baselineOffset); contents.showText(combString); baselineOffset = 0; prevCharWidth = currCharWidth; xOffset = combWidth; } contents.restoreGraphicsState(); } private void insertGeneratedSelectionHighlight(PDPageContentStream contents, PDAppearanceStream appearanceStream, PDFont font, float fontSize) throws IOException { List indexEntries = ((PDListBox) field).getSelectedOptionsIndex(); List values = ((PDListBox) field).getValue(); List options = ((PDListBox) field).getOptionsExportValues(); if (!values.isEmpty() && !options.isEmpty() && indexEntries.isEmpty()) { // create indexEntries from options indexEntries = new ArrayList<>(); for (String v : values) { indexEntries.add(options.indexOf(v)); } } // The first entry which shall be presented might be adjusted by the optional TI key // If this entry is present the first entry to be displayed is the keys value otherwise // display starts with the first entry in Opt. int topIndex = ((PDListBox) field).getTopIndex(); float highlightBoxHeight = font.getBoundingBox().getHeight() * fontSize / FONTSCALE; // the padding area PDRectangle paddingEdge = applyPadding(appearanceStream.getBBox(), 1); for (int selectedIndex : indexEntries) { contents.setNonStrokingColor(HIGHLIGHT_COLOR[0], HIGHLIGHT_COLOR[1], HIGHLIGHT_COLOR[2]); contents.addRect(paddingEdge.getLowerLeftX(), paddingEdge.getUpperRightY() - highlightBoxHeight * (selectedIndex - topIndex + 1) + 2, paddingEdge.getWidth(), highlightBoxHeight); contents.fill(); } contents.setNonStrokingColor(0); } private void insertGeneratedListboxAppearance(PDPageContentStream contents, PDAppearanceStream appearanceStream, PDRectangle contentRect, PDFont font, float fontSize) throws IOException { contents.setNonStrokingColor(0); int q = field.getQ(); if (q == PDVariableText.QUADDING_CENTERED || q == PDVariableText.QUADDING_RIGHT) { float fieldWidth = appearanceStream.getBBox().getWidth(); float stringWidth = (font.getStringWidth(value) / FONTSCALE) * fontSize; float adjustAmount = fieldWidth - stringWidth - 4; if (q == PDVariableText.QUADDING_CENTERED) { adjustAmount = adjustAmount / 2.0f; } contents.newLineAtOffset(adjustAmount, 0); } else if (q != PDVariableText.QUADDING_LEFT) { throw new IOException("Error: Unknown justification value:" + q); } List options = ((PDListBox) field).getOptionsDisplayValues(); int numOptions = options.size(); float yTextPos = contentRect.getUpperRightY(); int topIndex = ((PDListBox) field).getTopIndex(); for (int i = topIndex; i < numOptions; i++) { if (i == topIndex) { yTextPos = yTextPos - font.getFontDescriptor().getAscent() / FONTSCALE * fontSize; } else { yTextPos = yTextPos - font.getBoundingBox().getHeight() / FONTSCALE * fontSize; contents.beginText(); } contents.newLineAtOffset(contentRect.getLowerLeftX(), yTextPos); contents.showText(options.get(i)); if (i - topIndex != (numOptions - 1)) { contents.endText(); } } } /** * My "not so great" method for calculating the fontsize. It does not work superb, but it handles ok. * * @return the calculated font-size * @throws IOException If there is an error getting the font information. */ private float calculateFontSize(PDFont font, PDRectangle contentRect) throws IOException { float fontSize = defaultAppearance.getFontSize(); // zero is special, it means the text is auto-sized if (fontSize == 0) { if (isMultiLine()) { // Acrobat defaults to 12 for multiline text with size 0 return DEFAULT_FONT_SIZE; } float yScalingFactor = FONTSCALE * font.getFontMatrix().getScaleY(); float xScalingFactor = FONTSCALE * font.getFontMatrix().getScaleX(); // fit width float width = font.getStringWidth(value) * font.getFontMatrix().getScaleX(); float widthBasedFontSize = contentRect.getWidth() / width * xScalingFactor; // fit height float height = (font.getFontDescriptor().getCapHeight() + -font.getFontDescriptor().getDescent()) * font.getFontMatrix().getScaleY(); if (height <= 0) { height = font.getBoundingBox().getHeight() * font.getFontMatrix().getScaleY(); } float heightBasedFontSize = contentRect.getHeight() / height * yScalingFactor; return Math.min(heightBasedFontSize, widthBasedFontSize); } return fontSize; } /** * Resolve the bounding box. * * @param fieldWidget the annotation widget. * @param appearanceStream the annotations appearance stream. * @return the resolved boundingBox. */ private PDRectangle resolveBoundingBox(PDAnnotationWidget fieldWidget, PDAppearanceStream appearanceStream) { PDRectangle boundingBox = appearanceStream.getBBox(); if (boundingBox == null) { boundingBox = fieldWidget.getRectangle().createRetranslatedRectangle(); } return boundingBox; } /** * Apply padding to a box. * * @param box box * @return the padded box. */ private PDRectangle applyPadding(PDRectangle box, float padding) { return new PDRectangle(box.getLowerLeftX() + padding, box.getLowerLeftY() + padding, box.getWidth() - 2 * padding, box.getHeight() - 2 * padding); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/AppearanceStyle.java000066400000000000000000000050171320103431700315220ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import org.sejda.sambox.pdmodel.font.PDFont; /** * Define styling attributes to be used for text formatting. * */ class AppearanceStyle { private PDFont font; /** * The font size to be used for text formatting. * * Defaulting to 12 to match Acrobats default. */ private float fontSize = 12.0f; /** * The leading (distance between lines) to be used for text formatting. * * Defaulting to 1.2*fontSize to match Acrobats default. */ private float leading = 14.4f; /** * Get the font used for text formatting. * * @return the font used for text formatting. */ PDFont getFont() { return font; } /** * Set the font to be used for text formatting. * * @param font the font to be used. */ void setFont(PDFont font) { this.font = font; } /** * Get the fontSize used for text formatting. * * @return the fontSize used for text formatting. */ float getFontSize() { return fontSize; } /** * Set the font size to be used for formatting. * * @param fontSize the font size. */ void setFontSize(float fontSize) { this.fontSize = fontSize; leading = fontSize * 1.2f; } /** * Get the leading used for text formatting. * * @return the leading used for text formatting. */ float getLeading() { return leading; } /** * Set the leading used for text formatting. * * @param leading the leading to be used. */ void setLeading(float leading) { this.leading = leading; } }sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/FieldUtils.java000066400000000000000000000134661320103431700305150ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSString; /** * A set of utility methods to help with common AcroForm form and field related functions. */ public final class FieldUtils { /** * An implementation of a basic key value pair. * * This implementation is used to help sorting the content of field option entries with an array of two-element * arrays as used by choice fields. * */ static class KeyValue { private final String key; private final String value; public KeyValue(final String theKey, final String theValue) { this.key = theKey; this.value = theValue; } public String getKey() { return this.key; } public String getValue() { return this.value; } @Override public String toString() { return "(" + this.key + ", " + this.value + ")"; } } /** * Comparator to sort KeyValue by key. */ static class KeyValueKeyComparator implements Serializable, Comparator { private static final long serialVersionUID = 6715364290007167694L; @Override public int compare(KeyValue o1, KeyValue o2) { return o1.key.compareTo(o2.key); } } /** * Comparator to sort KeyValue by value. */ static class KeyValueValueComparator implements Serializable, Comparator { private static final long serialVersionUID = -3984095679894798265L; @Override public int compare(KeyValue o1, KeyValue o2) { return o1.value.compareTo(o2.value); } } /** * Constructor. */ private FieldUtils() { } /** * Return two related lists as a single list with key value pairs. * * @param key the key elements * @param value the value elements * @return a sorted list of KeyValue elements. */ static List toKeyValueList(List key, List value) { List list = new ArrayList<>(); for (int i = 0; i < key.size(); i++) { list.add(new FieldUtils.KeyValue(key.get(i), value.get(i))); } return list; } /** * Sort two related lists simultaneously by the elements in the key parameter. * * @param pairs a list of KeyValue elements */ static void sortByValue(List pairs) { Collections.sort(pairs, new FieldUtils.KeyValueValueComparator()); } /** * Sort two related lists simultaneously by the elements in the value parameter. * * @param pairs a list of KeyValue elements */ static void sortByKey(List pairs) { Collections.sort(pairs, new FieldUtils.KeyValueKeyComparator()); } /** * Return either one of a list which can have two-element arrays entries. *

* Some entries in a dictionary can either be an array of elements or an array of two-element arrays. This method * will either return the elements in the array or in case of two-element arrays, the element designated by the pair * index *

*

* An {@link IllegalArgumentException} will be thrown if the items contain two-element arrays and the index is not 0 * or 1. *

* * @param items the array of elements or two-element arrays * @param pairIdx the index into the two-element array * @return a List of single elements */ static List getPairableItems(COSBase items, int pairIdx) { if (pairIdx < 0 || pairIdx > 1) { throw new IllegalArgumentException( "Only 0 and 1 are allowed as an index into two-element arrays"); } if (items instanceof COSString) { List array = new ArrayList<>(); array.add(((COSString) items).getString()); return array; } else if (items instanceof COSArray) { COSArray array = (COSArray) items; List result = new ArrayList<>(); int numItems = ((COSArray) items).size(); for (int i = 0; i < numItems; i++) { COSBase item = array.get(i); if(item instanceof COSArray) { COSArray pair = (COSArray) array.get(i); COSString displayValue = (COSString) pair.get(pairIdx); result.add(displayValue.getString()); } else if(item instanceof COSString) { result.add(((COSString) item).getString()); } } return result; } return Collections.emptyList(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDAcroForm.java000066400000000000000000000520541320103431700304010ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.PDPageContentStream; import org.sejda.sambox.pdmodel.PDPageContentStream.AppendMode; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.font.PDType1Font; import org.sejda.sambox.pdmodel.graphics.PDXObject; import org.sejda.sambox.pdmodel.graphics.form.PDFormXObject; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotation; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationWidget; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceStream; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An interactive form, also known as an AcroForm. * * @author Ben Litchfield */ public final class PDAcroForm extends PDDictionaryWrapper { private static final Logger LOG = LoggerFactory.getLogger(PDAcroForm.class); private static final int FLAG_SIGNATURES_EXIST = 1; private static final int FLAG_APPEND_ONLY = 1 << 1; private final PDDocument document; /** * @param doc The document that this form is part of. */ public PDAcroForm(PDDocument document) { this.document = document; getCOSObject().setItem(COSName.FIELDS, new COSArray()); } /** * @param doc The document that this form is part of. * @param form The existing acroForm. */ public PDAcroForm(PDDocument document, COSDictionary form) { super(form); this.document = document; verifyOrCreateDefaults(); } /** * Verify that there are default entries for required properties. * * If these are missing create default entries similar to Adobe Reader / Adobe Acrobat * */ private void verifyOrCreateDefaults() { final String adobeDefaultAppearanceString = "/Helv 0 Tf 0 g "; // DA entry is required if (getDefaultAppearance().length() == 0) { setDefaultAppearance(adobeDefaultAppearanceString); } // DR entry is required PDResources defaultResources = getDefaultResources(); if (defaultResources == null) { defaultResources = new PDResources(); setDefaultResources(defaultResources); } // Adobe Acrobat uses Helvetica as a default font and // stores that under the name '/Helv' in the resources dictionary // Zapf Dingbats is included per default for check boxes and // radio buttons as /ZaDb. if (!defaultResources.getCOSObject().containsKey("Helv")) { defaultResources.put(COSName.getPDFName("Helv"), PDType1Font.HELVETICA); } if (!defaultResources.getCOSObject().containsKey("ZaDb")) { defaultResources.put(COSName.getPDFName("ZaDb"), PDType1Font.ZAPF_DINGBATS); } } /** * This will get the document associated with this form. * * @return The PDF document. */ public PDDocument getDocument() { return document; } /** * This will flatten all form fields. * *

* Flattening a form field will take the current appearance and make that part of the pages content stream. All form * fields and annotations associated are removed. *

* *

* The appearances for the form fields widgets will not be generated *

* * @throws IOException */ public void flatten() throws IOException { // for dynamic XFA forms there is no flatten as this would mean to do a rendering // from the XFA content into a static PDF. if (xfaIsDynamic()) { LOG.warn("Flatten for a dynamic XFA form is not supported"); return; } List fields = new ArrayList<>(); for (PDField field : getFieldTree()) { fields.add(field); } flatten(fields, false); } /** * This will flatten the specified form fields. * *

* Flattening a form field will take the current appearance and make that part of the pages content stream. All form * fields and annotations associated are removed. *

* * @param refreshAppearances if set to true the appearances for the form field widgets will be updated * @throws IOException */ public void flatten(List fields, boolean refreshAppearances) throws IOException { // for dynamic XFA forms there is no flatten as this would mean to do a rendering // from the XFA content into a static PDF. if (xfaIsDynamic()) { LOG.warn("Flatten for a dynamix XFA form is not supported"); return; } // refresh the appearances if set if (refreshAppearances) { refreshAppearances(fields); } // indicates if the original content stream // has been wrapped in a q...Q pair. boolean isContentStreamWrapped; // the content stream to write to PDPageContentStream contentStream; // preserve all non widget annotations for (PDPage page : document.getPages()) { isContentStreamWrapped = false; List annotations = new ArrayList(); for (PDAnnotation annotation : page.getAnnotations()) { if (!(annotation instanceof PDAnnotationWidget)) { annotations.add(annotation); } else if (!annotation.isInvisible() && !annotation.isHidden() && annotation.getNormalAppearanceStream() != null) { if (!isContentStreamWrapped) { contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, true, true); isContentStreamWrapped = true; } else { contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, true); } PDAppearanceStream appearanceStream = annotation.getNormalAppearanceStream(); PDFormXObject fieldObject = new PDFormXObject(appearanceStream.getCOSObject()); contentStream.saveGraphicsState(); // translate the appearance stream to the widget location if there is // not already a transformation in place boolean needsTranslation = resolveNeedsTranslation(appearanceStream); // scale the appearance stream - mainly needed for images // in buttons and signatures boolean needsScaling = resolveNeedsScaling(appearanceStream); Matrix transformationMatrix = new Matrix(); boolean transformed = false; if (needsTranslation) { transformationMatrix.translate(annotation.getRectangle().getLowerLeftX(), annotation.getRectangle().getLowerLeftY()); transformed = true; } if (needsScaling) { PDRectangle bbox = appearanceStream.getBBox(); PDRectangle fieldRect = annotation.getRectangle(); if (bbox.getWidth() - fieldRect.getWidth() != 0 && bbox.getHeight() - fieldRect.getHeight() != 0) { float xScale = fieldRect.getWidth() / bbox.getWidth(); float yScale = fieldRect.getHeight() / bbox.getHeight(); Matrix scalingMatrix = Matrix.getScaleInstance(xScale, yScale); transformationMatrix.concatenate(scalingMatrix); transformed = true; } } if (transformed) { contentStream.transform(transformationMatrix); } contentStream.drawForm(fieldObject); contentStream.restoreGraphicsState(); contentStream.close(); } } page.setAnnotations(annotations); } // remove the fields setFields(Collections. emptyList()); // remove XFA for hybrid forms getCOSObject().removeItem(COSName.XFA); } /** * Refreshes the appearance streams and appearance dictionaries for the widget annotations of all fields. * * @throws IOException */ public void refreshAppearances() throws IOException { for (PDField field : getFieldTree()) { if (field instanceof PDTerminalField) { ((PDTerminalField) field).constructAppearances(); } } } /** * Refreshes the appearance streams and appearance dictionaries for the widget annotations of the specified fields. * * @throws IOException */ public void refreshAppearances(List fields) throws IOException { for (PDField field : fields) { if (field instanceof PDTerminalField) { ((PDTerminalField) field).constructAppearances(); } } } /** * This will return all of the documents root fields. * * A field might have children that are fields (non-terminal field) or does not have children which are fields * (terminal fields). * * The fields within an AcroForm are organized in a tree structure. The documents root fields might either be * terminal fields, non-terminal fields or a mixture of both. Non-terminal fields mark branches which contents can * be retrieved using {@link PDNonTerminalField#getChildren()}. * * @return A list of the documents root fields, never null. If there are no fields then this method returns an empty * list. * */ public List getFields() { List pdFields = new ArrayList<>(); COSArray fields = getCOSObject().getDictionaryObject(COSName.FIELDS, COSArray.class); if (nonNull(fields)) { for (COSBase field : fields) { if (nonNull(field) && field.getCOSObject() instanceof COSDictionary) { pdFields.add(PDField.fromDictionary(this, (COSDictionary) field.getCOSObject(), null)); } } } return pdFields; } /** * Adds the fields to the root fields of the form * * @param fields */ public void addFields(List toAdd) { COSArray fields = ofNullable( getCOSObject().getDictionaryObject(COSName.FIELDS, COSArray.class)) .orElseGet(COSArray::new); for (PDField field : toAdd) { fields.add(field); } getCOSObject().setItem(COSName.FIELDS, fields); } /** * removes the given field from the root fields of the form * * @return the removed element or null */ public COSBase removeField(PDField remove) { COSArray fields = getCOSObject().getDictionaryObject(COSName.FIELDS, COSArray.class); if (nonNull(fields)) { int removeIdx = fields.indexOfObject(remove.getCOSObject()); if (removeIdx >= 0) { return fields.remove(removeIdx); } } return null; } /** * Set the documents root fields. * * @param fields The fields that are part of the documents root fields. */ public void setFields(List fields) { getCOSObject().setItem(COSName.FIELDS, COSArrayList.converterToCOSArray(fields)); } /** * Returns an iterator which walks all fields in the field tree, post-order. */ public Iterator getFieldIterator() { return new PDFieldTree(this).iterator(); } /** * @return the field tree representing all form fields and allowing a post-order visit of the tree */ public PDFieldTree getFieldTree() { return new PDFieldTree(this); } /** * This will get a field by name, possibly using the cache if setCache is true. * * @param fullyQualifiedName The name of the field to get. * @return The field with that name of null if one was not found. */ public PDField getField(String fullyQualifiedName) { if (fullyQualifiedName == null) { return null; } return getFieldTree().stream() .filter(f -> f != null && fullyQualifiedName.equals(f.getFullyQualifiedName())) .findFirst().orElse(null); } /** * @return the DA element of the dictionary object or null if nothing is defined */ public String getDefaultAppearance() { return ofNullable(getCOSObject().getDictionaryObject(COSName.DA, COSString.class)) .map(COSString::getString).orElse(""); } /** * Set the default appearance. * * @param daValue a string describing the default appearance */ public void setDefaultAppearance(String daValue) { getCOSObject().setString(COSName.DA, daValue); } /** * True if the viewing application should construct the appearances of all field widgets. The default value is * false. * * @return the value of NeedAppearances, false if the value isn't set */ public boolean isNeedAppearances() { return getCOSObject().getBoolean(COSName.NEED_APPEARANCES, false); } /** * Set the NeedAppearances value. If this is false, PDFBox will create appearances for all field widget. * * @param value the value for NeedAppearances */ public void setNeedAppearances(Boolean value) { getCOSObject().setBoolean(COSName.NEED_APPEARANCES, value); } /** * This will get the default resources for the acro form. * * @return The default resources. */ public PDResources getDefaultResources() { return ofNullable(getCOSObject().getDictionaryObject(COSName.DR, COSDictionary.class)) .map(dr -> new PDResources(dr, document.getResourceCache())).orElse(null); } /** * This will set the default resources for the acroform. * * @param dr The new default resources. */ public void setDefaultResources(PDResources dr) { getCOSObject().setItem(COSName.DR, dr); } /** * This will tell if the AcroForm has XFA content. * * @return true if the AcroForm is an XFA form */ public boolean hasXFA() { return getCOSObject().containsKey(COSName.XFA); } /** * This will tell if the AcroForm is a dynamic XFA form. * * @return true if the AcroForm is a dynamic XFA form */ public boolean xfaIsDynamic() { return hasXFA() && getFields().isEmpty(); } /** * Get the XFA resource, the XFA resource is only used for PDF 1.5+ forms. * * @return The xfa resource or null if it does not exist. */ public PDXFAResource getXFA() { return ofNullable(getCOSObject().getDictionaryObject(COSName.XFA, COSDictionary.class)) .map(PDXFAResource::new).orElse(null); } /** * Set the XFA resource, this is only used for PDF 1.5+ forms. * * @param xfa The xfa resource. */ public void setXFA(PDXFAResource xfa) { getCOSObject().setItem(COSName.XFA, xfa); } /** * This will get the 'quadding' or justification of the text to be displayed. 0 - Left(default)
* 1 - Centered
* 2 - Right
* See the QUADDING constants of {@link PDVariableText}. * * @return The justification of the text strings. */ public int getQuadding() { return getCOSObject().getInt(COSName.Q, 0); } /** * This will set the quadding/justification of the text. See QUADDING constants. * * @param q The new text justification. */ public void setQuadding(int q) { getCOSObject().setInt(COSName.Q, q); } /** * Determines if SignaturesExist is set. * * @return true if the document contains at least one signature. */ public boolean isSignaturesExist() { return getCOSObject().getFlag(COSName.SIG_FLAGS, FLAG_SIGNATURES_EXIST); } /** * Set the SignaturesExist bit. * * @param signaturesExist The value for SignaturesExist. */ public void setSignaturesExist(boolean signaturesExist) { getCOSObject().setFlag(COSName.SIG_FLAGS, FLAG_SIGNATURES_EXIST, signaturesExist); } /** * Determines if AppendOnly is set. * * @return true if the document contains signatures that may be invalidated if the file is saved. */ public boolean isAppendOnly() { return getCOSObject().getFlag(COSName.SIG_FLAGS, FLAG_APPEND_ONLY); } /** * Set the AppendOnly bit. * * @param appendOnly The value for AppendOnly. */ public void setAppendOnly(boolean appendOnly) { getCOSObject().setFlag(COSName.SIG_FLAGS, FLAG_APPEND_ONLY, appendOnly); } /** * Check if there is a translation needed to place the annotations content. * * @param appearanceStream * @return the need for a translation transformation. */ private boolean resolveNeedsTranslation(PDAppearanceStream appearanceStream) { boolean needsTranslation = false; PDResources resources = appearanceStream.getResources(); if (resources != null && resources.getXObjectNames().iterator().hasNext()) { Iterator xObjectNames = resources.getXObjectNames().iterator(); while (xObjectNames.hasNext()) { try { // if the BBox of the PDFormXObject does not start at 0,0 // there is no need do translate as this is done by the BBox definition. PDXObject xObject = resources.getXObject(xObjectNames.next()); if (xObject instanceof PDFormXObject) { PDRectangle bbox = ((PDFormXObject) xObject).getBBox(); float llX = bbox.getLowerLeftX(); float llY = bbox.getLowerLeftY(); if (llX == 0 && llY == 0) { needsTranslation = true; } } } catch (IOException e) { // we can safely ignore the exception here // as this might only cause a misplacement } } return needsTranslation; } return true; } /** * Check if there needs to be a scaling transformation applied. * * @param appearanceStream * @return the need for a scaling transformation. */ private boolean resolveNeedsScaling(PDAppearanceStream appearanceStream) { // Check if there is a transformation within the XObjects content PDResources resources = appearanceStream.getResources(); return resources != null && resources.getXObjectNames().iterator().hasNext(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDButton.java000066400000000000000000000325201320103431700301400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationWidget; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceDictionary; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceEntry; /** * A button field represents an interactive control on the screen that the user can manipulate with the mouse. * * @author sug */ public abstract class PDButton extends PDTerminalField { /** * A Ff flag. If set, the field is a set of radio buttons */ static final int FLAG_RADIO = 1 << 15; /** * A Ff flag. If set, the field is a pushbutton. */ static final int FLAG_PUSHBUTTON = 1 << 16; /** * A Ff flag. If set, radio buttons individual fields, using the same value for the on state will turn on and off in * unison. */ static final int FLAG_RADIOS_IN_UNISON = 1 << 25; /** * @see PDField#PDField(PDAcroForm) * * @param acroForm The acroform. */ public PDButton(PDAcroForm acroForm) { super(acroForm); getCOSObject().setItem(COSName.FT, COSName.BTN); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDButton(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * Determines if push button bit is set. * * @return true if type of button field is a push button. */ public boolean isPushButton() { return getCOSObject().getFlag(COSName.FF, FLAG_PUSHBUTTON); } /** * Set the push button bit. * * @param pushbutton if true the button field is treated as a push button field. */ public void setPushButton(boolean pushbutton) { getCOSObject().setFlag(COSName.FF, FLAG_PUSHBUTTON, pushbutton); } /** * Determines if radio button bit is set. * * @return true if type of button field is a push button. */ public boolean isRadioButton() { return getCOSObject().getFlag(COSName.FF, FLAG_RADIO); } /** * Set the radio button bit. * * @param radiobutton if true the button field is treated as a radio button field. */ public void setRadioButton(boolean radiobutton) { getCOSObject().setFlag(COSName.FF, FLAG_RADIO, radiobutton); } /** * Returns the selected value. May be empty if NoToggleToOff is set but there is no value selected. * * @return A non-null string. */ public String getValue() { COSBase value = getInheritableAttribute(COSName.V); if (value instanceof COSName) { return ((COSName) value).getName(); } return ""; } /** * Sets the selected option given its name. * * @param value Name of option to select * @throws IOException if the value could not be set * @throws IllegalArgumentException if the value is not a valid option. */ @Override public void setValue(String value) throws IOException { checkValue(value); // if there are export values/an Opt entry there is a different // approach to setting the value List exportValues = getExportValues(); if (exportValues.contains(value)) { updateByOption(value); } else { updateByValue(value); } applyChange(); } /** * Returns the default value, if any. * * @return A non-null string. */ public String getDefaultValue() { COSBase value = getInheritableAttribute(COSName.DV); if (value instanceof COSName) { return ((COSName) value).getName(); } return ""; } /** * Sets the default value. * * @param value Name of option to select * @throws IllegalArgumentException if the value is not a valid option. */ public void setDefaultValue(String value) { checkValue(value); getCOSObject().setName(COSName.DV, value); } @Override public String getValueAsString() { return getValue(); } /** * This will get the export values. * *

* The export values are defined in the field dictionaries /Opt key. *

* *

* The option values are used to define the export values for the field to *

    *
  • hold values in non-Latin writing systems as name objects, which represent the field value, are limited to * PDFDocEncoding
  • *
  • allow radio buttons having the same export value to be handled independently
  • *
*

* * @return List containing all possible export values. If there is no Opt entry an empty list will be returned. */ public List getExportValues() { COSBase value = getInheritableAttribute(COSName.OPT); if (value instanceof COSString) { List array = new ArrayList<>(); array.add(((COSString) value).getString()); return array; } else if (value instanceof COSArray) { return COSArrayList.convertCOSStringCOSArrayToList((COSArray) value); } return Collections.emptyList(); } /** * This will set the export values. * * @see #getExportValues() * @param values List containing all possible export values. Supplying null or an empty list will remove the Opt * entry. */ public void setExportValues(List values) { COSArray cosValues; if (values != null && !values.isEmpty()) { cosValues = COSArrayList.convertStringListToCOSStringCOSArray(values); getCOSObject().setItem(COSName.OPT, cosValues); } else { getCOSObject().removeItem(COSName.OPT); } } @Override void constructAppearances() throws IOException { List exportValues = getExportValues(); if (exportValues.contains(getValue())) { // the value is the index value of the option. So we need to get that // and use it to set the value try { int optionsIndex = Integer.parseInt(getValue()); if (optionsIndex >= 0 && optionsIndex < exportValues.size()) { updateByOption(exportValues.get(optionsIndex)); } } catch (NumberFormatException e) { // silently ignore that // and don't update the appearance } } else { updateByValue(getValue()); } } /** * Get the values to set individual buttons within a group to the on state. * *

* The On value could be an arbitrary string as long as it is within the limitations of a PDF name object. The Off * value shall always be 'Off'. If not set or not part of the normal appearance keys 'Off' is the default *

* * @return the potential values setting the check box to the On state. If an empty Set is returned there is no * appearance definition. */ public Set getOnValues() { Set onValues = new HashSet<>(); onValues.addAll(getExportValues()); onValues.addAll(getNormalAppearanceValues()); return onValues; } public List getNormalAppearanceValues() { List values = new ArrayList<>(); List widgets = this.getWidgets(); for (PDAnnotationWidget widget : widgets) { String value = getOnValueForWidget(widget); if(value != null) { values.add(value); } } return values; } /* * Get the on value for an individual widget by it's index. */ private String getOnValue(int index) { List widgets = this.getWidgets(); if (index < widgets.size()) { return getOnValueForWidget(widgets.get(index)); } // PDFBox returns an empty string here. // Chose to return null so it's clear it's an undefined value // Caller should choose to not act on these values return null; } /* * Get the on value for an individual widget. */ private String getOnValueForWidget(PDAnnotationWidget widget) { PDAppearanceDictionary apDictionary = widget.getAppearance(); if (apDictionary != null) { PDAppearanceEntry normalAppearance = apDictionary.getNormalAppearance(); if (normalAppearance != null) { Set entries = normalAppearance.getSubDictionary().keySet(); for (COSName entry : entries) { if (COSName.Off.compareTo(entry) != 0) { return entry.getName(); } } } } // PDFBox returns an empty string here. // Chose to return null so it's clear it's an undefined value // Caller should choose to not act on these values return null; } /** * Checks value. * * @param value Name of radio button to select * @throws IllegalArgumentException if the value is not a valid option. */ void checkValue(String value) throws IllegalArgumentException { Set onValues = getOnValues(); if(onValues.isEmpty()) { return; } if (COSName.Off.getName().compareTo(value) != 0 && !onValues.contains(value)) { throw new IllegalArgumentException("value '" + value + "' is not a valid option for the field " + getFullyQualifiedName() + ", valid values are: " + onValues + " and " + COSName.Off.getName()); } } private void updateByValue(String value) { getCOSObject().setName(COSName.V, value); // update the appearance state (AS) for (PDAnnotationWidget widget : getWidgets()) { boolean matchesAppearance = false; // don't crash when there's no appearances (eg: checkboxes) if(widget.getAppearance() != null && widget.getAppearance().getNormalAppearance() != null) { matchesAppearance =((COSDictionary) widget.getAppearance().getNormalAppearance().getCOSObject()) .containsKey(value); } // checkbox with no appearances scenario if(!COSName.OFF.getName().equals(value) && widget.getAppearance() == null && getWidgets().size() == 1) { matchesAppearance = true; } if (matchesAppearance) { widget.getCOSObject().setName(COSName.AS, value); } else { widget.getCOSObject().setItem(COSName.AS, COSName.Off); } } } private void updateByOption(String value) throws IOException { List widgets = getWidgets(); List options = getExportValues(); if (widgets.size() != options.size()) { throw new IllegalArgumentException( "The number of options doesn't match the number of widgets"); } if (value.equals(COSName.Off.getName())) { updateByValue(value); } else { // the value is the index of the matching option int optionsIndex = options.indexOf(value); // get the values the options are pointing to as // this might not be numerical // see PDFBOX-3682 if (optionsIndex != -1) { String onValue = getOnValue(optionsIndex); if(onValue != null) { updateByValue(onValue); } } } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDCheckBox.java000066400000000000000000000101271320103431700303520ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.util.List; import java.util.Set; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationWidget; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceDictionary; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceEntry; /** * A check box toggles between two states, on and off. * * @author Ben Litchfield * @author sug */ public final class PDCheckBox extends PDButton { /** * @see PDField#PDField(PDAcroForm) * * @param acroForm The acroform. */ public PDCheckBox(PDAcroForm acroForm) { super(acroForm); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDCheckBox(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * This will tell if this radio button is currently checked or not. This is equivalent to calling * {@link #getValue()}. * * @return true If this field is checked. */ public boolean isChecked() { if(COSName.Off.getName().equals(getValue())) { return false; } return getOnValues().contains(getValue()) || // no appearances COSName.YES.getName().equals(getValue()); } /** * Checks the check box. * * @throws IOException if the appearance couldn't be generated. */ public void check() throws IOException { setValue(getOnValue()); } /** * Unchecks the check box. * * @throws IOException if the appearance couldn't be generated. */ public void unCheck() throws IOException { setValue(COSName.Off.getName()); } /** * Get the value which sets the check box to the On state. * *

* The On value should be 'Yes' but other values are possible so we need to look for that. On the other hand the Off * value shall always be 'Off'. If not set or not part of the normal appearance keys 'Off' is the default *

* * @return the value setting the check box to the On state * definition. */ public String getOnValue() { List exportValues = getExportValues(); if(exportValues.size() > 0) { return exportValues.get(0); } PDAnnotationWidget widget = this.getWidgets().get(0); PDAppearanceDictionary apDictionary = widget.getAppearance(); String onValue = "Yes"; if (apDictionary != null) { PDAppearanceEntry normalAppearance = apDictionary.getNormalAppearance(); if (normalAppearance != null) { Set entries = normalAppearance.getSubDictionary().keySet(); for (COSName entry : entries) { if (COSName.Off.compareTo(entry) != 0) { onValue = entry.getName(); } } } } return onValue; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDChoice.java000066400000000000000000000351441320103431700300640ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import static org.sejda.util.RequireUtils.requireArg; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.interactive.form.FieldUtils.KeyValue; /** * A choice field contains several text items, one or more of which shall be selected as the field * value. * * @author sug * @author John Hewson */ public abstract class PDChoice extends PDVariableText { static final int FLAG_COMBO = 1 << 17; private static final int FLAG_SORT = 1 << 19; private static final int FLAG_MULTI_SELECT = 1 << 21; private static final int FLAG_DO_NOT_SPELL_CHECK = 1 << 22; private static final int FLAG_COMMIT_ON_SEL_CHANGE = 1 << 26; /** * @see PDField#PDField(PDAcroForm) * * @param acroForm The acroform. */ public PDChoice(PDAcroForm acroForm) { super(acroForm); getCOSObject().setItem(COSName.FT, COSName.CH); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDChoice(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * This will get the option values "Opt". * *

* For a choice field the options array can either be an array * of text strings or an array of a two-element arrays.
* The method always only returns either the text strings or, * in case of two-element arrays, an array of the first element of * the two-element arrays *

*

* Use {@link #getOptionsExportValues()} and {@link #getOptionsDisplayValues()} * to get the entries of two-element arrays. *

* * @return List containing the export values. */ public List getOptions() { return FieldUtils.getPairableItems(getCOSObject().getDictionaryObject(COSName.OPT), 0); } /** * This will set the display values - the 'Opt' key. * *

* The Opt array specifies the list of options in the choice field either * as an array of text strings representing the display value * or as an array of a two-element array where the * first element is the export value and the second the display value. *

*

* To set both the export and the display value use {@link #setOptions(List, List)} *

* * @param displayValues List containing all possible options. */ public void setOptions(List displayValues) { if (displayValues != null && !displayValues.isEmpty()) { if (isSort()) { Collections.sort(displayValues); } getCOSObject().setItem(COSName.OPT, COSArrayList.convertStringListToCOSStringCOSArray(displayValues)); } else { getCOSObject().removeItem(COSName.OPT); } } /** * This will set the display and export values - the 'Opt' key. * *

* This will set both, the export value and the display value * of the choice field. If either one of the parameters is null or an * empty list is supplied the options will * be removed. *

*

* An {@link IllegalArgumentException} will be thrown if the * number of items in the list differ. *

* * @see #setOptions(List) * @param exportValues List containing all possible export values. * @param displayValues List containing all possible display values. */ public void setOptions(List exportValues, List displayValues) { if (exportValues != null && displayValues != null && !exportValues.isEmpty() && !displayValues.isEmpty()) { requireArg(exportValues.size() == displayValues.size(), "The number of entries for exportValue and displayValue shall be the same."); List keyValuePairs = FieldUtils.toKeyValueList(exportValues, displayValues); if (isSort()) { FieldUtils.sortByValue(keyValuePairs); } COSArray options = new COSArray(); for (int i = 0; i < exportValues.size(); i++) { COSArray entry = new COSArray(); entry.add(COSString.parseLiteral(keyValuePairs.get(i).getKey())); entry.add(COSString.parseLiteral(keyValuePairs.get(i).getValue())); options.add(entry); } getCOSObject().setItem(COSName.OPT, options); } else { getCOSObject().removeItem(COSName.OPT); } } /** * This will get the display values from the options. * *

* For options with an array of text strings the display value and export value * are the same.
* For options with an array of two-element arrays the display value is the * second entry in the two-element array. *

* * @return List containing all the display values. */ public List getOptionsDisplayValues() { return FieldUtils.getPairableItems(getCOSObject().getDictionaryObject(COSName.OPT), 1); } /** * This will get the export values from the options. * *

* For options with an array of text strings the display value and export value * are the same.
* For options with an array of two-element arrays the export value is the * first entry in the two-element array. *

* * @return List containing all export values. */ public List getOptionsExportValues() { return getOptions(); } /** * This will get the indices of the selected options - the 'I' key. *

* This is only needed if a choice field allows multiple selections and * two different items have the same export value or more than one values * is selected. *

*

The indices are zero-based

* * @return List containing the indices of all selected options. */ public List getSelectedOptionsIndex() { COSBase value = getCOSObject().getDictionaryObject(COSName.I); if (value != null) { return COSArrayList.convertIntegerCOSArrayToList((COSArray) value); } return Collections.emptyList(); } /** * This will set the indices of the selected options - the 'I' key. *

* This method is preferred over {@link #setValue(List)} for choice fields which *

    *
  • do support multiple selections
  • *
  • have export values with the same value
  • *
*

*

* Setting the index will set the value too. *

* * @param values List containing the indices of all selected options. */ public void setSelectedOptionsIndex(List values) { if (values != null && !values.isEmpty()) { requireArg(isMultiSelect(), "Setting the indices is not allowed for choice fields not allowing multiple selections."); getCOSObject().setItem(COSName.I, COSArrayList.converterToCOSArray(values)); } else { getCOSObject().removeItem(COSName.I); } } /** * Determines if Sort is set. * *

* If set, the field’s option items shall be sorted alphabetically. * The sorting has to be done when writing the PDF. PDF Readers are supposed to * display the options in the order in which they occur in the Opt array. *

* * @return true if the options are sorted. */ public boolean isSort() { return getCOSObject().getFlag(COSName.FF, FLAG_SORT); } /** * Set the Sort bit. * * @see #isSort() * @param sort The value for Sort. */ public void setSort(boolean sort) { getCOSObject().setFlag(COSName.FF, FLAG_SORT, sort); } /** * Determines if MultiSelect is set. * * @return true if multi select is allowed. */ public boolean isMultiSelect() { return getCOSObject().getFlag(COSName.FF, FLAG_MULTI_SELECT); } /** * Set the MultiSelect bit. * * @param multiSelect The value for MultiSelect. */ public void setMultiSelect(boolean multiSelect) { getCOSObject().setFlag(COSName.FF, FLAG_MULTI_SELECT, multiSelect); } /** * Determines if DoNotSpellCheck is set. * * @return true if spell checker is disabled. */ public boolean isDoNotSpellCheck() { return getCOSObject().getFlag(COSName.FF, FLAG_DO_NOT_SPELL_CHECK); } /** * Set the DoNotSpellCheck bit. * * @param doNotSpellCheck The value for DoNotSpellCheck. */ public void setDoNotSpellCheck(boolean doNotSpellCheck) { getCOSObject().setFlag(COSName.FF, FLAG_DO_NOT_SPELL_CHECK, doNotSpellCheck); } /** * Determines if CommitOnSelChange is set. * * @return true if value shall be committed as soon as a selection is made. */ public boolean isCommitOnSelChange() { return getCOSObject().getFlag(COSName.FF, FLAG_COMMIT_ON_SEL_CHANGE); } /** * Set the CommitOnSelChange bit. * * @param commitOnSelChange The value for CommitOnSelChange. */ public void setCommitOnSelChange(boolean commitOnSelChange) { getCOSObject().setFlag(COSName.FF, FLAG_COMMIT_ON_SEL_CHANGE, commitOnSelChange); } /** * Determines if Combo is set. * * @return true if value the choice is a combo box.. */ public boolean isCombo() { return getCOSObject().getFlag(COSName.FF, FLAG_COMBO); } /** * Set the Combo bit. * * @param combo The value for Combo. */ public void setCombo(boolean combo) { getCOSObject().setFlag(COSName.FF, FLAG_COMBO, combo); } /** * Sets the selected value of this field. * * @param value The name of the selected item. * @throws IOException if the value could not be set */ @Override public void setValue(String value) throws IOException { getCOSObject().setString(COSName.V, value); // remove I key for single valued choice field setSelectedOptionsIndex(null); applyChange(); } /** * Sets the default value of this field. * * @param value The name of the selected item. * @throws IOException if the value could not be set */ public void setDefaultValue(String value) throws IOException { getCOSObject().setString(COSName.DV, value); } /** * Sets the entry "V" to the given values. Requires {@link #isMultiSelect()} to be true. * * @param values the list of values */ public void setValue(List values) throws IOException { if (values != null && !values.isEmpty()) { requireArg(isMultiSelect(), "The list box does not allow multiple selections."); requireArg(getOptions().containsAll(values), "The values are not contained in the selectable options."); getCOSObject().setItem(COSName.V, COSArrayList.convertStringListToCOSStringCOSArray(values)); updateSelectedOptionsIndex(values); } else { getCOSObject().removeItem(COSName.V); getCOSObject().removeItem(COSName.I); } applyChange(); } /** * Returns the selected values, or an empty List. This list always contains a single item unless * {@link #isMultiSelect()} is true. * * @return A non-null string. */ public List getValue() { return getValueFor(COSName.V); } /** * Returns the default values, or an empty List. This list always contains a single item unless * {@link #isMultiSelect()} is true. * * @return A non-null string. */ public List getDefaultValue() { return getValueFor(COSName.DV); } /** * Returns the selected values, or an empty List, for the given key. */ private List getValueFor(COSName name) { COSBase value = getCOSObject().getDictionaryObject(name); if (value instanceof COSString) { List array = new ArrayList<>(); array.add(((COSString) value).getString()); return array; } else if (value instanceof COSArray) { return COSArrayList.convertCOSStringCOSArrayToList((COSArray)value); } return Collections.emptyList(); } @Override public String getValueAsString() { return Arrays.toString(getValue().toArray()); } /** * Update the 'I' key based on values set. */ private void updateSelectedOptionsIndex(List values) { List options = getOptions(); List indices = new ArrayList<>(); for (String value : values) { indices.add(options.indexOf(value)); } // Indices have to be "... array of integers, sorted in ascending order ..." Collections.sort(indices); setSelectedOptionsIndex(indices); } @Override abstract void constructAppearances() throws IOException; } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDComboBox.java000066400000000000000000000051761320103431700304040ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.util.List; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * A combo box consisting of a drop-down list. May be accompanied by an editable text box in which non-predefined values * may be entered. * * @author John Hewson */ public final class PDComboBox extends PDChoice { private static final int FLAG_EDIT = 1 << 18; /** * @see PDField#PDField(PDAcroForm) * * @param acroForm The acroform. */ public PDComboBox(PDAcroForm acroForm) { super(acroForm); setCombo(true); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDComboBox(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * Determines if Edit is set. * * @return true if the combo box shall include an editable text box as well as a drop-down list. */ public boolean isEdit() { return getCOSObject().getFlag(COSName.FF, FLAG_EDIT); } /** * Set the Edit bit. * * @param edit The value for Edit. */ public void setEdit(boolean edit) { getCOSObject().setFlag(COSName.FF, FLAG_EDIT, edit); } @Override void constructAppearances() throws IOException { AppearanceGeneratorHelper apHelper; apHelper = new AppearanceGeneratorHelper(this); List values = getValue(); if (!values.isEmpty()) { apHelper.setAppearanceValue(values.get(0)); } else { apHelper.setAppearanceValue(""); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDDefaultAppearanceString.java000066400000000000000000000236001320103431700334170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import static org.sejda.io.SeekableSources.inMemorySeekableSourceFrom; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.sejda.sambox.contentstream.operator.Operator; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.input.ContentStreamParser; import org.sejda.sambox.pdmodel.PDPageContentStream; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.font.PDType1Font; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceCMYK; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceRGB; import org.sejda.sambox.pdmodel.interactive.annotation.PDAppearanceStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Represents a default appearance string, as found in the /DA entry of free text annotations. * *

* The default appearance string (DA) contains any graphics state or text state operators needed to establish the * graphics state parameters, such as text size and colour, for displaying the field’s variable text. Only operators * that are allowed within text objects shall occur in this string. * * Note: This class is not yet public, as its API is still unstable. */ class PDDefaultAppearanceString { private static final Logger LOG = LoggerFactory.getLogger(PDDefaultAppearanceString.class); /** * The default font size used by Acrobat. */ private static final float DEFAULT_FONT_SIZE = 12; private COSName fontName; private PDFont font; private float fontSize = DEFAULT_FONT_SIZE; private PDColor fontColor; private final PDResources defaultResources; /** * Constructor for reading an existing DA string. * * @param defaultResources DR entry * @param defaultAppearance DA entry * @throws IOException If the DA could not be parsed */ PDDefaultAppearanceString(COSString defaultAppearance, PDResources defaultResources) throws IOException { if (defaultAppearance == null) { defaultAppearance = new COSString("/Helvetica 0 Tf 0 g".getBytes()); } if (defaultResources == null) { defaultResources = new PDResources(); } this.defaultResources = defaultResources; processAppearanceStringOperators(defaultAppearance.getBytes()); } /** * Processes the operators of the given content stream. * * @param content the content to parse. * @throws IOException if there is an error reading or parsing the content stream. */ private void processAppearanceStringOperators(byte[] content) throws IOException { List arguments = new ArrayList<>(); try (ContentStreamParser parser = new ContentStreamParser( inMemorySeekableSourceFrom(content))) { for (Object token : parser.tokens()) { if (token instanceof COSBase) { arguments.add(((COSBase) token).getCOSObject()); } else if (token instanceof Operator) { processOperator((Operator) token, arguments); arguments = new ArrayList<>(); } else { LOG.error("Unrecognized operator type {}", token); } } } } /** * This is used to handle an operation. * * @param operator The operation to perform. * @param operands The list of arguments. * @throws IOException If there is an error processing the operation. */ private void processOperator(Operator operator, List operands) throws IOException { String name = operator.getName(); if ("Tf".equals(name)) { processSetFont(operands); } else if ("g".equals(name)) { processSetFontColor(operands); } else if ("rg".equals(name)) { processSetFontColor(operands); } else if ("k".equals(name)) { processSetFontColor(operands); } } /** * Process the set font and font size operator. * * @param operands the font name and size * @throws IOException in case there are missing operators or the font is not within the resources */ private void processSetFont(List operands) throws IOException { if (operands.size() < 2) { throw new IOException("Missing operands for set font operator " + Arrays.toString(operands.toArray())); } COSBase base0 = operands.get(0); COSBase base1 = operands.get(1); if (!(base0 instanceof COSName)) { return; } if (!(base1 instanceof COSNumber)) { return; } COSName fontName = (COSName) base0; PDFont font = defaultResources.getFont(fontName); float fontSize = ((COSNumber) base1).floatValue(); // todo: handle cases where font == null with special mapping logic (see PDFBOX-2661) if (font == null) { LOG.warn( "Could not find font: /" + fontName.getName() + ", will use Helvetica instead"); font = PDType1Font.HELVETICA; } setFontName(fontName); setFont(font); setFontSize(fontSize); } /** * Process the font color operator. * * This is assumed to be an RGB color. * * @param operands the color components * @throws IOException in case of the color components not matching */ private void processSetFontColor(List operands) throws IOException { PDColorSpace colorSpace; switch (operands.size()) { case 1: colorSpace = PDDeviceGray.INSTANCE; break; case 3: colorSpace = PDDeviceRGB.INSTANCE; break; case 4: colorSpace = PDDeviceCMYK.INSTANCE; break; default: throw new IOException("Missing operands for set non stroking color operator " + Arrays.toString(operands.toArray())); } COSArray array = new COSArray(); array.addAll(operands); setFontColor(new PDColor(array, colorSpace)); } /** * Get the font name * * @return the font name to use for resource lookup */ COSName getFontName() { return fontName; } /** * Set the font name. * * @param fontName the font name to use for resource lookup */ void setFontName(COSName fontName) { this.fontName = fontName; } /** * Returns the font. */ PDFont getFont() throws IOException { return font; } /** * Set the font. * * @param font the font to use. */ void setFont(PDFont font) { this.font = font; } /** * Returns the font size. */ public float getFontSize() { return fontSize; } /** * Set the font size. * * @param fontSize the font size. */ void setFontSize(float fontSize) { this.fontSize = fontSize; } /** * Returns the font color */ PDColor getFontColor() { return fontColor; } /** * Set the font color. * * @param fontColor the fontColor to use. */ void setFontColor(PDColor fontColor) { this.fontColor = fontColor; } /** * Writes the DA string to the given content stream. */ void writeTo(PDPageContentStream contents, float zeroFontSize) throws IOException { float fontSize = getFontSize(); if (fontSize == 0) { fontSize = zeroFontSize; } contents.setFont(getFont(), fontSize); if (getFontColor() != null) { contents.setNonStrokingColor(getFontColor()); } } /** * Copies any needed resources from the document’s DR dictionary into the stream’s Resources dictionary. Resources * with the same name shall be left intact. */ void copyNeededResourcesTo(PDAppearanceStream appearanceStream) throws IOException { // make sure we have resources PDResources streamResources = appearanceStream.getResources(); if (streamResources == null) { streamResources = new PDResources(); appearanceStream.setResources(streamResources); } if (streamResources.getFont(getFontName()) == null) { streamResources.put(fontName, getFont()); } // todo: other kinds of resource... } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDField.java000066400000000000000000000246071320103431700277170ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; import org.sejda.sambox.pdmodel.interactive.action.PDFormFieldAdditionalActions; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationWidget; /** * A field in an interactive form. */ public abstract class PDField extends PDDictionaryWrapper { private static final int FLAG_READ_ONLY = 1; private static final int FLAG_REQUIRED = 1 << 1; private static final int FLAG_NO_EXPORT = 1 << 2; private final PDAcroForm acroForm; private final PDNonTerminalField parent; /** * Constructor. * * @param acroForm The form that this field is part of. */ PDField(PDAcroForm acroForm) { this(acroForm, new COSDictionary(), null); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param dictionary the PDF object to represent as a field. * @param parent the parent node of the node */ PDField(PDAcroForm acroForm, COSDictionary dictionary, PDNonTerminalField parent) { super(dictionary); this.acroForm = acroForm; this.parent = parent; } /** * Creates a COSField subclass from the given COS field. This is for reading fields from PDFs. * * @param form the form that the field is part of * @param field the dictionary representing a field element * @param parent the parent node of the node to be created, or null if root. * @return a new PDField instance */ static PDField fromDictionary(PDAcroForm form, COSDictionary field, PDNonTerminalField parent) { return PDFieldFactory.createField(form, field, parent); } /** * Returns the given attribute, inheriting from parent nodes if necessary. * * @param key the key to look up * @return COS value for the given key */ protected COSBase getInheritableAttribute(COSName key) { if (getCOSObject().containsKey(key)) { return getCOSObject().getDictionaryObject(key); } else if (parent != null) { return parent.getInheritableAttribute(key); } else { return acroForm.getCOSObject().getDictionaryObject(key); } } /** * Get the FT entry of the field. This is a read only field and is set depending on the actual type. The field type * is an inheritable attribute. * * @return The Field type. * */ public abstract String getFieldType(); /** * Returns a string representation of the "V" entry, or an empty string. * * @return A non-null string. */ public abstract String getValueAsString(); /** * Sets the value of the field. * * @param value the new field value. * * @throws IOException if the value could not be set */ public abstract void setValue(String value) throws IOException; /** * Returns the widget annotations associated with this field. * * For {@link PDNonTerminalField} the list will be empty as non terminal fields have no visual representation in the * form. * * @return A non-null string. */ public abstract List getWidgets(); /** * sets the field to be read-only. * * @param readonly The new flag for readonly. */ public void setReadonly(boolean readonly) { getCOSObject().setFlag(COSName.FF, FLAG_READ_ONLY, readonly); } /** * * @return true if the field is readonly */ public boolean isReadonly() { return getCOSObject().getFlag(COSName.FF, FLAG_READ_ONLY); } /** * sets the field to be required. * * @param required The new flag for required. */ public void setRequired(boolean required) { getCOSObject().setFlag(COSName.FF, FLAG_REQUIRED, required); } /** * * @return true if the field is required */ public boolean isRequired() { return getCOSObject().getFlag(COSName.FF, FLAG_REQUIRED); } /** * sets the field to be not exported. * * @param noExport The new flag for noExport. */ public void setNoExport(boolean noExport) { getCOSObject().setFlag(COSName.FF, FLAG_NO_EXPORT, noExport); } /** * * @return true if the field is not to be exported. */ public boolean isNoExport() { return getCOSObject().getFlag(COSName.FF, FLAG_NO_EXPORT); } /** * This will get the flags for this field. * * @return flags The set of flags. */ public abstract int getFieldFlags(); /** * This will set the flags for this field. * * @param flags The new flags. */ public void setFieldFlags(int flags) { getCOSObject().setInt(COSName.FF, flags); } /** * Get the additional actions for this field. This will return null if there are no additional actions for this * field. * * @return The actions of the field. */ public PDFormFieldAdditionalActions getActions() { COSDictionary aa = (COSDictionary) getCOSObject().getDictionaryObject(COSName.AA); if (aa != null) { return new PDFormFieldAdditionalActions(aa); } return null; } /** * Get the parent field to this field, or null if none exists. * * @return The parent field. */ public PDNonTerminalField getParent() { return parent; } /** * This will find one of the child elements. The name array are the components of the name to search down the tree * of names. The nameIndex is where to start in that array. This method is called recursively until it finds the end * point based on the name array. * * @param name An array that picks the path to the field. * @param nameIndex The index into the array. * @return The field at the endpoint or null if none is found. */ PDField findKid(String[] name, int nameIndex) { PDField retval = null; COSArray kids = (COSArray) getCOSObject().getDictionaryObject(COSName.KIDS); if (kids != null) { for (int i = 0; retval == null && i < kids.size(); i++) { COSDictionary kidDictionary = (COSDictionary) kids.getObject(i); if (name[nameIndex].equals(kidDictionary.getString(COSName.T))) { retval = PDField.fromDictionary(acroForm, kidDictionary, (PDNonTerminalField) this); if (name.length > nameIndex + 1) { retval = retval.findKid(name, nameIndex + 1); } } } } return retval; } /** * This will get the acroform that this field is part of. * * @return The form this field is on. */ public PDAcroForm getAcroForm() { return acroForm; } /** * Returns the partial name of the field. * * @return the name of the field */ public String getPartialName() { return getCOSObject().getString(COSName.T); } /** * This will set the partial name of the field. * * @param name The new name for the field. */ public void setPartialName(String name) { getCOSObject().setString(COSName.T, name); } /** * Returns the fully qualified name of the field, which is a concatenation of the names of all the parents fields. * * @return the name of the field */ public String getFullyQualifiedName() { String finalName = getPartialName(); String parentName = parent != null ? parent.getFullyQualifiedName() : null; if (parentName != null) { if (finalName != null) { finalName = parentName + "." + finalName; } else { finalName = parentName; } } return finalName; } /** * Gets the alternate name of the field. * * @return the alternate name of the field */ public String getAlternateFieldName() { return getCOSObject().getString(COSName.TU); } /** * This will set the alternate name of the field. * * @param alternateFieldName the alternate name of the field */ public void setAlternateFieldName(String alternateFieldName) { getCOSObject().setString(COSName.TU, alternateFieldName); } /** * Gets the mapping name of the field. * * The mapping name shall be used when exporting interactive form field data from the document. * * @return the mapping name of the field */ public String getMappingName() { return getCOSObject().getString(COSName.TM); } /** * This will set the mapping name of the field. * * @param mappingName the mapping name of the field */ public void setMappingName(String mappingName) { getCOSObject().setString(COSName.TM, mappingName); } public abstract boolean isTerminal(); @Override public String toString() { return getFullyQualifiedName() + "{type: " + getClass().getSimpleName() + " value: " + getInheritableAttribute(COSName.V) + "}"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDFieldFactory.java000066400000000000000000000105421320103431700312400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.util.Optional; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * A PDField factory. */ public final class PDFieldFactory { private PDFieldFactory() { } private static final String FIELD_TYPE_TEXT = "Tx"; private static final String FIELD_TYPE_BUTTON = "Btn"; private static final String FIELD_TYPE_CHOICE = "Ch"; private static final String FIELD_TYPE_SIGNATURE = "Sig"; /** * Creates and sets it as a child of the given parent {@link PDNonTerminalField} * * @param form * @param field * @param parent * @return */ public static PDField createFieldAddingChildToParent(PDAcroForm form, COSDictionary field, PDNonTerminalField parent) { PDField retField = createField(form, field, parent); Optional.ofNullable(retField.getParent()).ifPresent(p -> p.addChild(retField)); return retField; } /** * Creates a {@link PDField} subclass from the given field. * * @param form the form that the field is part of * @param field the dictionary representing a field element * @param parent the parent node of the node to be created * @return the corresponding PDField instance */ public static PDField createField(PDAcroForm form, COSDictionary field, PDNonTerminalField parent) { String fieldType = findFieldType(field); if (FIELD_TYPE_CHOICE.equals(fieldType)) { return createChoiceSubType(form, field, parent); } else if (FIELD_TYPE_TEXT.equals(fieldType)) { return new PDTextField(form, field, parent); } else if (FIELD_TYPE_SIGNATURE.equals(fieldType)) { return new PDSignatureField(form, field, parent); } else if (FIELD_TYPE_BUTTON.equals(fieldType)) { return createButtonSubType(form, field, parent); } return new PDNonTerminalField(form, field, parent); } private static PDField createChoiceSubType(PDAcroForm form, COSDictionary field, PDNonTerminalField parent) { int flags = field.getInt(COSName.FF, 0); if ((flags & PDChoice.FLAG_COMBO) != 0) { return new PDComboBox(form, field, parent); } return new PDListBox(form, field, parent); } private static PDField createButtonSubType(PDAcroForm form, COSDictionary field, PDNonTerminalField parent) { int flags = field.getInt(COSName.FF, 0); // BJL: I have found that the radio flag bit is not always set // and that sometimes there is just a kids dictionary. // so, if there is a kids dictionary then it must be a radio button group. if ((flags & PDButton.FLAG_RADIO) != 0) { return new PDRadioButton(form, field, parent); } else if ((flags & PDButton.FLAG_PUSHBUTTON) != 0) { return new PDPushButton(form, field, parent); } else { return new PDCheckBox(form, field, parent); } } private static String findFieldType(COSDictionary dic) { String retval = dic.getNameAsString(COSName.FT); if (retval == null) { COSDictionary parent = (COSDictionary) dic.getDictionaryObject(COSName.PARENT, COSName.P); if (parent != null) { retval = findFieldType(parent); } } return retval; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDFieldTree.java000066400000000000000000000103511320103431700305260ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Queue; import java.util.Spliterator; import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * The field tree allowing a post-order iteration of the nodes. */ public class PDFieldTree implements Iterable { private final PDAcroForm acroForm; /** * @param acroForm the AcroForm containing the fields. */ public PDFieldTree(PDAcroForm acroForm) { requireNotNullArg(acroForm, "root cannot be null"); this.acroForm = acroForm; } /** * @return an iterator which walks all fields in the tree, post-order. */ @Override public Iterator iterator() { return new FieldIterator(acroForm); } /** * Iterator which walks all fields in the tree, post-order. */ private static final class FieldIterator implements Iterator { private final Queue queue = new ArrayDeque<>(); private FieldIterator(PDAcroForm form) { for (PDField field : form.getFields()) { enqueueKids(field); } } @Override public boolean hasNext() { return !queue.isEmpty(); } @Override public PDField next() { return queue.remove(); } @Override public void remove() { throw new UnsupportedOperationException(); } private void enqueueKids(PDField node) { if (node instanceof PDNonTerminalField) { List kids = ((PDNonTerminalField) node).getChildren(); for (PDField kid : kids) { enqueueKids(kid); } } queue.add(node); } } /** * @return a pre order sequential {@code Stream} over the fields of this form. */ public Stream stream() { return StreamSupport.stream(Spliterators.spliteratorUnknownSize( new PreOrderIterator(acroForm), Spliterator.ORDERED | Spliterator.NONNULL), false); } /** * Iterator which walks all the fields pre order */ private final class PreOrderIterator implements Iterator { private final Deque queue = new ArrayDeque<>(); private PreOrderIterator(PDAcroForm form) { for (PDField field : form.getFields()) { enqueueKids(field); } } private void enqueueKids(PDField node) { queue.add(node); if (node instanceof PDNonTerminalField) { List kids = ((PDNonTerminalField) node).getChildren(); for (PDField kid : kids) { enqueueKids(kid); } } } @Override public boolean hasNext() { return !queue.isEmpty(); } @Override public PDField next() { return queue.remove(); } @Override public void remove() { throw new UnsupportedOperationException(); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDListBox.java000066400000000000000000000047271320103431700302610ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * A scrollable list box. Contains several text items, one or more of which shall be selected as the field value. * * @author John Hewson */ public final class PDListBox extends PDChoice { /** * @see PDField#PDField(PDAcroForm) * * @param acroForm The acroform. */ public PDListBox(PDAcroForm acroForm) { super(acroForm); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDListBox(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * This will get the top index "TI" value. * * @return the top index, default value 0. */ public int getTopIndex() { return getCOSObject().getInt(COSName.TI, 0); } /** * This will set top index "TI" value. * * @param topIndex the value for the top index, null will remove the value. */ public void setTopIndex(Integer topIndex) { if (topIndex != null) { getCOSObject().setInt(COSName.TI, topIndex); } else { getCOSObject().removeItem(COSName.TI); } } @Override void constructAppearances() throws IOException { AppearanceGeneratorHelper apHelper; apHelper = new AppearanceGeneratorHelper(this); apHelper.setAppearanceValue(""); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDNonTerminalField.java000066400000000000000000000165471320103431700320720ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import static java.util.Objects.nonNull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationWidget; /** * A non terminal field in an interactive form. * * A non terminal field is a node in the fields tree node whose descendants are fields. * * The attributes such as FT (field type) or V (field value) do not logically belong to the non terminal field but are * inheritable attributes for descendant terminal fields. */ public class PDNonTerminalField extends PDField { /** * Constructor. * * @param acroForm The form that this field is part of. */ public PDNonTerminalField(PDAcroForm acroForm) { super(acroForm); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node to be created */ PDNonTerminalField(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } @Override public int getFieldFlags() { int retval = 0; COSInteger ff = (COSInteger) getCOSObject().getDictionaryObject(COSName.FF); if (ff != null) { retval = ff.intValue(); } // There is no need to look up the parent hierarchy within a non terminal field return retval; } /** * @return this field's children. These may be either terminal or non-terminal fields. */ public List getChildren() { List children = new ArrayList<>(); COSArray kids = getCOSObject().getDictionaryObject(COSName.KIDS, COSArray.class); if (kids != null) { for (COSBase kid : kids) { if (nonNull(kid) && kid.getCOSObject() instanceof COSDictionary) { children.add(PDField.fromDictionary(getAcroForm(), (COSDictionary) kid.getCOSObject(), this)); } } } return children; } /** * * @return true if the field has at least one child */ public boolean hasChildren() { return getChildren().size() > 0; } /** * Sets the child fields. * * @param children The list of child fields. */ public void setChildren(List children) { getCOSObject().setItem(COSName.KIDS, COSArrayList.converterToCOSArray(children)); } /** * Adds a child to the array of children * * @param field */ public void addChild(PDField field) { COSArray kids = (COSArray) getCOSObject().getDictionaryObject(COSName.KIDS); if (kids == null) { kids = new COSArray(); } if (!kids.contains(field)) { kids.add(field); field.getCOSObject().setItem(COSName.PARENT, this); getCOSObject().setItem(COSName.KIDS, kids); } } /** * Removes the given node from the children list * * @param field * @return the removed COSBase or null */ public COSBase removeChild(PDField field) { COSArray kids = getCOSObject().getDictionaryObject(COSName.KIDS, COSArray.class); if (nonNull(kids)) { int removeIdx = kids.indexOfObject(field.getCOSObject()); if (removeIdx >= 0) { return kids.remove(removeIdx); } } return null; } /** *

* Note: while non-terminal fields do inherit field values, this method returns the local value, * without inheritance. */ @Override public String getFieldType() { return getCOSObject().getNameAsString(COSName.FT); } /** *

* Note: while non-terminal fields do inherit field values, this method returns the local value, * without inheritance. */ public COSBase getValue() { return getCOSObject().getDictionaryObject(COSName.V); } /** *

* Note: while non-terminal fields do inherit field values, this method returns the local value, * without inheritance. */ @Override public String getValueAsString() { return getCOSObject().getDictionaryObject(COSName.V).toString(); } /** * Sets the value of this field. This may be of any kind which is valid for this field's children. * *

* Note: while non-terminal fields do inherit field values, this method returns the local value, * without inheritance. */ public void setValue(COSBase object) { getCOSObject().setItem(COSName.V, object); // todo: propagate change event to children? // todo: construct appearances of children? } /** * Sets the plain text value of this field. * * @param value Plain text * @throws IOException if the value could not be set */ @Override public void setValue(String value) { getCOSObject().setString(COSName.V, value); // todo: propagate change event to children? // todo: construct appearances of children? } /** * Returns the default value of this field. This may be of any kind which is valid for this field's children. * *

* Note: while non-terminal fields do inherit field values, this method returns the local value, * without inheritance. */ public COSBase getDefaultValue() { return getCOSObject().getDictionaryObject(COSName.DV); } /** * Sets the default of this field. This may be of any kind which is valid for this field's children. * *

* Note: while non-terminal fields do inherit field values, this method returns the local value, * without inheritance. */ public void setDefaultValue(COSBase value) { getCOSObject().setItem(COSName.V, value); } @Override public List getWidgets() { return Collections.emptyList(); } @Override public boolean isTerminal() { return false; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDPushButton.java000066400000000000000000000047351320103431700310070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.util.Collections; import java.util.List; import org.sejda.sambox.cos.COSDictionary; /** * A pushbutton is a purely interactive control that responds immediately to user * input without retaining a permanent value. * * @author sug */ public class PDPushButton extends PDButton { /** * @see PDField#PDField(PDAcroForm) * * @param acroForm The acroform. */ public PDPushButton(PDAcroForm acroForm) { super(acroForm); setPushButton(true); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDPushButton(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } @Override public List getExportValues() { return Collections.emptyList(); } @Override public void setExportValues(List values) { if (values != null && !values.isEmpty()) { throw new IllegalArgumentException("A PDPushButton shall not use the Opt entry in the field dictionary"); } } @Override public String getValue() { return ""; } @Override public String getDefaultValue() { return ""; } @Override public String getValueAsString() { return getValue(); } @Override void constructAppearances() throws IOException { // TODO: add appearance handler to generate/update appearance } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDRadioButton.java000066400000000000000000000076101320103431700311210ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * Radio button fields contain a set of related buttons that can each be on or off. * * @author sug */ public final class PDRadioButton extends PDButton { /** * @see PDField#PDField(PDAcroForm) * * @param acroForm The acroform. */ public PDRadioButton(PDAcroForm acroForm) { super(acroForm); setRadioButton(true); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDRadioButton(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * From the PDF Spec
* If set, a group of radio buttons within a radio button field that use the same value for the on state will turn * on and off in unison; that is if one is checked, they are all checked. If clear, the buttons are mutually * exclusive (the same behavior as HTML radio buttons). * * @param radiosInUnison The new flag for radiosInUnison. */ public void setRadiosInUnison(boolean radiosInUnison) { getCOSObject().setFlag(COSName.FF, FLAG_RADIOS_IN_UNISON, radiosInUnison); } /** * * @return true If the flag is set for radios in unison. */ public boolean isRadiosInUnison() { return getCOSObject().getFlag(COSName.FF, FLAG_RADIOS_IN_UNISON); } /** * This will get the selected export values. *

* A RadioButton might have an export value to allow field values which can not be encoded as PDFDocEncoding or for * the same export value being assigned to multiple RadioButtons in a group.
* To define an export value the RadioButton must define options {@link #setExportValues(List)} which correspond to * the individual items within the RadioButton. *

*

* The method will either return the corresponding values from the options entry or in case there is no such entry * the fields value *

* * @return the export value of the field. * @throws IOException in case the fields value can not be retrieved */ public List getSelectedExportValues() { Set onValues = getOnValues(); List exportValues = getExportValues(); List selectedExportValues = new ArrayList<>(); if (exportValues.isEmpty()) { selectedExportValues.add(getValue()); return selectedExportValues; } String fieldValue = getValue(); int idx = 0; for (String onValue : onValues) { if (onValue.compareTo(fieldValue) == 0) { selectedExportValues.add(exportValues.get(idx)); } } return selectedExportValues; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDSignatureField.java000066400000000000000000000072041320103431700315730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.util.Optional; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationWidget; /** * A signature field is a form field that contains a digital signature. * * @author Ben Litchfield * @author Thomas Chojecki */ // TODO this is currently just an empty shell to distinguish the type of a field public class PDSignatureField extends PDTerminalField { /** * @see PDTerminalField#PDTerminalField(PDAcroForm) * * @param acroForm The acroForm for this field. * @throws IOException If there is an error while resolving partial name for the signature field or getting the * widget object. */ public PDSignatureField(PDAcroForm acroForm) { super(acroForm); getCOSObject().setItem(COSName.FT, COSName.SIG); getWidgets().get(0).setLocked(true); getWidgets().get(0).setPrinted(true); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node to be created */ PDSignatureField(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } @Override public String getValueAsString() { return Optional.ofNullable(getCOSObject().getDictionaryObject(COSName.V)) .map(COSBase::toString).orElse(""); } @Override void constructAppearances() { PDAnnotationWidget widget = this.getWidgets().get(0); if (widget != null) { // check if the signature is visible if (widget.getRectangle() == null || widget.getRectangle().getHeight() == 0 && widget.getRectangle().getWidth() == 0 || widget.isNoView() || widget.isHidden()) { return; } // TODO: implement appearance generation for signatures throw new UnsupportedOperationException("not implemented"); } } /** * Sets the value of this field. * * This will throw an UnsupportedOperationException if used as the signature fields value can't be set using a * String * * @param value the plain text value. * * @throws UnsupportedOperationException in all cases! */ @Override public void setValue(String value) throws UnsupportedOperationException { throw new UnsupportedOperationException( "Signature fields don't support setting the value as String " + "- use setValue(PDSignature value) instead"); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDTerminalField.java000066400000000000000000000134701320103431700314070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import static java.util.Objects.nonNull; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSArrayList; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNull; import org.sejda.sambox.pdmodel.interactive.action.PDFormFieldAdditionalActions; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationWidget; /** * A field in an interactive form. Fields may be one of four types: button, text, choice, or signature. * * @author sug */ public abstract class PDTerminalField extends PDField { /** * Constructor. * * @param acroForm The form that this field is part of. */ protected PDTerminalField(PDAcroForm acroForm) { super(acroForm); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDTerminalField(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * Set the actions of the field. * * @param actions The field actions. */ public void setActions(PDFormFieldAdditionalActions actions) { getCOSObject().setItem(COSName.AA, actions); } @Override public int getFieldFlags() { int retval = 0; COSInteger ff = (COSInteger) getCOSObject().getDictionaryObject(COSName.FF); if (ff != null) { retval = ff.intValue(); } else if (getParent() != null) { retval = getParent().getFieldFlags(); } return retval; } @Override public String getFieldType() { String fieldType = getCOSObject().getNameAsString(COSName.FT); if (fieldType == null && getParent() != null) { fieldType = getParent().getFieldType(); } return fieldType; } /** * Returns the widget annotations associated with this field. * * @return The list of widget annotations. */ @Override public List getWidgets() { List widgets = new ArrayList<>(); COSArray kids = (COSArray) getCOSObject().getDictionaryObject(COSName.KIDS); if (kids == null) { // the field itself is a widget widgets.add(new PDAnnotationWidget(getCOSObject())); } else if (kids.size() > 0) { // there are multiple widgets for (COSBase kid : kids) { if (nonNull(kid) && !COSNull.NULL.equals(kid.getCOSObject())) { widgets.add(new PDAnnotationWidget((COSDictionary) kid.getCOSObject())); } } } return widgets; } /** * Adds the given widget as child of this fields, only if not already present * * @param widget */ public void addWidgetIfMissing(PDAnnotationWidget widget) { if (nonNull(widget)) { COSArray kids = (COSArray) getCOSObject().getDictionaryObject(COSName.KIDS); if (kids == null) { kids = new COSArray(widget.getCOSObject()); widget.getCOSObject().setItem(COSName.PARENT, this); getCOSObject().setItem(COSName.KIDS, kids); } else if (!kids.contains(widget.getCOSObject())) { kids.add(widget.getCOSObject()); widget.getCOSObject().setItem(COSName.PARENT, this); } } } /** * Sets the field's widget annotations. * * @param children The list of widget annotations. */ public void setWidgets(List children) { getCOSObject().setItem(COSName.KIDS, COSArrayList.converterToCOSArray(children)); for (PDAnnotationWidget widget : children) { widget.getCOSObject().setItem(COSName.PARENT, this); } } /** * Applies a value change to the field. Generates appearances if required and raises events. * * @throws IOException if the appearance couldn't be generated */ protected final void applyChange() throws IOException { constructAppearances(); // if we supported JavaScript we would raise a field changed event here } /** * Constructs appearance streams and appearance dictionaries for all widget annotations. Subclasses should not call * this method directly but via {@link #applyChange()}. * * @throws IOException if the appearance couldn't be generated */ abstract void constructAppearances() throws IOException; @Override public boolean isTerminal() { return true; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDTextField.java000066400000000000000000000157621320103431700305660ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * A text field is a box or space for text fill-in data typically entered from a keyboard. * The text may be restricted to a single line or may be permitted to span multiple lines * * @author sug */ public final class PDTextField extends PDVariableText { private static final int FLAG_MULTILINE = 1 << 12; private static final int FLAG_PASSWORD = 1 << 13; private static final int FLAG_FILE_SELECT = 1 << 20; private static final int FLAG_DO_NOT_SPELL_CHECK = 1 << 22; private static final int FLAG_DO_NOT_SCROLL = 1 << 23; private static final int FLAG_COMB = 1 << 24; private static final int FLAG_RICH_TEXT = 1 << 25; /** * @see PDField#PDField(PDAcroForm) * * @param acroForm The acroform. */ public PDTextField(PDAcroForm acroForm) { super(acroForm); getCOSObject().setItem(COSName.FT, COSName.TX); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDTextField(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * @return true if the field is multiline */ public boolean isMultiline() { return getCOSObject().getFlag(COSName.FF, FLAG_MULTILINE); } /** * Set the multiline bit. * * @param multiline The value for the multiline. */ public void setMultiline(boolean multiline) { getCOSObject().setFlag(COSName.FF, FLAG_MULTILINE, multiline); } /** * @return true if the field is a password field. */ public boolean isPassword() { return getCOSObject().getFlag(COSName.FF, FLAG_PASSWORD); } /** * Set the password bit. * * @param password The value for the password. */ public void setPassword(boolean password) { getCOSObject().setFlag(COSName.FF, FLAG_PASSWORD, password); } /** * @return true if the field is a file select field. */ public boolean isFileSelect() { return getCOSObject().getFlag(COSName.FF, FLAG_FILE_SELECT); } /** * Set the file select bit. * * @param fileSelect The value for the fileSelect. */ public void setFileSelect(boolean fileSelect) { getCOSObject().setFlag(COSName.FF, FLAG_FILE_SELECT, fileSelect); } /** * @return true if the field is not suppose to spell check. */ public boolean doNotSpellCheck() { return getCOSObject().getFlag(COSName.FF, FLAG_DO_NOT_SPELL_CHECK); } /** * Set the doNotSpellCheck bit. * * @param doNotSpellCheck The value for the doNotSpellCheck. */ public void setDoNotSpellCheck(boolean doNotSpellCheck) { getCOSObject().setFlag(COSName.FF, FLAG_DO_NOT_SPELL_CHECK, doNotSpellCheck); } /** * @return true if the field is not suppose to scroll. */ public boolean doNotScroll() { return getCOSObject().getFlag(COSName.FF, FLAG_DO_NOT_SCROLL); } /** * Set the doNotScroll bit. * * @param doNotScroll The value for the doNotScroll. */ public void setDoNotScroll(boolean doNotScroll) { getCOSObject().setFlag(COSName.FF, FLAG_DO_NOT_SCROLL, doNotScroll); } /** * @return true if the field is not suppose to comb the text display. */ public boolean isComb() { return getCOSObject().getFlag(COSName.FF, FLAG_COMB); } /** * Set the comb bit. * * @param comb The value for the comb. */ public void setComb(boolean comb) { getCOSObject().setFlag(COSName.FF, FLAG_COMB, comb); } /** * @return true if the field is a rich text field. */ public boolean isRichText() { return getCOSObject().getFlag(COSName.FF, FLAG_RICH_TEXT); } /** * Set the richText bit. * * @param richText The value for the richText. */ public void setRichText(boolean richText) { getCOSObject().setFlag(COSName.FF, FLAG_RICH_TEXT, richText); } /** * Returns the maximum number of characters of the text field. * * @return the maximum number of characters, returns -1 if the value isn't present */ public int getMaxLen() { return getCOSObject().getInt(COSName.MAX_LEN); } /** * Sets the maximum number of characters of the text field. * * @param maxLen the maximum number of characters */ public void setMaxLen(int maxLen) { getCOSObject().setInt(COSName.MAX_LEN, maxLen); } /** * Sets the plain text value of this field. * * @param value Plain text * @throws IOException if the value could not be set */ @Override public void setValue(String value) throws IOException { getCOSObject().setString(COSName.V, value); applyChange(); } /** * Sets the default value of this field. * * @param value Plain text * @throws IOException if the value could not be set */ public void setDefaultValue(String value) throws IOException { getCOSObject().setString(COSName.DV, value); } /** * Returns the value of this field, or an empty string. * * @return A non-null string. */ public String getValue() { return getStringOrStream(getInheritableAttribute(COSName.V)); } /** * Returns the default value of this field, or an empty string. * * @return A non-null string. */ public String getDefaultValue() { return getStringOrStream(getInheritableAttribute(COSName.DV)); } @Override public String getValueAsString() { return getValue(); } @Override void constructAppearances() throws IOException { AppearanceGeneratorHelper apHelper; apHelper = new AppearanceGeneratorHelper(this); apHelper.setAppearanceValue(getValue()); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDVariableText.java000066400000000000000000000166561320103431700312730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.cos.COSStream; import org.sejda.sambox.cos.COSString; import org.sejda.sambox.pdmodel.PDResources; import org.sejda.sambox.pdmodel.font.PDFont; /** * Base class for fields which use "Variable Text". These fields construct an appearance stream dynamically at viewing * time. * * @author Ben Litchfield */ public abstract class PDVariableText extends PDTerminalField { public static final int QUADDING_LEFT = 0; public static final int QUADDING_CENTERED = 1; public static final int QUADDING_RIGHT = 2; private PDFont appearanceOverrideFont = null; /** * @see PDTerminalField#PDTerminalField(PDAcroForm) * * @param acroForm The acroform. */ PDVariableText(PDAcroForm acroForm) { super(acroForm); } /** * Constructor. * * @param acroForm The form that this field is part of. * @param field the PDF object to represent as a field. * @param parent the parent node of the node */ PDVariableText(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent) { super(acroForm, field, parent); } /** * Get the default appearance. * * This is an inheritable attribute. * * The default appearance contains a set of default graphics and text operators to define the field’s text size and * color. * * @return the DA element of the dictionary object */ public String getDefaultAppearance() { COSString defaultAppearance = (COSString) getInheritableAttribute(COSName.DA); return defaultAppearance.getString(); } /** * Get the default appearance. * * This is an inheritable attribute. * * The default appearance contains a set of default graphics and text operators to define the field’s text size and * color. * * @return the DA element of the dictionary object */ PDDefaultAppearanceString getDefaultAppearanceString() throws IOException { COSString da = (COSString) getInheritableAttribute(COSName.DA); PDResources dr = getAcroForm().getDefaultResources(); return new PDDefaultAppearanceString(da, dr); } /** * Set the default appearance. * * This will set the local default appearance for the variable text field only not affecting a default appearance in * the parent hierarchy. * * Providing null as the value will remove the local default appearance. * * @param daValue a string describing the default appearance */ public void setDefaultAppearance(String daValue) { getCOSObject().setString(COSName.DA, daValue); } /** * Get the default style string. * * The default style string defines the default style for rich text fields. * * @return the DS element of the dictionary object */ public String getDefaultStyleString() { COSString defaultStyleString = (COSString) getCOSObject().getDictionaryObject(COSName.DS); return defaultStyleString.getString(); } /** * Set the default style string. * * Providing null as the value will remove the default style string. * * @param defaultStyleString a string describing the default style. */ public void setDefaultStyleString(String defaultStyleString) { if (defaultStyleString != null) { getCOSObject().setItem(COSName.DS, COSString.parseLiteral(defaultStyleString)); } else { getCOSObject().removeItem(COSName.DS); } } /** * This will get the 'quadding' or justification of the text to be displayed. * * This is an inheritable attribute. * * 0 - Left(default)
* 1 - Centered
* 2 - Right
* Please see the QUADDING_CONSTANTS. * * @return The justification of the text strings. */ public int getQ() { int retval = 0; COSNumber number = (COSNumber) getInheritableAttribute(COSName.Q); if (number != null) { retval = number.intValue(); } return retval; } /** * This will set the quadding/justification of the text. See QUADDING constants. * * @param q The new text justification. */ public void setQ(int q) { getCOSObject().setInt(COSName.Q, q); } /** * Get the fields rich text value. * * @return the rich text value string */ public String getRichTextValue() { return getStringOrStream(getInheritableAttribute(COSName.RV)); } /** * Set the fields rich text value. * *

* Setting the rich text value will not generate the appearance for the field.
* You can set {@link PDAcroForm#setNeedAppearances(Boolean)} to signal a conforming reader to generate the * appearance stream. *

* * Providing null as the value will remove the default style string. * * @param richTextValue a rich text string */ public void setRichTextValue(String richTextValue) { if (richTextValue != null) { getCOSObject().setItem(COSName.RV, COSString.parseLiteral(richTextValue)); } else { getCOSObject().removeItem(COSName.RV); } } /** * Get a text as text stream. * * Some dictionary entries allow either a text or a text stream. * * @param base the potential text or text stream * @return the text stream */ protected final String getStringOrStream(COSBase base) { if (base instanceof COSString) { return ((COSString) base).getString(); } else if (base instanceof COSStream) { return ((COSStream) base).asTextString(); } return ""; } public PDFont getAppearanceFont() { try { if (appearanceOverrideFont != null) { return appearanceOverrideFont; } return getDefaultAppearanceString().getFont(); } catch (IOException ioe) { return null; } } public PDFont getAppearanceOverrideFont() { return appearanceOverrideFont; } public void setAppearanceOverrideFont(PDFont appearanceOverrideFont) { this.appearanceOverrideFont = appearanceOverrideFont; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PDXFAResource.java000066400000000000000000000125111320103431700310110ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.cos.COSStream; import org.sejda.util.IOUtils; import org.w3c.dom.Document; import org.xml.sax.SAXException; /** * An XML Forms Architecture (XFA) resource. * * @author Ben Litchfield */ public final class PDXFAResource implements COSObjectable { /** * The default buffer size */ private static final int BUFFER_SIZE = 1024; private COSBase xfa; /** * Constructor. * * @param xfaBase The xfa resource. */ public PDXFAResource(COSBase xfaBase) { xfa = xfaBase; } @Override public COSBase getCOSObject() { return xfa; } /** * Get the XFA content as byte array. * * The XFA is either a stream containing the entire XFA resource or an array specifying individual packets that * together make up the XFA resource. * * A packet is a pair of a string and stream. The string contains the name of the XML element and the stream * contains the complete text of this XML element. Each packet represents a complete XML element, with the exception * of the first and last packet, which specify begin and end tags for the xdp:xdp element. [IS0 32000-1:2008: * 12.7.8] * * @return the XFA content * @throws IOException */ public byte[] getBytes() throws IOException { InputStream is = null; byte[] xfaBytes = null; try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { // handle the case if the XFA is split into individual parts if (this.getCOSObject() instanceof COSArray) { xfaBytes = new byte[BUFFER_SIZE]; COSArray cosArray = (COSArray) this.getCOSObject(); for (int i = 1; i < cosArray.size(); i += 2) { COSBase cosObj = cosArray.getObject(i); if (cosObj instanceof COSStream) { is = ((COSStream) cosObj).getUnfilteredStream(); int nRead = 0; while ((nRead = is.read(xfaBytes, 0, xfaBytes.length)) != -1) { baos.write(xfaBytes, 0, nRead); } baos.flush(); } } // handle the case if the XFA is represented as a single stream } else if (xfa.getCOSObject() instanceof COSStream) { xfaBytes = new byte[BUFFER_SIZE]; is = ((COSStream) xfa.getCOSObject()).getUnfilteredStream(); int nRead = 0; while ((nRead = is.read(xfaBytes, 0, xfaBytes.length)) != -1) { baos.write(xfaBytes, 0, nRead); } baos.flush(); } return baos.toByteArray(); } finally { IOUtils.close(is); } } /** * Get the XFA content as W3C document. * * @see #getBytes() * * @return the XFA content * * @throws ParserConfigurationException parser exception. * @throws SAXException parser exception. * @throws IOException if something went wrong when reading the XFA content. * */ public Document getDocument() throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); factory.setXIncludeAware(false); factory.setExpandEntityReferences(false); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); return builder.parse(new ByteArrayInputStream(this.getBytes())); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PlainText.java000066400000000000000000000205141320103431700303510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.text.AttributedCharacterIterator.Attribute; import java.text.AttributedString; import java.text.BreakIterator; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.sejda.sambox.pdmodel.font.PDFont; /** * A block of text. *

* A block of text can contain multiple paragraphs which will be treated individually within the block placement. *

* */ class PlainText { private static final float FONTSCALE = 1000f; private final List paragraphs; /** * Construct the text block from a single value. * * Constructs the text block from a single value splitting into individual {@link Paragraph} when a new line * character is encountered. * * @param textValue the text block string. */ PlainText(String textValue) { List parts = Arrays .asList(textValue.replaceAll("\t", " ").split("\\r\\n|\\n|\\r|\\u2028|\\u2029")); paragraphs = new ArrayList<>(); for (String part : parts) { // Acrobat prints a space for an empty paragraph if (part.length() == 0) { part = " "; } paragraphs.add(new Paragraph(part)); } } /** * Construct the text block from a list of values. * * Constructs the text block from a list of values treating each entry as an individual {@link Paragraph}. * * @param listValue the text block string. */ PlainText(List listValue) { paragraphs = new ArrayList(); for (String part : listValue) { paragraphs.add(new Paragraph(part)); } } /** * Get the list of paragraphs. * * @return the paragraphs. */ List getParagraphs() { return paragraphs; } /** * Attribute keys and attribute values used for text handling. * * This is similar to {@link java.awt.font.TextAttribute} but handled individually as to avoid a dependency on awt. * */ static class TextAttribute extends Attribute { /** * UID for serializing. */ private static final long serialVersionUID = -3138885145941283005L; /** * Attribute width of the text. */ public static final Attribute WIDTH = new TextAttribute("width"); protected TextAttribute(String name) { super(name); } } /** * A block of text to be formatted as a whole. *

* A block of text can contain multiple paragraphs which will be treated individually within the block placement. *

* */ static class Paragraph { private String textContent; Paragraph(String text) { textContent = text; } /** * Get the paragraph text. * * @return the text. */ String getText() { return textContent; } /** * Break the paragraph into individual lines. * * @param font the font used for rendering the text. * @param fontSize the fontSize used for rendering the text. * @param width the width of the box holding the content. * @return the individual lines. * @throws IOException */ List getLines(PDFont font, float fontSize, float width) throws IOException { BreakIterator iterator = BreakIterator.getLineInstance(); iterator.setText(textContent); final float scale = fontSize / FONTSCALE; int start = iterator.first(); int end = iterator.next(); float lineWidth = 0; float wordWidth = 0f; float whitespaceWidth = 0f; List textLines = new ArrayList(); Line textLine = new Line(); while (end != BreakIterator.DONE) { whitespaceWidth = 0f; String word = textContent.substring(start, end); wordWidth = font.getStringWidth(word) * scale; lineWidth = lineWidth + wordWidth; // check if the last word would fit without the whitespace ending it if (lineWidth >= width && Character.isWhitespace(word.charAt(word.length() - 1))) { whitespaceWidth = font.getStringWidth(word.substring(word.length() - 1)) * scale; lineWidth = lineWidth - whitespaceWidth; } if (lineWidth >= width) { textLine.setWidth(textLine.calculateWidth(font, fontSize)); textLines.add(textLine); textLine = new Line(); lineWidth = font.getStringWidth(word) * scale; } AttributedString as = new AttributedString(word); as.addAttribute(TextAttribute.WIDTH, wordWidth); Word wordInstance = new Word(word); wordInstance.setAttributes(as); textLine.addWord(wordInstance); start = end; end = iterator.next(); } textLine.setWidth(textLine.calculateWidth(font, fontSize)); textLines.add(textLine); return textLines; } } /** * An individual line of text. */ static class Line { private List words = new ArrayList(); private float lineWidth; float getWidth() { return lineWidth; } void setWidth(float width) { lineWidth = width; } float calculateWidth(PDFont font, float fontSize) throws IOException { final float scale = fontSize / FONTSCALE; float calculatedWidth = 0f; for (Word word : words) { calculatedWidth = calculatedWidth + (Float) word.getAttributes().getIterator() .getAttribute(TextAttribute.WIDTH); String text = word.getText(); if (words.indexOf(word) == words.size() - 1 && Character.isWhitespace(text.charAt(text.length() - 1))) { float whitespaceWidth = font.getStringWidth(text.substring(text.length() - 1)) * scale; calculatedWidth = calculatedWidth - whitespaceWidth; } } return calculatedWidth; } List getWords() { return words; } float getInterWordSpacing(float width) { return (width - lineWidth) / (words.size() - 1); } void addWord(Word word) { words.add(word); } } /** * An individual word. * * A word is defined as a string which must be kept on the same line. */ static class Word { private AttributedString attributedString; private String textContent; Word(String text) { textContent = text; } String getText() { return textContent; } AttributedString getAttributes() { return attributedString; } void setAttributes(AttributedString as) { this.attributedString = as; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/PlainTextFormatter.java000066400000000000000000000212471320103431700322410ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.form; import java.io.IOException; import java.util.List; import org.sejda.sambox.pdmodel.PDPageContentStream; import org.sejda.sambox.pdmodel.interactive.form.PlainText.Line; import org.sejda.sambox.pdmodel.interactive.form.PlainText.Paragraph; import org.sejda.sambox.pdmodel.interactive.form.PlainText.TextAttribute; import org.sejda.sambox.pdmodel.interactive.form.PlainText.Word; /** * TextFormatter to handle plain text formatting. * * The text formatter will take a single value or an array of values which are treated as paragraphs. */ class PlainTextFormatter { enum TextAlign { LEFT(0), CENTER(1), RIGHT(2), JUSTIFY(4); private final int alignment; private TextAlign(int alignment) { this.alignment = alignment; } int getTextAlign() { return alignment; } public static TextAlign valueOf(int alignment) { for (TextAlign textAlignment : TextAlign.values()) { if (textAlignment.getTextAlign() == alignment) { return textAlignment; } } return TextAlign.LEFT; } } /** * The scaling factor for font units to PDF units */ private static final int FONTSCALE = 1000; private final AppearanceStyle appearanceStyle; private final boolean wrapLines; private final float width; private final PDPageContentStream contents; private final PlainText textContent; private final TextAlign textAlignment; private float horizontalOffset; private float verticalOffset; static class Builder { // required parameters private PDPageContentStream contents; // optional parameters private AppearanceStyle appearanceStyle; private boolean wrapLines = false; private float width = 0f; private PlainText textContent; private TextAlign textAlignment = TextAlign.LEFT; // initial offset from where to start the position of the first line private float horizontalOffset = 0f; private float verticalOffset = 0f; Builder(PDPageContentStream contents) { this.contents = contents; } Builder style(AppearanceStyle appearanceStyle) { this.appearanceStyle = appearanceStyle; return this; } Builder wrapLines(boolean wrapLines) { this.wrapLines = wrapLines; return this; } Builder width(float width) { this.width = width; return this; } Builder textAlign(int alignment) { this.textAlignment = TextAlign.valueOf(alignment); return this; } Builder textAlign(TextAlign alignment) { this.textAlignment = alignment; return this; } Builder text(PlainText textContent) { this.textContent = textContent; return this; } Builder initialOffset(float horizontalOffset, float verticalOffset) { this.horizontalOffset = horizontalOffset; this.verticalOffset = verticalOffset; return this; } PlainTextFormatter build() { return new PlainTextFormatter(this); } } private PlainTextFormatter(Builder builder) { appearanceStyle = builder.appearanceStyle; wrapLines = builder.wrapLines; width = builder.width; contents = builder.contents; textContent = builder.textContent; textAlignment = builder.textAlignment; horizontalOffset = builder.horizontalOffset; verticalOffset = builder.verticalOffset; } /** * Format the text block. * * @throws IOException if there is an error writing to the stream. */ public void format() throws IOException { if (textContent != null && !textContent.getParagraphs().isEmpty()) { contents.saveGraphicsState(); contents.setFont(appearanceStyle.getFont(), appearanceStyle.getFontSize()); boolean isFirstParagraph = true; for (Paragraph paragraph : textContent.getParagraphs()) { if (wrapLines) { List lines = paragraph.getLines(appearanceStyle.getFont(), appearanceStyle.getFontSize(), width); processLines(lines, isFirstParagraph); isFirstParagraph = false; } else { float startOffset = 0f; float lineWidth = appearanceStyle.getFont().getStringWidth(paragraph.getText()) * appearanceStyle.getFontSize() / FONTSCALE; if (lineWidth < width) { switch (textAlignment) { case CENTER: startOffset = (width - lineWidth) / 2; break; case RIGHT: startOffset = width - lineWidth; break; case JUSTIFY: default: startOffset = 0f; } } contents.newLineAtOffset(horizontalOffset + startOffset, verticalOffset); contents.showText(paragraph.getText()); } } contents.restoreGraphicsState(); } } /** * Process lines for output. * * Process lines for an individual paragraph and generate the commands for the content stream to show the text. * * @param lines the lines to process. * @throws IOException if there is an error writing to the stream. */ private void processLines(List lines, boolean isFirstParagraph) throws IOException { float wordWidth = 0f; float lastPos = 0f; float startOffset = 0f; float interWordSpacing = 0f; for (Line line : lines) { switch (textAlignment) { case CENTER: startOffset = (width - line.getWidth()) / 2; break; case RIGHT: startOffset = width - line.getWidth(); break; case JUSTIFY: if (lines.indexOf(line) != lines.size() - 1) { interWordSpacing = line.getInterWordSpacing(width); } break; default: startOffset = 0f; } float offset = -lastPos + startOffset + horizontalOffset; if (lines.indexOf(line) == 0 && isFirstParagraph) { contents.newLineAtOffset(offset, verticalOffset); } else { // keep the last position verticalOffset = verticalOffset - appearanceStyle.getLeading(); contents.newLineAtOffset(offset, -appearanceStyle.getLeading()); } lastPos += offset; List words = line.getWords(); for (Word word : words) { contents.showText(word.getText()); wordWidth = (Float) word.getAttributes().getIterator() .getAttribute(TextAttribute.WIDTH); if (words.indexOf(word) != words.size() - 1) { contents.newLineAtOffset(wordWidth + interWordSpacing, 0f); lastPos = lastPos + wordWidth + interWordSpacing; } } } horizontalOffset = horizontalOffset - lastPos; } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/form/package.html000066400000000000000000000017611320103431700300620ustar00rootroot00000000000000 The interactive package contains classes for handling Interactive Forms, also known as "AcroForms". sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/measurement/000077500000000000000000000000001320103431700271565ustar00rootroot00000000000000PDMeasureDictionary.java000066400000000000000000000051241320103431700336170ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/measurement/* * 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. */ package org.sejda.sambox.pdmodel.interactive.measurement; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This class represents a measure dictionary. * */ public class PDMeasureDictionary implements COSObjectable { /** * The type of the dictionary. */ public static final String TYPE = "Measure"; private final COSDictionary measureDictionary; /** * Constructor. */ protected PDMeasureDictionary() { this.measureDictionary = new COSDictionary(); this.getCOSObject().setName(COSName.TYPE, TYPE); } /** * Constructor. * * @param dictionary the corresponding dictionary */ public PDMeasureDictionary(COSDictionary dictionary) { this.measureDictionary = dictionary; } /** * This will return the corresponding dictionary. * * @return the measure dictionary */ @Override public COSDictionary getCOSObject() { return this.measureDictionary; } /** * This will return the type of the measure dictionary. * It must be "Measure" * * @return the type */ public String getType() { return TYPE; } /** * returns the subtype of the measure dictionary. * @return the subtype of the measure data dictionary */ public String getSubtype() { return this.getCOSObject().getNameAsString(COSName.SUBTYPE, PDRectlinearMeasureDictionary.SUBTYPE); } /** * This will set the subtype of the measure dictionary. * @param subtype the subtype of the measure dictionary */ protected void setSubtype(String subtype) { this.getCOSObject().setName(COSName.SUBTYPE, subtype); } } PDNumberFormatDictionary.java000066400000000000000000000224461320103431700346250ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/measurement/* * 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. */ package org.sejda.sambox.pdmodel.interactive.measurement; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This class represents a number format dictionary. * */ public class PDNumberFormatDictionary implements COSObjectable { /** * The type of the dictionary. */ public static final String TYPE = "NumberFormat"; /** * Constant indicating that the label specified by U is a suffix to the value. */ public static final String LABEL_SUFFIX_TO_VALUE = "S"; /** * Constant indicating that the label specified by U is a postfix to the value. */ public static final String LABEL_PREFIX_TO_VALUE = "P"; /** * Constant for showing a fractional value as decimal to the precision specified by the D entry. */ public static final String FRACTIONAL_DISPLAY_DECIMAL = "D"; /** * Constant for showing a fractional value as a fraction with denominator specified by the D entry. */ public static final String FRACTIONAL_DISPLAY_FRACTION = "F"; /** * Constant for showing a fractional value without fractional part; round to the nearest whole unit. */ public static final String FRACTIONAL_DISPLAY_ROUND = "R"; /** * Constant for showing a fractional value without fractional part; truncate to achieve whole units. */ public static final String FRACTIONAL_DISPLAY_TRUNCATE = "T"; private COSDictionary numberFormatDictionary; /** * Constructor. */ public PDNumberFormatDictionary() { this.numberFormatDictionary = new COSDictionary(); this.numberFormatDictionary.setName(COSName.TYPE, TYPE); } /** * Constructor. * * @param dictionary the corresponding dictionary */ public PDNumberFormatDictionary(COSDictionary dictionary) { this.numberFormatDictionary = dictionary; } /** * This will return the dictionary. * * @return the number format dictionary */ @Override public COSDictionary getCOSObject() { return this.numberFormatDictionary; } /** * This will return the type of the number format dictionary. * It must be "NumberFormat" * * @return the type */ public String getType() { return TYPE; } /** * This will return the label for the units. * * @return the label for the units */ public String getUnits() { return this.getCOSObject().getString("U"); } /** * This will set the label for the units. * * @param units the label for the units */ public void setUnits(String units) { this.getCOSObject().setString("U", units); } /** * This will return the conversion factor. * * @return the conversion factor */ public float getConversionFactor() { return this.getCOSObject().getFloat("C"); } /** * This will set the conversion factor. * * @param conversionFactor the conversion factor */ public void setConversionFactor(float conversionFactor) { this.getCOSObject().setFloat("C", conversionFactor); } /** * This will return the value for the manner to display a fractional value. * * @return the manner to display a fractional value */ public String getFractionalDisplay() { return this.getCOSObject().getString("F", FRACTIONAL_DISPLAY_DECIMAL); } /** * This will set the value for the manner to display a fractional value. * Allowed values are "D", "F", "R" and "T" * @param fractionalDisplay the manner to display a fractional value */ public void setFractionalDisplay(String fractionalDisplay) { if ((fractionalDisplay == null) || FRACTIONAL_DISPLAY_DECIMAL.equals(fractionalDisplay) || FRACTIONAL_DISPLAY_FRACTION.equals(fractionalDisplay) || FRACTIONAL_DISPLAY_ROUND.equals(fractionalDisplay) || FRACTIONAL_DISPLAY_TRUNCATE.equals(fractionalDisplay)) { this.getCOSObject().setString("F", fractionalDisplay); } else { throw new IllegalArgumentException("Value must be \"D\", \"F\", \"R\", or \"T\", (or null)."); } } /** * This will return the precision or denominator of a fractional amount. * * @return the precision or denominator */ public int getDenominator() { return this.getCOSObject().getInt("D"); } /** * This will set the precision or denominator of a fractional amount. * * @param denominator the precision or denominator */ public void setDenominator(int denominator) { this.getCOSObject().setInt("D", denominator); } /** * This will return the value indication if the denominator of the fractional value is reduced/truncated . * * @return fd */ public boolean isFD() { return this.getCOSObject().getBoolean("FD", false); } /** * This will set the value indication if the denominator of the fractional value is reduced/truncated . * The denominator may not be reduced/truncated if true * @param fd fd */ public void setFD(boolean fd) { this.getCOSObject().setBoolean("FD", fd); } /** * This will return the text to be used between orders of thousands in display of numerical values. * * @return thousands separator */ public String getThousandsSeparator() { return this.getCOSObject().getString("RT", ","); } /** * This will set the text to be used between orders of thousands in display of numerical values. * * @param thousandsSeparator thousands separator */ public void setThousandsSeparator(String thousandsSeparator) { this.getCOSObject().setString("RT", thousandsSeparator); } /** * This will return the text to be used as the decimal point in displaying numerical values. * * @return decimal separator */ public String getDecimalSeparator() { return this.getCOSObject().getString("RD", "."); } /** * This will set the text to be used as the decimal point in displaying numerical values. * * @param decimalSeparator decimal separator */ public void setDecimalSeparator(String decimalSeparator) { this.getCOSObject().setString("RD", decimalSeparator); } /** * This will return the text to be concatenated to the left of the label specified by U. * @return label prefix */ public String getLabelPrefixString() { return this.getCOSObject().getString("PS", " "); } /** * This will set the text to be concatenated to the left of the label specified by U. * @param labelPrefixString label prefix */ public void setLabelPrefixString(String labelPrefixString) { this.getCOSObject().setString("PS", labelPrefixString); } /** * This will return the text to be concatenated after the label specified by U. * * @return label suffix */ public String getLabelSuffixString() { return this.getCOSObject().getString("SS", " "); } /** * This will set the text to be concatenated after the label specified by U. * * @param labelSuffixString label suffix */ public void setLabelSuffixString(String labelSuffixString) { this.getCOSObject().setString("SS", labelSuffixString); } /** * This will return a value indicating the ordering of the label specified by U to the calculated unit value. * * @return label position */ public String getLabelPositionToValue() { return this.getCOSObject().getString("O", LABEL_SUFFIX_TO_VALUE); } /** * This will set the value indicating the ordering of the label specified by U to the calculated unit value. * Possible values are "S" and "P" * * @param labelPositionToValue label position */ public void setLabelPositionToValue(String labelPositionToValue) { if ((labelPositionToValue == null) || LABEL_PREFIX_TO_VALUE.equals(labelPositionToValue) || LABEL_SUFFIX_TO_VALUE.equals(labelPositionToValue)) { this.getCOSObject().setString("O", labelPositionToValue); } else { throw new IllegalArgumentException("Value must be \"S\", or \"P\" (or null)."); } } } PDRectlinearMeasureDictionary.java000066400000000000000000000217141320103431700356330ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/measurement/* * 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. */ package org.sejda.sambox.pdmodel.interactive.measurement; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; /** * This class represents a rectlinear measure dictionary. * */ public class PDRectlinearMeasureDictionary extends PDMeasureDictionary { /** * The subtype of the rectlinear measure dictionary. */ public static final String SUBTYPE = "RL"; /** * Constructor. */ public PDRectlinearMeasureDictionary() { this.setSubtype(SUBTYPE); } /** * Constructor. * * @param dictionary the corresponding dictionary */ public PDRectlinearMeasureDictionary(COSDictionary dictionary) { super(dictionary); } /** * This will return the scale ration. * * @return the scale ratio. */ public String getScaleRatio() { return this.getCOSObject().getString(COSName.R); } /** * This will set the scale ration. * * @param scaleRatio the scale ratio. */ public void setScaleRatio(String scaleRatio) { this.getCOSObject().setString(COSName.R, scaleRatio); } /** * This will return the changes along the x-axis. * * @return changes along the x-axis */ public PDNumberFormatDictionary[] getChangeXs() { COSArray x = (COSArray)this.getCOSObject().getDictionaryObject("X"); if (x != null) { PDNumberFormatDictionary[] retval = new PDNumberFormatDictionary[x.size()]; for (int i = 0; i < x.size(); i++) { COSDictionary dic = (COSDictionary) x.get(i); retval[i] = new PDNumberFormatDictionary(dic); } return retval; } return null; } /** * This will set the changes along the x-axis. * * @param changeXs changes along the x-axis */ public void setChangeXs(PDNumberFormatDictionary[] changeXs) { COSArray array = new COSArray(); for (PDNumberFormatDictionary changeX : changeXs) { array.add(changeX); } this.getCOSObject().setItem("X", array); } /** * This will return the changes along the y-axis. * * @return changes along the y-axis */ public PDNumberFormatDictionary[] getChangeYs() { COSArray y = (COSArray)this.getCOSObject().getDictionaryObject("Y"); if (y != null) { PDNumberFormatDictionary[] retval = new PDNumberFormatDictionary[y.size()]; for (int i = 0; i < y.size(); i++) { COSDictionary dic = (COSDictionary) y.get(i); retval[i] = new PDNumberFormatDictionary(dic); } return retval; } return null; } /** * This will set the changes along the y-axis. * * @param changeYs changes along the y-axis */ public void setChangeYs(PDNumberFormatDictionary[] changeYs) { COSArray array = new COSArray(); for (PDNumberFormatDictionary changeY : changeYs) { array.add(changeY); } this.getCOSObject().setItem("Y", array); } /** * This will return the distances. * * @return distances */ public PDNumberFormatDictionary[] getDistances() { COSArray d = (COSArray)this.getCOSObject().getDictionaryObject("D"); if (d != null) { PDNumberFormatDictionary[] retval = new PDNumberFormatDictionary[d.size()]; for (int i = 0; i < d.size(); i++) { COSDictionary dic = (COSDictionary) d.get(i); retval[i] = new PDNumberFormatDictionary(dic); } return retval; } return null; } /** * This will set the distances. * * @param distances distances */ public void setDistances(PDNumberFormatDictionary[] distances) { COSArray array = new COSArray(); for (PDNumberFormatDictionary distance : distances) { array.add(distance); } this.getCOSObject().setItem("D", array); } /** * This will return the areas. * * @return areas */ public PDNumberFormatDictionary[] getAreas() { COSArray a = (COSArray)this.getCOSObject().getDictionaryObject(COSName.A); if (a != null) { PDNumberFormatDictionary[] retval = new PDNumberFormatDictionary[a.size()]; for (int i = 0; i < a.size(); i++) { COSDictionary dic = (COSDictionary) a.get(i); retval[i] = new PDNumberFormatDictionary(dic); } return retval; } return null; } /** * This will set the areas. * * @param areas areas */ public void setAreas(PDNumberFormatDictionary[] areas) { COSArray array = new COSArray(); for (PDNumberFormatDictionary area : areas) { array.add(area); } this.getCOSObject().setItem(COSName.A, array); } /** * This will return the angles. * * @return angles */ public PDNumberFormatDictionary[] getAngles() { COSArray t = (COSArray)this.getCOSObject().getDictionaryObject("T"); if (t != null) { PDNumberFormatDictionary[] retval = new PDNumberFormatDictionary[t.size()]; for (int i = 0; i < t.size(); i++) { COSDictionary dic = (COSDictionary) t.get(i); retval[i] = new PDNumberFormatDictionary(dic); } return retval; } return null; } /** * This will set the angles. * * @param angles angles */ public void setAngles(PDNumberFormatDictionary[] angles) { COSArray array = new COSArray(); for (PDNumberFormatDictionary angle : angles) { array.add(angle); } this.getCOSObject().setItem("T", array); } /** * This will return the sloaps of a line. * * @return the sloaps of a line */ public PDNumberFormatDictionary[] getLineSloaps() { COSArray s = (COSArray)this.getCOSObject().getDictionaryObject("S"); if (s != null) { PDNumberFormatDictionary[] retval = new PDNumberFormatDictionary[s.size()]; for (int i = 0; i < s.size(); i++) { COSDictionary dic = (COSDictionary) s.get(i); retval[i] = new PDNumberFormatDictionary(dic); } return retval; } return null; } /** * This will set the sloaps of a line. * * @param lineSloaps the sloaps of a line */ public void setLineSloaps(PDNumberFormatDictionary[] lineSloaps) { COSArray array = new COSArray(); for (PDNumberFormatDictionary lineSloap : lineSloaps) { array.add(lineSloap); } this.getCOSObject().setItem("S", array); } /** * This will return the origin of the coordinate system. * * @return the origin */ public float[] getCoordSystemOrigin() { COSArray o = (COSArray)this.getCOSObject().getDictionaryObject("O"); if (o != null) { return o.toFloatArray(); } return null; } /** * This will set the origin of the coordinate system. * * @param coordSystemOrigin the origin */ public void setCoordSystemOrigin(float[] coordSystemOrigin) { COSArray array = new COSArray(); array.setFloatArray(coordSystemOrigin); this.getCOSObject().setItem("O", array); } /** * This will return the CYX factor. * * @return CYX factor */ public float getCYX() { return this.getCOSObject().getFloat("CYX"); } /** * This will set the CYX factor. * * @param cyx CYX factor */ public void setCYX(float cyx) { this.getCOSObject().setFloat("CYX", cyx); } } PDViewportDictionary.java000066400000000000000000000074171320103431700340440ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/measurement/* * 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. */ package org.sejda.sambox.pdmodel.interactive.measurement; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.common.PDRectangle; /** * This class represents a viewport dictionary. * */ public class PDViewportDictionary implements COSObjectable { /** * The type of this annotation. */ public static final String TYPE = "Viewport"; private COSDictionary viewportDictionary; /** * Constructor. */ public PDViewportDictionary() { this.viewportDictionary = new COSDictionary(); } /** * Constructor. * * @param dictionary the dictionary */ public PDViewportDictionary(COSDictionary dictionary) { this.viewportDictionary = dictionary; } /** * This will return the corresponding dictionary. * * @return the viewport dictionary */ @Override public COSDictionary getCOSObject() { return this.viewportDictionary; } /** * Returns the type of the viewport dictionary. * It must be "Viewport" * @return the type of the external data dictionary */ public String getType() { return TYPE; } /** * This will retrieve the rectangle specifying the location of the viewport. * * @return the location */ public PDRectangle getBBox() { COSArray bbox = (COSArray)this.getCOSObject().getDictionaryObject("BBox"); if (bbox != null) { return new PDRectangle(bbox); } return null; } /** * This will set the rectangle specifying the location of the viewport. * * @param rectangle the rectangle specifying the location. */ public void setBBox(PDRectangle rectangle) { this.getCOSObject().setItem("BBox", rectangle); } /** * This will retrieve the name of the viewport. * * @return the name of the viewport */ public String getName() { return this.getCOSObject().getNameAsString(COSName.NAME); } /** * This will set the name of the viewport. * * @param name the name of the viewport */ public void setName(String name) { this.getCOSObject().setName(COSName.NAME, name); } /** * This will retrieve the measure dictionary. * * @return the measure dictionary */ public PDMeasureDictionary getMeasure() { COSDictionary measure = (COSDictionary)this.getCOSObject().getDictionaryObject("Measure"); if (measure != null) { return new PDMeasureDictionary(measure); } return null; } /** * This will set the measure dictionary. * * @param measure the measure dictionary */ public void setMeasure(PDMeasureDictionary measure) { this.getCOSObject().setItem("Measure", measure); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/measurement/package.html000066400000000000000000000017551320103431700314470ustar00rootroot00000000000000 The measurement package contains classes that work with elements specifying measure properties. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/000077500000000000000000000000001320103431700276255ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/PDThread.java000066400000000000000000000063171320103431700321320ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.pagenavigation; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.PDDocumentInformation; /** * This a single thread in a PDF document. * * @author Ben Litchfield */ public class PDThread implements COSObjectable { private COSDictionary thread; /** * Constructor that is used for a preexisting dictionary. * * @param t The underlying dictionary. */ public PDThread(COSDictionary t) { thread = t; } /** * Default constructor. * */ public PDThread() { thread = new COSDictionary(); thread.setName("Type", "Thread"); } /** * This will get the underlying dictionary that this object wraps. * * @return The underlying info dictionary. */ @Override public COSDictionary getCOSObject() { return thread; } /** * Get info about the thread, or null if there is nothing. * * @return The thread information. */ public PDDocumentInformation getThreadInfo() { PDDocumentInformation retval = null; COSDictionary info = (COSDictionary) thread.getDictionaryObject("I"); if (info != null) { retval = new PDDocumentInformation(info); } return retval; } /** * Set the thread info, can be null. * * @param info The info dictionary about this thread. */ public void setThreadInfo(PDDocumentInformation info) { thread.setItem("I", info); } /** * Get the first bead in the thread, or null if it has not been set yet. This is a required field for this object. * * @return The first bead in the thread. */ public PDThreadBead getFirstBead() { PDThreadBead retval = null; COSDictionary bead = (COSDictionary) thread.getDictionaryObject("F"); if (bead != null) { retval = new PDThreadBead(bead); } return retval; } /** * This will set the first bead in the thread. When this is set it will also set the thread property of the bead * object. * * @param bead The first bead in the thread. */ public void setFirstBead(PDThreadBead bead) { if (bead != null) { bead.setThread(this); } thread.setItem("F", bead); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/PDThreadBead.java000066400000000000000000000134251320103431700327040ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.pagenavigation; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.common.PDRectangle; /** * This a single bead in a thread in a PDF document. * * @author Ben Litchfield */ public class PDThreadBead implements COSObjectable { private final COSDictionary bead; /** * Constructor that is used for a preexisting dictionary. * * @param b The underlying dictionary. */ public PDThreadBead( COSDictionary b ) { bead = b; } /** * Default constructor. * */ public PDThreadBead() { bead = new COSDictionary(); bead.setName( "Type", "Bead" ); setNextBead( this ); setPreviousBead( this ); } /** * This will get the underlying dictionary that this object wraps. * * @return The underlying info dictionary. */ @Override public COSDictionary getCOSObject() { return bead; } /** * This will get the thread that this bead is part of. This is only required * for the first bead in a thread, so other beads 'may' return null. * * @return The thread that this bead is part of. */ public PDThread getThread() { PDThread retval = null; COSDictionary dic = (COSDictionary)bead.getDictionaryObject( "T" ); if( dic != null ) { retval = new PDThread( dic ); } return retval; } /** * Set the thread that this bead is part of. This is only required for the * first bead in a thread. Note: This property is set for you by the PDThread.setFirstBead() method. * * @param thread The thread that this bead is part of. */ public void setThread( PDThread thread ) { bead.setItem( "T", thread ); } /** * This will get the next bead. If this bead is the last bead in the list then this * will return the first bead. * * @return The next bead in the list or the first bead if this is the last bead. */ public PDThreadBead getNextBead() { return new PDThreadBead( (COSDictionary) bead.getDictionaryObject( "N" ) ); } /** * Set the next bead in the thread. * * @param next The next bead. */ protected final void setNextBead( PDThreadBead next ) { bead.setItem( "N", next ); } /** * This will get the previous bead. If this bead is the first bead in the list then this * will return the last bead. * * @return The previous bead in the list or the last bead if this is the first bead. */ public PDThreadBead getPreviousBead() { return new PDThreadBead( (COSDictionary) bead.getDictionaryObject( "V" ) ); } /** * Set the previous bead in the thread. * * @param previous The previous bead. */ protected final void setPreviousBead( PDThreadBead previous ) { bead.setItem( "V", previous ); } /** * Append a bead after this bead. This will correctly set the next/previous beads in the * linked list. * * @param append The bead to insert. */ public void appendBead( PDThreadBead append ) { PDThreadBead nextBead = getNextBead(); nextBead.setPreviousBead( append ); append.setNextBead( nextBead ); setNextBead( append ); append.setPreviousBead( this ); } /** * Get the page that this bead is part of. * * @return The page that this bead is part of. */ public PDPage getPage() { PDPage page = null; COSDictionary dic = (COSDictionary)bead.getDictionaryObject( "P" ); if( dic != null ) { page = new PDPage( dic ); } return page; } /** * Set the page that this bead is part of. This is a required property and must be * set when creating a new bead. The PDPage object also has a list of beads in the natural * reading order. It is recommended that you add this object to that list as well. * * @param page The page that this bead is on. */ public void setPage( PDPage page ) { bead.setItem( "P", page ); } /** * The rectangle on the page that this bead is part of. * * @return The part of the page that this bead covers. */ public PDRectangle getRectangle() { PDRectangle rect = null; COSArray array = (COSArray)bead.getDictionaryObject( COSName.R ); if( array != null ) { rect = new PDRectangle( array ); } return rect; } /** * Set the rectangle on the page that this bead covers. * * @param rect The portion of the page that this bead covers. */ public void setRectangle( PDRectangle rect ) { bead.setItem( COSName.R, rect ); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/PDTransition.java000066400000000000000000000136341320103431700330550ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.pdmodel.interactive.pagenavigation; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSBoolean; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; /** * Represents a page transition as defined in paragraph 12.4.4.1 of PDF 32000-1:2008 * * @author Andrea Vacondio * */ public final class PDTransition extends PDDictionaryWrapper { /** * creates a new transition with default "replace" style {@link PDTransitionStyle#R} */ public PDTransition() { this(PDTransitionStyle.R); } /** * creates a new transition with the given style. * * @param style */ public PDTransition(PDTransitionStyle style) { super(); getCOSObject().setName(COSName.TYPE, COSName.TRANS.getName()); getCOSObject().setName(COSName.S, style.name()); } /** * creates a new transition for an existing dictionary * * @param dictionary */ public PDTransition(COSDictionary dictionary) { super(dictionary); } /** * @return the style for this transition * @see PDTransitionStyle#valueOf(String) */ public String getStyle() { return getCOSObject().getNameAsString(COSName.S, PDTransitionStyle.R.name()); } /** * @return The dimension in which the specified transition effect shall occur or the default * {@link PDTransitionDimension#H} if no dimension is found. * @see PDTransitionDimension */ public String getDimension() { return getCOSObject().getNameAsString(COSName.DM, PDTransitionDimension.H.name()); } /** * Sets the dimension in which the specified transition effect shall occur. Only for {@link PDTransitionStyle#Split} * and {@link PDTransitionStyle#Blinds}. */ public void setDimension(PDTransitionDimension dimension) { getCOSObject().setName(COSName.DM, dimension.name()); } /** * @return The direction of motion for the specified transition effect or the default {@link PDTransitionMotion#I} * if no motion is found. * @see PDTransitionMotion */ public String getMotion() { return getCOSObject().getNameAsString(COSName.M, PDTransitionMotion.I.name()); } /** * Sets the direction of motion for the specified transition effect. Only for {@link PDTransitionStyle#Split}, * {@link PDTransitionStyle#Blinds} and {@link PDTransitionStyle#Fly}. */ public void setMotion(PDTransitionMotion motion) { getCOSObject().setName(COSName.M, motion.name()); } /** * @return the direction in which the specified transition effect shall moves. It can be either a {@link COSInteger} * or {@link COSName#NONE}. Default to {@link COSInteger#ZERO} * @see PDTransitionDirection */ public COSBase getDirection() { COSBase item = getCOSObject().getItem(COSName.DI); if (item == null) { return COSInteger.ZERO; } return item; } /** * Sets the direction in which the specified transition effect shall moves. Only for {@link PDTransitionStyle#Wipe}, * {@link PDTransitionStyle#Glitter}, {@link PDTransitionStyle#Fly}, {@link PDTransitionStyle#Cover}, * {@link PDTransitionStyle#Uncover} and {@link PDTransitionStyle#Push}. */ public void setDirection(PDTransitionDirection direction) { getCOSObject().setItem(COSName.DI, direction.getCOSBase()); } /** * @return The duration in seconds of the transition effect or the default 1 if no duration is found. */ public float getDuration() { return getCOSObject().getFloat(COSName.D, 1); } /** * @param duration The duration of the transition effect, in seconds. */ public void setDuration(float duration) { getCOSObject().setItem(COSName.D, new COSFloat(duration)); } /** * @return The starting or ending scale at which the changes shall be drawn or the default 1 if no scale is found. * Only for {@link PDTransitionStyle#Fly}. */ public float getFlyScale() { return getCOSObject().getFloat(COSName.SS, 1); } /** * @param scale The starting or ending scale at which the changes shall be drawn. Only for * {@link PDTransitionStyle#Fly}. */ public void setFlyScale(float scale) { getCOSObject().setItem(COSName.SS, new COSFloat(scale)); } /** * @return true if the area that shall be flown in is rectangular and opaque. Default is false. Only for * {@link PDTransitionStyle#Fly}. */ public boolean isFlyAreaOpaque() { return getCOSObject().getBoolean(COSName.B, false); } /** * @param opaque If true, the area that shall be flown in is rectangular and opaque. Only for * {@link PDTransitionStyle#Fly}. */ public void setFlyAreaOpaque(boolean opaque) { getCOSObject().setItem(COSName.B, COSBoolean.valueOf(opaque)); } } PDTransitionDimension.java000066400000000000000000000022331320103431700346350ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.pagenavigation; /** * The dimension in which the specified transition effect shall occur. Only for {@link PDTransitionStyle#Split} and * {@link PDTransitionStyle#Blinds}. * * @author Andrea Vacondio * */ public enum PDTransitionDimension { /** * Horizontal */ H, /** * Vertical */ V; } PDTransitionDirection.java000066400000000000000000000042701320103431700346330ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.pagenavigation; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSInteger; import org.sejda.sambox.cos.COSName; /** * The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting * from a left-to-right direction. Only for {@link PDTransitionStyle#Wipe}, {@link PDTransitionStyle#Glitter}, * {@link PDTransitionStyle#Fly}, {@link PDTransitionStyle#Cover}, {@link PDTransitionStyle#Uncover} and * {@link PDTransitionStyle#Push}. * * @author Andrea Vacondio * */ public enum PDTransitionDirection { LEFT_TO_RIGHT(0), /** * Relevant only for the Wipe transition */ BOTTOM_TO_TOP(90), /** * Relevant only for the Wipe transition */ RIGHT_TO_LEFT(180), TOP_TO_BOTTOM(270), /** * Relevant only for the Glitter transition */ TOP_LEFT_TO_BOTTOM_RIGHT(315), /** * Relevant only for the Fly transition when the value of SS is not 1.0 */ NONE(0) { @Override public COSBase getCOSBase() { return COSName.NONE; } }; private final int degrees; private PDTransitionDirection(int degrees) { this.degrees = degrees; } /** * @return the value for this direction */ public COSBase getCOSBase() { return COSInteger.get(degrees); } } PDTransitionMotion.java000066400000000000000000000023411320103431700341550ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.pagenavigation; /** * The direction of motion for the specified transition effect. Only for {@link PDTransitionStyle#Split}, * {@link PDTransitionStyle#Blinds} and {@link PDTransitionStyle#Fly}. * * @author Andrea Vacondio * */ public enum PDTransitionMotion { /** * Inward from the edges of the page */ I, /** * Outward from the center of the page */ O; } PDTransitionStyle.java000066400000000000000000000022131320103431700340060ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/* * 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. */ package org.sejda.sambox.pdmodel.interactive.pagenavigation; /** * The transition style that shall be used when moving to the page from another during a presentation. Ref. table 162 * PDF32000-1:2008 * * @author Andrea Vacondio * */ public enum PDTransitionStyle { Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade; } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/pagenavigation/package.html000066400000000000000000000017251320103431700321130ustar00rootroot00000000000000 A package to allow provide access to PDF page navigation functionality. sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/viewerpreferences/000077500000000000000000000000001320103431700303545ustar00rootroot00000000000000PDViewerPreferences.java000066400000000000000000000340261320103431700350140ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/viewerpreferences/* * 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. */ package org.sejda.sambox.pdmodel.interactive.viewerpreferences; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSObjectable; /** * This is the document viewing preferences. * * @author Ben Litchfield */ public class PDViewerPreferences implements COSObjectable { /** * From PDF Reference: "Neither document outline nor thumbnail images visible". * * @deprecated use {@link NON_FULL_SCREEN_PAGE_MODE} instead */ public static final String NON_FULL_SCREEN_PAGE_MODE_USE_NONE = "UseNone"; /** * From PDF Reference: "Document outline visible". * * @deprecated use {@link NON_FULL_SCREEN_PAGE_MODE} instead */ public static final String NON_FULL_SCREEN_PAGE_MODE_USE_OUTLINES = "UseOutlines"; /** * From PDF Reference: "Thumbnail images visible". * * @deprecated use {@link NON_FULL_SCREEN_PAGE_MODE} instead */ public static final String NON_FULL_SCREEN_PAGE_MODE_USE_THUMBS = "UseThumbs"; /** * From PDF Reference: "Optional content group panel visible". * * @deprecated use {@link NON_FULL_SCREEN_PAGE_MODE} instead */ public static final String NON_FULL_SCREEN_PAGE_MODE_USE_OPTIONAL_CONTENT = "UseOC"; /** * Enumeration containing all valid values for NonFullScreenPageMode. */ public enum NON_FULL_SCREEN_PAGE_MODE { /** * From PDF Reference: "Neither document outline nor thumbnail images visible". */ UseNone, /** * From PDF Reference: "Document outline visible". */ UseOutlines, /** * From PDF Reference: "Thumbnail images visible". */ UseThumbs, /** * From PDF Reference: "Optional content group panel visible". */ UseOC } /** * Reading direction. * * @deprecated use {@link READING_DIRECTION} instead */ public static final String READING_DIRECTION_L2R = "L2R"; /** * Reading direction. * * @deprecated use {@link READING_DIRECTION} instead */ public static final String READING_DIRECTION_R2L = "R2L"; /** * Enumeration containing all valid values for ReadingDirection. */ public enum READING_DIRECTION { /** * left to right. */ L2R, /** * right to left. */ R2L } /** * Boundary constant. * * @deprecated use {@link BOUNDARY} instead */ public static final String BOUNDARY_MEDIA_BOX = "MediaBox"; /** * Boundary constant. * * @deprecated use {@link BOUNDARY} instead */ public static final String BOUNDARY_CROP_BOX = "CropBox"; /** * Boundary constant. * * @deprecated use {@link BOUNDARY} instead */ public static final String BOUNDARY_BLEED_BOX = "BleedBox"; /** * Boundary constant. * * @deprecated use {@link BOUNDARY} instead */ public static final String BOUNDARY_TRIM_BOX = "TrimBox"; /** * Boundary constant. * * @deprecated use {@link BOUNDARY} instead */ public static final String BOUNDARY_ART_BOX = "ArtBox"; /** * Enumeration containing all valid values for boundaries. */ public enum BOUNDARY { /** * use media box as boundary. */ MediaBox, /** * use crop box as boundary. */ CropBox, /** * use bleed box as boundary. */ BleedBox, /** * use trim box as boundary. */ TrimBox, /** * use art box as boundary. */ ArtBox } /** * Enumeration containing all valid values for duplex. */ public enum DUPLEX { /** * simplex printing. */ Simplex, /** * duplex printing, flip at short edge. */ DuplexFlipShortEdge, /** * duplex printing, flip at long edge. */ DuplexFlipLongEdge } /** * Enumeration containing all valid values for printscaling. */ public enum PRINT_SCALING { /** * no scaling. */ None, /** * use app default. */ AppDefault } private final COSDictionary prefs; /** * Constructor that is used for a preexisting dictionary. * * @param dic The underlying dictionary. */ public PDViewerPreferences( COSDictionary dic ) { prefs = dic; } /** * This will get the underlying dictionary that this object wraps. * * @return The underlying info dictionary. */ @Override public COSDictionary getCOSObject() { return prefs; } /** * Get the toolbar preference. * * @return the toolbar preference. */ public boolean hideToolbar() { return prefs.getBoolean( COSName.HIDE_TOOLBAR, false ); } /** * Set the toolbar preference. * * @param value Set the toolbar preference. */ public void setHideToolbar( boolean value ) { prefs.setBoolean( COSName.HIDE_TOOLBAR, value ); } /** * Get the menubar preference. * * @return the menubar preference. */ public boolean hideMenubar() { return prefs.getBoolean( COSName.HIDE_MENUBAR, false ); } /** * Set the menubar preference. * * @param value Set the menubar preference. */ public void setHideMenubar( boolean value ) { prefs.setBoolean( COSName.HIDE_MENUBAR, value ); } /** * Get the window UI preference. * * @return the window UI preference. */ public boolean hideWindowUI() { return prefs.getBoolean( COSName.HIDE_WINDOWUI, false ); } /** * Set the window UI preference. * * @param value Set the window UI preference. */ public void setHideWindowUI( boolean value ) { prefs.setBoolean( COSName.HIDE_WINDOWUI, value ); } /** * Get the fit window preference. * * @return the fit window preference. */ public boolean fitWindow() { return prefs.getBoolean( COSName.FIT_WINDOW, false ); } /** * Set the fit window preference. * * @param value Set the fit window preference. */ public void setFitWindow( boolean value ) { prefs.setBoolean( COSName.FIT_WINDOW, value ); } /** * Get the center window preference. * * @return the center window preference. */ public boolean centerWindow() { return prefs.getBoolean( COSName.CENTER_WINDOW, false ); } /** * Set the center window preference. * * @param value Set the center window preference. */ public void setCenterWindow( boolean value ) { prefs.setBoolean( COSName.CENTER_WINDOW, value ); } /** * Get the display doc title preference. * * @return the display doc title preference. */ public boolean displayDocTitle() { return prefs.getBoolean( COSName.DISPLAY_DOC_TITLE, false ); } /** * Set the display doc title preference. * * @param value Set the display doc title preference. */ public void setDisplayDocTitle( boolean value ) { prefs.setBoolean( COSName.DISPLAY_DOC_TITLE, value ); } /** * Get the non full screen page mode preference. * * @return the non full screen page mode preference. */ public String getNonFullScreenPageMode() { return prefs.getNameAsString( COSName.NON_FULL_SCREEN_PAGE_MODE, NON_FULL_SCREEN_PAGE_MODE.UseNone.toString()); } /** * Set the non full screen page mode preference. * * @param value Set the non full screen page mode preference. */ public void setNonFullScreenPageMode( NON_FULL_SCREEN_PAGE_MODE value ) { prefs.setName( COSName.NON_FULL_SCREEN_PAGE_MODE, value.toString() ); } /** * Set the non full screen page mode preference. * * @param value Set the non full screen page mode preference. * * @deprecated */ public void setNonFullScreenPageMode( String value ) { prefs.setName( COSName.NON_FULL_SCREEN_PAGE_MODE, value ); } /** * Get the reading direction preference. * * @return the reading direction preference. */ public String getReadingDirection() { return prefs.getNameAsString( COSName.DIRECTION, READING_DIRECTION.L2R.toString()); } /** * Set the reading direction preference. * * @param value Set the reading direction preference. */ public void setReadingDirection( READING_DIRECTION value ) { prefs.setName( COSName.DIRECTION, value.toString() ); } /** * Set the reading direction preference. * * @param value Set the reading direction preference. * * @deprecated */ public void setReadingDirection( String value ) { prefs.setName( COSName.DIRECTION, value); } /** * Get the ViewArea preference. See BOUNDARY enumeration. * * @return the ViewArea preference. */ public String getViewArea() { return prefs.getNameAsString( COSName.VIEW_AREA, BOUNDARY.CropBox.toString()); } /** * Set the ViewArea preference. See BOUNDARY_XXX constants. * * @param value Set the ViewArea preference. * * @deprecated */ public void setViewArea( String value ) { prefs.setName( COSName.VIEW_AREA, value ); } /** * Set the ViewArea preference. See BOUNDARY enumeration. * * @param value Set the ViewArea preference. */ public void setViewArea( BOUNDARY value ) { prefs.setName( COSName.VIEW_AREA, value.toString() ); } /** * Get the ViewClip preference. See BOUNDARY enumeration. * * @return the ViewClip preference. */ public String getViewClip() { return prefs.getNameAsString( COSName.VIEW_CLIP, BOUNDARY.CropBox.toString()); } /** * Set the ViewClip preference. See BOUNDARY enumeration. * * @param value Set the ViewClip preference. */ public void setViewClip( BOUNDARY value ) { prefs.setName( COSName.VIEW_CLIP, value.toString() ); } /** * Set the ViewClip preference. See BOUNDARY_XXX constants. * * @param value Set the ViewClip preference. * * @deprecated */ public void setViewClip( String value ) { prefs.setName( COSName.VIEW_CLIP, value ); } /** * Get the PrintArea preference. See BOUNDARY enumeration. * * @return the PrintArea preference. */ public String getPrintArea() { return prefs.getNameAsString( COSName.PRINT_AREA, BOUNDARY.CropBox.toString()); } /** * Set the PrintArea preference. See BOUNDARY_XXX constants. * * @param value Set the PrintArea preference. * * @deprecated */ public void setPrintArea( String value ) { prefs.setName( COSName.PRINT_AREA, value ); } /** * Set the PrintArea preference. See BOUNDARY enumeration. * * @param value Set the PrintArea preference. */ public void setPrintArea( BOUNDARY value ) { prefs.setName( COSName.PRINT_AREA, value.toString() ); } /** * Get the PrintClip preference. See BOUNDARY enumeration. * * @return the PrintClip preference. */ public String getPrintClip() { return prefs.getNameAsString( COSName.PRINT_CLIP, BOUNDARY.CropBox.toString()); } /** * Set the PrintClip preference. See BOUNDARY_XXX constants. * * @param value Set the PrintClip preference. * * @deprecated */ public void setPrintClip( String value ) { prefs.setName( COSName.PRINT_CLIP, value ); } /** * Set the PrintClip preference. See BOUNDARY enumeration. * * @param value Set the PrintClip preference. */ public void setPrintClip( BOUNDARY value ) { prefs.setName( COSName.PRINT_CLIP, value.toString() ); } /** * Get the Duplex preference. See DUPLEX enumeration. * * @return the Duplex preference. */ public String getDuplex() { return prefs.getNameAsString( COSName.DUPLEX ); } /** * Set the Duplex preference. See DUPLEX enumeration. * * @param value Set the Duplex preference. */ public void setDuplex( DUPLEX value ) { prefs.setName( COSName.DUPLEX, value.toString() ); } /** * Get the PrintScaling preference. See PRINT_SCALING enumeration. * * @return the PrintScaling preference. */ public String getPrintScaling() { return prefs.getNameAsString( COSName.PRINT_SCALING , PRINT_SCALING.AppDefault.toString()); } /** * Set the PrintScaling preference. See PRINT_SCALING enumeration. * * @param value Set the PrintScaling preference. */ public void setPrintScaling( PRINT_SCALING value ) { prefs.setName( COSName.PRINT_SCALING, value.toString() ); } } sambox-1.1.19/src/main/java/org/sejda/sambox/pdmodel/interactive/viewerpreferences/package.html000066400000000000000000000017101320103431700326340ustar00rootroot00000000000000 A package to allow access to document viewing preferences. sambox-1.1.19/src/main/java/org/sejda/sambox/printing/000077500000000000000000000000001320103431700225225ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/printing/Orientation.java000066400000000000000000000021701320103431700256600ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.printing; /** * Orientation of printed pages. * * @author John Hewson */ public enum Orientation { /** Automatically select the orientation of each page based on its aspect ratio. */ AUTO, /** Print all pages as landscape. */ LANDSCAPE, /** Print all pages as portrait. */ PORTRAIT } sambox-1.1.19/src/main/java/org/sejda/sambox/printing/PDFPageable.java000066400000000000000000000133651320103431700254270ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.printing; import java.awt.print.Book; import java.awt.print.PageFormat; import java.awt.print.Paper; import java.awt.print.Printable; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.common.PDRectangle; /** * Prints a PDF document using its original paper size. * * @author John Hewson */ public final class PDFPageable extends Book { private final PDDocument document; private final boolean showPageBorder; private final float dpi; private final Orientation orientation; /** * Creates a new PDFPageable. * * @param document the document to print */ public PDFPageable(PDDocument document) { this(document, Orientation.AUTO, false, 0); } /** * Creates a new PDFPageable with the given page orientation. * * @param document the document to print * @param orientation page orientation policy */ public PDFPageable(PDDocument document, Orientation orientation) { this(document, orientation, false, 0); } /** * Creates a new PDFPageable with the given page orientation and with optional page borders * shown. The image will be rasterized at the given DPI before being sent to the printer. * * @param document the document to print * @param orientation page orientation policy * @param showPageBorder true if page borders are to be printed */ public PDFPageable(PDDocument document, Orientation orientation, boolean showPageBorder) { this(document, orientation, showPageBorder, 0); } /** * Creates a new PDFPageable with the given page orientation and with optional page borders * shown. The image will be rasterized at the given DPI before being sent to the printer. * * @param document the document to print * @param orientation page orientation policy * @param showPageBorder true if page borders are to be printed * @param dpi if non-zero then the image will be rasterized at the given DPI */ public PDFPageable(PDDocument document, Orientation orientation, boolean showPageBorder, float dpi) { this.document = document; this.orientation = orientation; this.showPageBorder = showPageBorder; this.dpi = dpi; } @Override public int getNumberOfPages() { return document.getNumberOfPages(); } /** * {@inheritDoc} * * Returns the actual physical size of the pages in the PDF file. May not fit the local printer. */ @Override public PageFormat getPageFormat(int pageIndex) { PDPage page = document.getPage(pageIndex); PDRectangle mediaBox = PDFPrintable.getRotatedMediaBox(page); PDRectangle cropBox = PDFPrintable.getRotatedCropBox(page); // Java does not seem to understand landscape paper sizes, i.e. where width > height, it // always crops the imageable area as if the page were in portrait. I suspect that this is // a JDK bug but it might be by design, see PDFBOX-2922. // // As a workaround, we normalise all Page(s) to be portrait, then flag them as landscape in // the PageFormat. Paper paper; boolean isLandscape; if (mediaBox.getWidth() > mediaBox.getHeight()) { // rotate paper = new Paper(); paper.setSize(mediaBox.getHeight(), mediaBox.getWidth()); paper.setImageableArea(cropBox.getLowerLeftY(), cropBox.getLowerLeftX(), cropBox.getHeight(), cropBox.getWidth()); isLandscape = true; } else { paper = new Paper(); paper.setSize(mediaBox.getWidth(), mediaBox.getHeight()); paper.setImageableArea(cropBox.getLowerLeftX(), cropBox.getLowerLeftY(), cropBox.getWidth(), cropBox.getHeight()); isLandscape = false; } PageFormat format = new PageFormat(); format.setPaper(paper); // auto portrait/landscape if (orientation == Orientation.AUTO) { if (isLandscape) { format.setOrientation(PageFormat.LANDSCAPE); } else { format.setOrientation(PageFormat.PORTRAIT); } } else if (orientation == Orientation.LANDSCAPE) { format.setOrientation(PageFormat.LANDSCAPE); } else if (orientation == Orientation.PORTRAIT) { format.setOrientation(PageFormat.PORTRAIT); } return format; } @Override public Printable getPrintable(int i) { if (i >= getNumberOfPages()) { throw new IndexOutOfBoundsException(i + " >= " + getNumberOfPages()); } return new PDFPrintable(document, Scaling.ACTUAL_SIZE, showPageBorder, dpi); } } sambox-1.1.19/src/main/java/org/sejda/sambox/printing/PDFPrintable.java000066400000000000000000000217211320103431700256420ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.printing; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterIOException; import java.io.IOException; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.rendering.PDFRenderer; /** * Prints pages from a PDF document using any page size or scaling mode. * * @author John Hewson */ public final class PDFPrintable implements Printable { private final PDDocument document; private final PDFRenderer renderer; private final boolean showPageBorder; private final Scaling scaling; private final float dpi; private final boolean center; /** * Creates a new PDFPrintable. * * @param document the document to print */ public PDFPrintable(PDDocument document) { this(document, Scaling.SHRINK_TO_FIT); } /** * Creates a new PDFPrintable with the given page scaling. * * @param document the document to print * @param scaling page scaling policy */ public PDFPrintable(PDDocument document, Scaling scaling) { this(document, scaling, false, 0); } /** * Creates a new PDFPrintable with the given page scaling and with optional page borders shown. * * @param document the document to print * @param scaling page scaling policy * @param showPageBorder true if page borders are to be printed */ public PDFPrintable(PDDocument document, Scaling scaling, boolean showPageBorder) { this(document, scaling, showPageBorder, 0); } /** * Creates a new PDFPrintable with the given page scaling and with optional page borders shown. The image will be * rasterized at the given DPI before being sent to the printer. * * @param document the document to print * @param scaling page scaling policy * @param showPageBorder true if page borders are to be printed * @param dpi if non-zero then the image will be rasterized at the given DPI */ public PDFPrintable(PDDocument document, Scaling scaling, boolean showPageBorder, float dpi) { this(document, scaling, showPageBorder, dpi, true); } /** * Creates a new PDFPrintable with the given page scaling and with optional page borders shown. The image will be * rasterized at the given DPI before being sent to the printer. * * @param document the document to print * @param scaling page scaling policy * @param showPageBorder true if page borders are to be printed * @param dpi if non-zero then the image will be rasterized at the given DPI * @param center true if the content is to be centered on the page (otherwise top-left). */ public PDFPrintable(PDDocument document, Scaling scaling, boolean showPageBorder, float dpi, boolean center) { this.document = document; this.renderer = new PDFRenderer(document); this.scaling = scaling; this.showPageBorder = showPageBorder; this.dpi = dpi; this.center = center; } @Override public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { if (pageIndex < 0 || pageIndex >= document.getNumberOfPages()) { return NO_SUCH_PAGE; } try { Graphics2D graphics2D = (Graphics2D) graphics; PDPage page = document.getPage(pageIndex); PDRectangle cropBox = getRotatedCropBox(page); // the imageable area is the area within the page margins final double imageableWidth = pageFormat.getImageableWidth(); final double imageableHeight = pageFormat.getImageableHeight(); double scale = 1; if (scaling != Scaling.ACTUAL_SIZE) { // scale to fit double scaleX = imageableWidth / cropBox.getWidth(); double scaleY = imageableHeight / cropBox.getHeight(); scale = Math.min(scaleX, scaleY); // only shrink to fit when enabled if (scale > 1 && scaling == Scaling.SHRINK_TO_FIT) { scale = 1; } // only stretch to fit when enabled if (scale < 1 && scaling == Scaling.STRETCH_TO_FIT) { scale = 1; } } // set the graphics origin to the origin of the imageable area (i.e the margins) graphics2D.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); // center on page if (center) { graphics2D.translate((imageableWidth - cropBox.getWidth() * scale) / 2, (imageableHeight - cropBox.getHeight() * scale) / 2); } // rasterize to bitmap (optional) Graphics2D printerGraphics = null; BufferedImage image = null; if (dpi > 0) { float dpiScale = dpi / 72; image = new BufferedImage((int) (imageableWidth * dpiScale / scale), (int) (imageableHeight * dpiScale / scale), BufferedImage.TYPE_INT_ARGB); printerGraphics = graphics2D; graphics2D = image.createGraphics(); // rescale printerGraphics.scale(scale / dpiScale, scale / dpiScale); scale = dpiScale; } // draw to graphics using PDFRender AffineTransform transform = (AffineTransform) graphics2D.getTransform().clone(); graphics2D.setBackground(Color.WHITE); renderer.renderPageToGraphics(pageIndex, graphics2D, (float) scale); // draw crop box if (showPageBorder) { graphics2D.setTransform(transform); graphics2D.setClip(0, 0, (int) imageableWidth, (int) imageableHeight); graphics2D.scale(scale, scale); graphics2D.setColor(Color.GRAY); graphics2D.setStroke(new BasicStroke(0.5f)); graphics.drawRect(0, 0, (int) cropBox.getWidth(), (int) cropBox.getHeight()); } // draw rasterized bitmap (optional) if (printerGraphics != null) { printerGraphics.setBackground(Color.WHITE); printerGraphics.clearRect(0, 0, image.getWidth(), image.getHeight()); printerGraphics.drawImage(image, 0, 0, null); graphics2D.dispose(); } return PAGE_EXISTS; } catch (IOException e) { throw new PrinterIOException(e); } } /** * This will find the CropBox with rotation applied, for this page by looking up the hierarchy until it finds them. * * @return The CropBox at this level in the hierarchy. */ static PDRectangle getRotatedCropBox(PDPage page) { PDRectangle cropBox = page.getCropBox(); int rotationAngle = page.getRotation(); if (rotationAngle == 90 || rotationAngle == 270) { return new PDRectangle(cropBox.getLowerLeftY(), cropBox.getLowerLeftX(), cropBox.getHeight(), cropBox.getWidth()); } return cropBox; } /** * This will find the MediaBox with rotation applied, for this page by looking up the hierarchy until it finds them. * * @return The MediaBox at this level in the hierarchy. */ static PDRectangle getRotatedMediaBox(PDPage page) { PDRectangle mediaBox = page.getMediaBox(); int rotationAngle = page.getRotation(); if (rotationAngle == 90 || rotationAngle == 270) { return new PDRectangle(mediaBox.getLowerLeftY(), mediaBox.getLowerLeftX(), mediaBox.getHeight(), mediaBox.getWidth()); } return mediaBox; } } sambox-1.1.19/src/main/java/org/sejda/sambox/printing/Scaling.java000066400000000000000000000023251320103431700247470ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.printing; /** * Scale of the image on printed pages. * * @author John Hewson */ public enum Scaling { /** Print the image at 100% scale. */ ACTUAL_SIZE, /** Shrink the image to fit the page, if needed. */ SHRINK_TO_FIT, /** Stretch the image to fill the page, if needed. */ STRETCH_TO_FIT, /** Stretch or shrink the image to fill the page, as needed. */ SCALE_TO_FIT } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/000077500000000000000000000000001320103431700226455ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/CIDType0Glyph2D.java000066400000000000000000000050631320103431700262270ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.pdmodel.font.PDCIDFontType0; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * GeneralPath conversion for CFF CIDFont. * * @author John Hewson */ final class CIDType0Glyph2D implements Glyph2D { private static final Logger LOG = LoggerFactory.getLogger(CIDType0Glyph2D.class); private final Map cache = new HashMap(); private final PDCIDFontType0 font; private final String fontName; /** * Constructor. * * @param font Type 0 CIDFont */ CIDType0Glyph2D(PDCIDFontType0 font) // todo: what about PDCIDFontType2? { this.font = font; fontName = font.getBaseFont(); } @Override public GeneralPath getPathForCharacterCode(int code) { GeneralPath path = cache.get(code); if (path == null) { try { if (!font.hasGlyph(code)) { int cid = font.getParent().codeToCID(code); String cidHex = String.format("%04x", cid); LOG.warn("No glyph for " + code + " (CID " + cidHex + ") in font " + fontName); } path = font.getPath(code); cache.put(code, path); return path; } catch (IOException e) { // todo: escalate this error? LOG.error("Glyph rendering failed", e); path = new GeneralPath(); } } return path; } @Override public void dispose() { cache.clear(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/Glyph2D.java000066400000000000000000000026601320103431700247650ustar00rootroot00000000000000/* 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. */ package org.sejda.sambox.rendering; import java.awt.geom.GeneralPath; import java.io.IOException; /** * This interface is implemented by several font specific classes which is called to get the * general path of a single glyph of the represented font most likely to render it. */ interface Glyph2D { /** * Returns the path describing the glyph for the given character code. * * @param code the character code * * @return the GeneralPath for the given character code */ GeneralPath getPathForCharacterCode(int code) throws IOException; /** * Remove all cached resources. */ void dispose(); } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/ImageType.java000066400000000000000000000032111320103431700253710ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import java.awt.image.BufferedImage; /** * Image type for rendering. */ public enum ImageType { /** Black or white. */ BINARY { @Override int toBufferedImageType() { return BufferedImage.TYPE_BYTE_BINARY; } }, /** Shades of gray */ GRAY { @Override int toBufferedImageType() { return BufferedImage.TYPE_BYTE_GRAY; } }, /** Red, Green, Blue */ RGB { @Override int toBufferedImageType() { return BufferedImage.TYPE_INT_RGB; } }, /** Alpha, Red, Green, Blue */ ARGB { @Override int toBufferedImageType() { return BufferedImage.TYPE_INT_ARGB; } }; abstract int toBufferedImageType(); } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/PDFRenderer.java000066400000000000000000000214671320103431700256220ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.IOException; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.common.PDRectangle; /** * Renders a PDF document to an AWT BufferedImage. This class may be overridden in order to perform custom rendering. * * @author John Hewson */ public class PDFRenderer { protected final PDDocument document; // TODO keep rendering state such as caches here /** * Creates a new PDFRenderer. * * @param document the document to render */ public PDFRenderer(PDDocument document) { this.document = document; } /** * Returns the given page as an RGB image at 72 DPI * * @param pageIndex the zero-based index of the page to be converted. * @return the rendered page image * @throws IOException if the PDF cannot be read */ public BufferedImage renderImage(int pageIndex) throws IOException { return renderImage(pageIndex, 1); } /** * Returns the given page as an RGB image at the given scale. A scale of 1 will render at 72 DPI. * * @param pageIndex the zero-based index of the page to be converted * @param scale the scaling factor, where 1 = 72 DPI * @return the rendered page image * @throws IOException if the PDF cannot be read */ public BufferedImage renderImage(int pageIndex, float scale) throws IOException { return renderImage(pageIndex, scale, ImageType.RGB); } /** * Returns the given page as an RGB image at the given DPI. * * @param pageIndex the zero-based index of the page to be converted * @param dpi the DPI (dots per inch) to render at * @return the rendered page image * @throws IOException if the PDF cannot be read */ public BufferedImage renderImageWithDPI(int pageIndex, float dpi) throws IOException { return renderImage(pageIndex, dpi / 72f, ImageType.RGB); } /** * @param page the zero-based index of the page to be converted * @param dpi the DPI (dots per inch) to render at * @param imageType the type of image to return * @return the page rendered as a {@link BufferedImage} * @throws IOException if the PDF cannot be read */ public BufferedImage renderImageWithDPI(int pageIndex, float dpi, ImageType imageType) throws IOException { return renderImage(pageIndex, dpi / 72f, imageType); } /** * @param page the zero-based index of the page to be converted * @param dpi the DPI (dots per inch) to render at * @param bufferedImageType the type of image to return * @return the page rendered as a {@link BufferedImage} * @throws IOException if the PDF cannot be read */ public BufferedImage renderImageWithDPI(int page, float dpi, int bufferedImageType) throws IOException { return renderImage(page, dpi / 72f, bufferedImageType); } /** * @param pageIndex the zero-based index of the page to be converted * @param scale the scaling factor, where 1 = 72 DPI * @param bufferedImageType the type of image to return * @return the page rendered as a {@link BufferedImage} * @throws IOException if the PDF cannot be read */ public BufferedImage renderImage(int pageIndex, float scale, ImageType imageType) throws IOException { return renderImage(pageIndex, scale, imageType.toBufferedImageType()); } /** * @param pageIndex the zero-based index of the page to be converted * @param scale the scaling factor, where 1 = 72 DPI * @param bufferedImageType the type of image to return * @return the page rendered as a {@link BufferedImage} * @throws IOException if the PDF cannot be read */ public BufferedImage renderImage(int pageIndex, float scale, int bufferedImageType) throws IOException { PDPage page = document.getPage(pageIndex); PDRectangle cropbBox = page.getCropBox(); float widthPt = cropbBox.getWidth(); float heightPt = cropbBox.getHeight(); int widthPx = Math.round(widthPt * scale); int heightPx = Math.round(heightPt * scale); int rotationAngle = page.getRotation(); // swap width and height BufferedImage image; if (rotationAngle == 90 || rotationAngle == 270) { image = new BufferedImage(heightPx, widthPx, bufferedImageType); } else { image = new BufferedImage(widthPx, heightPx, bufferedImageType); } // use a transparent background if the imageType supports alpha Graphics2D g = image.createGraphics(); if (bufferedImageType == BufferedImage.TYPE_INT_ARGB) { g.setBackground(new Color(0, 0, 0, 0)); } else { g.setBackground(Color.WHITE); } g.clearRect(0, 0, image.getWidth(), image.getHeight()); transform(g, page, scale); // the end-user may provide a custom PageDrawer PageDrawerParameters parameters = new PageDrawerParameters(this, page); PageDrawer drawer = createPageDrawer(parameters); drawer.drawPage(g, page.getCropBox()); g.dispose(); return image; } /** * Renders a given page to an AWT Graphics2D instance. * * @param pageIndex the zero-based index of the page to be converted * @param graphics the Graphics2D on which to draw the page * @throws IOException if the PDF cannot be read */ public void renderPageToGraphics(int pageIndex, Graphics2D graphics) throws IOException { renderPageToGraphics(pageIndex, graphics, 1); } /** * Renders a given page to an AWT Graphics2D instance. * * @param pageIndex the zero-based index of the page to be converted * @param graphics the Graphics2D on which to draw the page * @param scale the scale to draw the page at * @throws IOException if the PDF cannot be read */ public void renderPageToGraphics(int pageIndex, Graphics2D graphics, float scale) throws IOException { PDPage page = document.getPage(pageIndex); // TODO need width/wight calculations? should these be in PageDrawer? transform(graphics, page, scale); PDRectangle cropBox = page.getCropBox(); graphics.clearRect(0, 0, (int) cropBox.getWidth(), (int) cropBox.getHeight()); // the end-user may provide a custom PageDrawer PageDrawerParameters parameters = new PageDrawerParameters(this, page); PageDrawer drawer = createPageDrawer(parameters); drawer.drawPage(graphics, cropBox); } /// scale rotate translate private void transform(Graphics2D graphics, PDPage page, float scale) { graphics.scale(scale, scale); // TODO should we be passing the scale to PageDrawer rather than messing with Graphics? int rotationAngle = page.getRotation(); PDRectangle cropBox = page.getCropBox(); if (rotationAngle != 0) { float translateX = 0; float translateY = 0; switch (rotationAngle) { case 90: translateX = cropBox.getHeight(); break; case 270: translateY = cropBox.getWidth(); break; case 180: translateX = cropBox.getWidth(); translateY = cropBox.getHeight(); break; default: break; } graphics.translate(translateX, translateY); graphics.rotate((float) Math.toRadians(rotationAngle)); } } /** * Returns a new PageDrawer instance, using the given parameters. May be overridden. */ protected PageDrawer createPageDrawer(PageDrawerParameters parameters) throws IOException { return new PageDrawer(parameters); } } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/PageDrawer.java000066400000000000000000001665421320103431700255470ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsDevice; import java.awt.Paint; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.TexturePaint; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.contentstream.PDFGraphicsStreamEngine; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSBase; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.cos.COSNumber; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.common.function.PDFunction; import org.sejda.sambox.pdmodel.font.PDCIDFontType0; import org.sejda.sambox.pdmodel.font.PDCIDFontType2; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.font.PDTrueTypeFont; import org.sejda.sambox.pdmodel.font.PDType0Font; import org.sejda.sambox.pdmodel.font.PDType1CFont; import org.sejda.sambox.pdmodel.font.PDType1Font; import org.sejda.sambox.pdmodel.graphics.PDLineDashPattern; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.color.PDDeviceGray; import org.sejda.sambox.pdmodel.graphics.color.PDICCBased; import org.sejda.sambox.pdmodel.graphics.color.PDPattern; import org.sejda.sambox.pdmodel.graphics.form.PDTransparencyGroup; import org.sejda.sambox.pdmodel.graphics.image.PDImage; import org.sejda.sambox.pdmodel.graphics.pattern.PDAbstractPattern; import org.sejda.sambox.pdmodel.graphics.pattern.PDShadingPattern; import org.sejda.sambox.pdmodel.graphics.pattern.PDTilingPattern; import org.sejda.sambox.pdmodel.graphics.shading.PDShading; import org.sejda.sambox.pdmodel.graphics.state.PDGraphicsState; import org.sejda.sambox.pdmodel.graphics.state.PDSoftMask; import org.sejda.sambox.pdmodel.graphics.state.RenderingMode; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotation; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationLink; import org.sejda.sambox.pdmodel.interactive.annotation.PDAnnotationMarkup; import org.sejda.sambox.pdmodel.interactive.annotation.PDBorderStyleDictionary; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Paints a page in a PDF document to a Graphics context. May be subclassed to provide custom rendering. * *

* If you want to do custom graphics processing rather than Graphics2D rendering, then you should subclass * PDFGraphicsStreamEngine instead. Subclassing PageDrawer is only suitable for cases where the goal is to render onto a * Graphics2D surface. * * @author Ben Litchfield */ public class PageDrawer extends PDFGraphicsStreamEngine { private static final Logger LOG = LoggerFactory.getLogger(PageDrawer.class); // parent document renderer - note: this is needed for not-yet-implemented resource caching private final PDFRenderer renderer; // the graphics device to draw to, xform is the initial transform of the device (i.e. DPI) private Graphics2D graphics; private AffineTransform xform; // the page box to draw (usually the crop box but may be another) private PDRectangle pageSize; private int pageRotation; // whether image of a transparency group must be flipped // needed when in a tiling pattern private boolean flipTG = false; // clipping winding rule used for the clipping path private int clipWindingRule = -1; private GeneralPath linePath = new GeneralPath(); // last clipping path private Area lastClip; // buffered clipping area for text being drawn private Area textClippingArea; // glyph cache private final Map fontGlyph2D = new HashMap(); private final TilingPaintFactory tilingPaintFactory = new TilingPaintFactory(this); /** * Constructor. * * @param parameters Parameters for page drawing. * @throws IOException If there is an error loading properties from the file. */ public PageDrawer(PageDrawerParameters parameters) throws IOException { super(parameters.getPage()); this.renderer = parameters.getRenderer(); } /** * Returns the parent renderer. */ public final PDFRenderer getRenderer() { return renderer; } /** * Returns the underlying Graphics2D. May be null if drawPage has not yet been called. */ protected final Graphics2D getGraphics() { return graphics; } /** * Returns the current line path. This is reset to empty after each fill/stroke. */ protected final GeneralPath getLinePath() { return linePath; } /** * Sets high-quality rendering hints on the current Graphics2D. */ private void setRenderingHints() { graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } /** * Draws the page to the requested context. * * @param g The graphics context to draw onto. * @param pageSize The size of the page to draw. * @throws IOException If there is an IO error while drawing the page. */ public void drawPage(Graphics g, PDRectangle pageSize) throws IOException { graphics = (Graphics2D) g; xform = graphics.getTransform(); this.pageSize = pageSize; pageRotation = getPage().getRotation() % 360; setRenderingHints(); graphics.translate(0, pageSize.getHeight()); graphics.scale(1, -1); // adjust for non-(0,0) crop box graphics.translate(-pageSize.getLowerLeftX(), -pageSize.getLowerLeftY()); processPage(getPage()); for (PDAnnotation annotation : getPage().getAnnotations()) { showAnnotation(annotation); } graphics = null; } /** * Draws the pattern stream to the requested context. * * @param g The graphics context to draw onto. * @param pattern The tiling pattern to be used. * @param colorSpace color space for this tiling. * @param color color for this tiling. * @param patternMatrix the pattern matrix * @throws IOException If there is an IO error while drawing the page. */ void drawTilingPattern(Graphics2D g, PDTilingPattern pattern, PDColorSpace colorSpace, PDColor color, Matrix patternMatrix) throws IOException { Graphics2D oldGraphics = graphics; graphics = g; GeneralPath oldLinePath = linePath; linePath = new GeneralPath(); int oldClipWindingRule = clipWindingRule; clipWindingRule = -1; Area oldLastClip = lastClip; lastClip = null; boolean oldFlipTG = flipTG; flipTG = true; setRenderingHints(); processTilingPattern(pattern, color, colorSpace, patternMatrix); flipTG = oldFlipTG; graphics = oldGraphics; linePath = oldLinePath; lastClip = oldLastClip; clipWindingRule = oldClipWindingRule; } private float clampColor(float color) { return color < 0 ? 0 : (color > 1 ? 1 : color); } /** * Returns an AWT paint for the given PDColor. * * @param color The color to get a paint for. This can be an actual color or a pattern. * @throws IOException */ protected Paint getPaint(PDColor color) throws IOException { PDColorSpace colorSpace = color.getColorSpace(); if (!(colorSpace instanceof PDPattern)) { float[] rgb = colorSpace.toRGB(color.getComponents()); return new Color(clampColor(rgb[0]), clampColor(rgb[1]), clampColor(rgb[2])); } else { PDPattern patternSpace = (PDPattern) colorSpace; PDAbstractPattern pattern = patternSpace.getPattern(color); if (pattern instanceof PDTilingPattern) { PDTilingPattern tilingPattern = (PDTilingPattern) pattern; if (tilingPattern.getPaintType() == PDTilingPattern.PAINT_COLORED) { // colored tiling pattern return tilingPaintFactory.create(tilingPattern, null, null, xform); } else { // uncolored tiling pattern return tilingPaintFactory.create(tilingPattern, patternSpace.getUnderlyingColorSpace(), color, xform); } } else { PDShadingPattern shadingPattern = (PDShadingPattern) pattern; PDShading shading = shadingPattern.getShading(); if (shading == null) { LOG.error("shadingPattern is null, will be filled with transparency"); return new Color(0, 0, 0, 0); } return shading.toPaint( Matrix.concatenate(getInitialMatrix(), shadingPattern.getMatrix())); } } } // sets the clipping path using caching for performance, we track lastClip manually because // Graphics2D#getClip() returns a new object instead of the same one passed to setClip private void setClip() { Area clippingPath = getGraphicsState().getCurrentClippingPath(); if (clippingPath != lastClip) { graphics.setClip(clippingPath); lastClip = clippingPath; } } @Override public void beginText() throws IOException { setClip(); beginTextClip(); } @Override public void endText() throws IOException { endTextClip(); } /** * Begin buffering the text clipping path, if any. */ private void beginTextClip() { // buffer the text clip because it represents a single clipping area textClippingArea = new Area(); } /** * End buffering the text clipping path, if any. */ private void endTextClip() { PDGraphicsState state = getGraphicsState(); RenderingMode renderingMode = state.getTextState().getRenderingMode(); // apply the buffered clip as one area if (renderingMode.isClip() && !textClippingArea.isEmpty()) { state.intersectClippingPath(textClippingArea); textClippingArea = null; // PDFBOX-3681: lastClip needs to be reset, because after intersection it is still the same // object, thus setClip() would believe that it is cached. lastClip = null; } } @Override protected void showFontGlyph(Matrix textRenderingMatrix, PDFont font, int code, String unicode, Vector displacement) throws IOException { AffineTransform at = textRenderingMatrix.createAffineTransform(); at.concatenate(font.getFontMatrix().createAffineTransform()); Glyph2D glyph2D = createGlyph2D(font); drawGlyph2D(glyph2D, font, code, displacement, at); } /** * Render the font using the Glyph2D interface. * * @param glyph2D the Glyph2D implementation provided a GeneralPath for each glyph * @param font the font * @param code character code * @param displacement the glyph's displacement (advance) * @param at the transformation * @throws IOException if something went wrong */ private void drawGlyph2D(Glyph2D glyph2D, PDFont font, int code, Vector displacement, AffineTransform at) throws IOException { PDGraphicsState state = getGraphicsState(); RenderingMode renderingMode = state.getTextState().getRenderingMode(); GeneralPath path = glyph2D.getPathForCharacterCode(code); if (path != null) { // stretch non-embedded glyph if it does not match the width contained in the PDF if (!font.isEmbedded()) { float fontWidth = font.getWidthFromFont(code); if (fontWidth > 0 && // ignore spaces Math.abs(fontWidth - displacement.getX() * 1000) > 0.0001) { float pdfWidth = displacement.getX() * 1000; at.scale(pdfWidth / fontWidth, 1); } } // render glyph Shape glyph = at.createTransformedShape(path); if (renderingMode.isFill()) { graphics.setComposite(state.getNonStrokingJavaComposite()); graphics.setPaint(getNonStrokingPaint()); setClip(); graphics.fill(glyph); } if (renderingMode.isStroke()) { graphics.setComposite(state.getStrokingJavaComposite()); graphics.setPaint(getStrokingPaint()); graphics.setStroke(getStroke()); setClip(); graphics.draw(glyph); } if (renderingMode.isClip()) { textClippingArea.add(new Area(glyph)); } } } /** * Provide a Glyph2D for the given font. * * @param font the font * @return the implementation of the Glyph2D interface for the given font * @throws IOException if something went wrong */ private Glyph2D createGlyph2D(PDFont font) throws IOException { Glyph2D glyph2D = fontGlyph2D.get(font); // Is there already a Glyph2D for the given font? if (glyph2D != null) { return glyph2D; } if (font instanceof PDTrueTypeFont) { PDTrueTypeFont ttfFont = (PDTrueTypeFont) font; glyph2D = new TTFGlyph2D(ttfFont); // TTF is never null } else if (font instanceof PDType1Font) { PDType1Font pdType1Font = (PDType1Font) font; glyph2D = new Type1Glyph2D(pdType1Font); // T1 is never null } else if (font instanceof PDType1CFont) { PDType1CFont type1CFont = (PDType1CFont) font; glyph2D = new Type1Glyph2D(type1CFont); } else if (font instanceof PDType0Font) { PDType0Font type0Font = (PDType0Font) font; if (type0Font.getDescendantFont() instanceof PDCIDFontType2) { glyph2D = new TTFGlyph2D(type0Font); // TTF is never null } else if (type0Font.getDescendantFont() instanceof PDCIDFontType0) { // a Type0 CIDFont contains CFF font PDCIDFontType0 cidType0Font = (PDCIDFontType0) type0Font.getDescendantFont(); glyph2D = new CIDType0Glyph2D(cidType0Font); // todo: could be null (need incorporate fallback) } } else { throw new IllegalStateException("Bad font type: " + font.getClass().getSimpleName()); } // cache the Glyph2D instance if (glyph2D != null) { fontGlyph2D.put(font, glyph2D); } if (glyph2D == null) { // todo: make sure this never happens throw new UnsupportedOperationException("No font for " + font.getName()); } return glyph2D; } @Override public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) { // to ensure that the path is created in the right direction, we have to create // it by combining single lines instead of creating a simple rectangle linePath.moveTo((float) p0.getX(), (float) p0.getY()); linePath.lineTo((float) p1.getX(), (float) p1.getY()); linePath.lineTo((float) p2.getX(), (float) p2.getY()); linePath.lineTo((float) p3.getX(), (float) p3.getY()); // close the subpath instead of adding the last line so that a possible set line // cap style isn't taken into account at the "beginning" of the rectangle linePath.closePath(); } // TODO: move soft mask apply to getPaint()? private Paint applySoftMaskToPaint(Paint parentPaint, PDSoftMask softMask) throws IOException { if (softMask == null || softMask.getGroup() == null) { return parentPaint; } PDColor backdropColor = null; if (COSName.LUMINOSITY.equals(softMask.getSubType())) { COSArray backdropColorArray = softMask.getBackdropColor(); PDColorSpace colorSpace = softMask.getGroup().getGroup().getColorSpace(); if (colorSpace != null && backdropColorArray != null) { backdropColor = new PDColor(backdropColorArray, colorSpace); } } TransparencyGroup transparencyGroup = new TransparencyGroup(softMask.getGroup(), true, softMask.getInitialTransformationMatrix(), backdropColor); BufferedImage image = transparencyGroup.getImage(); if (image == null) { // Adobe Reader ignores empty softmasks instead of using bc color // sample file: PDFJS-6967_reduced_outside_softmask.pdf return parentPaint; } BufferedImage gray = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); if (COSName.ALPHA.equals(softMask.getSubType())) { gray.setData(image.getAlphaRaster()); } else if (COSName.LUMINOSITY.equals(softMask.getSubType())) { Graphics g = gray.getGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); } else { throw new IOException("Invalid soft mask subtype."); } gray = getRotatedImage(gray); Rectangle2D tpgBounds = transparencyGroup.getBounds(); adjustRectangle(tpgBounds); return new SoftMask(parentPaint, gray, tpgBounds, backdropColor, softMask.getTransferFunction()); } // this adjusts the rectangle to the rotated image to put the soft mask at the correct position // TODO after all transparency problems have been solved: // 1. shouldn't this be done in transparencyGroup.getBounds() ? // 2. change transparencyGroup.getBounds() to getOrigin(), because size isn't used in SoftMask // 3. Is it possible to create the softmask and transparency group in the correct rotation? // (needs rendering identity testing before committing!) private void adjustRectangle(Rectangle2D r) { Matrix m = new Matrix(xform); if (pageRotation == 90) { r.setRect(pageSize.getHeight() * m.getScalingFactorY() - r.getY() - r.getHeight(), r.getX(), r.getWidth(), r.getHeight()); } if (pageRotation == 180) { r.setRect(pageSize.getWidth() * m.getScalingFactorX() - r.getX() - r.getWidth(), pageSize.getHeight() * m.getScalingFactorY() - r.getY() - r.getHeight(), r.getWidth(), r.getHeight()); } if (pageRotation == 270) { r.setRect(r.getY(), pageSize.getWidth() * m.getScalingFactorX() - r.getX() - r.getWidth(), r.getWidth(), r.getHeight()); } } // return quadrant-rotated image with adjusted size private BufferedImage getRotatedImage(BufferedImage gray) throws IOException { BufferedImage gray2; AffineTransform at; switch (pageRotation % 360) { case 90: gray2 = new BufferedImage(gray.getHeight(), gray.getWidth(), BufferedImage.TYPE_BYTE_GRAY); at = AffineTransform.getQuadrantRotateInstance(1, gray.getHeight() / 2d, gray.getHeight() / 2d); break; case 180: gray2 = new BufferedImage(gray.getWidth(), gray.getHeight(), BufferedImage.TYPE_BYTE_GRAY); at = AffineTransform.getQuadrantRotateInstance(2, gray.getWidth() / 2d, gray.getHeight() / 2d); break; case 270: gray2 = new BufferedImage(gray.getHeight(), gray.getWidth(), BufferedImage.TYPE_BYTE_GRAY); at = AffineTransform.getQuadrantRotateInstance(3, gray.getWidth() / 2d, gray.getWidth() / 2d); break; default: return gray; } Graphics2D g2 = (Graphics2D) gray2.getGraphics(); g2.drawImage(gray, at, null); g2.dispose(); return gray2; } // returns the stroking AWT Paint private Paint getStrokingPaint() throws IOException { return applySoftMaskToPaint(getPaint(getGraphicsState().getStrokingColor()), getGraphicsState().getSoftMask()); } // returns the non-stroking AWT Paint private Paint getNonStrokingPaint() throws IOException { return applySoftMaskToPaint(getPaint(getGraphicsState().getNonStrokingColor()), getGraphicsState().getSoftMask()); } // create a new stroke based on the current CTM and the current stroke private BasicStroke getStroke() { PDGraphicsState state = getGraphicsState(); // apply the CTM float lineWidth = transformWidth(state.getLineWidth()); // minimum line width as used by Adobe Reader if (lineWidth < 0.25) { lineWidth = 0.25f; } PDLineDashPattern dashPattern = state.getLineDashPattern(); int phaseStart = dashPattern.getPhase(); float[] dashArray = dashPattern.getDashArray(); // apply the CTM for (int i = 0; i < dashArray.length; ++i) { // minimum line dash width avoids JVM crash, // see PDFBOX-2373, PDFBOX-2929, PDFBOX-3204, PDFBOX-3813 // also avoid 0 in array like "[ 0 1000 ] 0 d", see PDFBOX-3724 float w = transformWidth(dashArray[i]); dashArray[i] = Math.max(w, 0.062f); } phaseStart = (int) transformWidth(phaseStart); // empty dash array is illegal // avoid also infinite and NaN values (PDFBOX-3360) if (dashArray.length == 0 || Float.isInfinite(phaseStart) || Float.isNaN(phaseStart)) { dashArray = null; } else { for (int i = 0; i < dashArray.length; ++i) { if (Float.isInfinite(dashArray[i]) || Float.isNaN(dashArray[i])) { dashArray = null; break; } } } return new BasicStroke(lineWidth, state.getLineCap(), state.getLineJoin(), state.getMiterLimit(), dashArray, phaseStart); } @Override public void strokePath() throws IOException { graphics.setComposite(getGraphicsState().getStrokingJavaComposite()); graphics.setPaint(getStrokingPaint()); graphics.setStroke(getStroke()); setClip(); // TODO bbox of shading pattern should be used here? (see fillPath) graphics.draw(linePath); linePath.reset(); } @Override public void fillPath(int windingRule) throws IOException { graphics.setComposite(getGraphicsState().getNonStrokingJavaComposite()); graphics.setPaint(getNonStrokingPaint()); setClip(); linePath.setWindingRule(windingRule); // disable anti-aliasing for rectangular paths, this is a workaround to avoid small stripes // which occur when solid fills are used to simulate piecewise gradients, see PDFBOX-2302 // note that we ignore paths with a width/height under 1 as these are fills used as strokes, // see PDFBOX-1658 for an example Rectangle2D bounds = linePath.getBounds2D(); boolean noAntiAlias = isRectangular(linePath) && bounds.getWidth() > 1 && bounds.getHeight() > 1; if (noAntiAlias) { graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); } if (!(graphics.getPaint() instanceof Color)) { // apply clip to path to avoid oversized device bounds in shading contexts (PDFBOX-2901) Area area = new Area(linePath); area.intersect(new Area(graphics.getClip())); intersectShadingBBox(getGraphicsState().getNonStrokingColor(), area); graphics.fill(area); } else { graphics.fill(linePath); } linePath.reset(); if (noAntiAlias) { // JDK 1.7 has a bug where rendering hints are reset by the above call to // the setRenderingHint method, so we re-set all hints, see PDFBOX-2302 setRenderingHints(); } } // checks whether this is a shading pattern and if yes, // get the transformed BBox and intersect with current paint area // need to do it here and not in shading getRaster() because it may have been rotated private void intersectShadingBBox(PDColor color, Area area) throws IOException { if (color.getColorSpace() instanceof PDPattern) { PDColorSpace colorSpace = color.getColorSpace(); PDAbstractPattern pat = ((PDPattern) colorSpace).getPattern(color); if (pat instanceof PDShadingPattern) { PDShading shading = ((PDShadingPattern) pat).getShading(); PDRectangle bbox = shading.getBBox(); if (bbox != null) { Matrix m = Matrix.concatenate(getInitialMatrix(), pat.getMatrix()); Area bboxArea = new Area(bbox.transform(m)); area.intersect(bboxArea); } } } } /** * Returns true if the given path is rectangular. */ private boolean isRectangular(GeneralPath path) { PathIterator iter = path.getPathIterator(null); double[] coords = new double[6]; int count = 0; int[] xs = new int[4]; int[] ys = new int[4]; while (!iter.isDone()) { switch (iter.currentSegment(coords)) { case PathIterator.SEG_MOVETO: if (count == 0) { xs[count] = (int) Math.floor(coords[0]); ys[count] = (int) Math.floor(coords[1]); } else { return false; } count++; break; case PathIterator.SEG_LINETO: if (count < 4) { xs[count] = (int) Math.floor(coords[0]); ys[count] = (int) Math.floor(coords[1]); } else { return false; } count++; break; case PathIterator.SEG_CUBICTO: return false; case PathIterator.SEG_CLOSE: break; } iter.next(); } if (count == 4) { return xs[0] == xs[1] || xs[0] == xs[2] || ys[0] == ys[1] || ys[0] == ys[3]; } return false; } /** * Fills and then strokes the path. * * @param windingRule The winding rule this path will use. * @throws IOException If there is an IO error while filling the path. */ @Override public void fillAndStrokePath(int windingRule) throws IOException { // TODO can we avoid cloning the path? GeneralPath path = (GeneralPath) linePath.clone(); fillPath(windingRule); linePath = path; strokePath(); } @Override public void clip(int windingRule) { // the clipping path will not be updated until the succeeding painting operator is called clipWindingRule = windingRule; } @Override public void moveTo(float x, float y) { linePath.moveTo(x, y); } @Override public void lineTo(float x, float y) { linePath.lineTo(x, y); } @Override public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) { linePath.curveTo(x1, y1, x2, y2, x3, y3); } @Override public Point2D getCurrentPoint() { return linePath.getCurrentPoint(); } @Override public void closePath() { linePath.closePath(); } @Override public void endPath() { if (clipWindingRule != -1) { linePath.setWindingRule(clipWindingRule); getGraphicsState().intersectClippingPath(linePath); // PDFBOX-3836: lastClip needs to be reset, because after intersection it is still the same // object, thus setClip() would believe that it is cached. lastClip = null; clipWindingRule = -1; } linePath.reset(); } @Override public void drawImage(PDImage pdImage) throws IOException { Matrix ctm = getGraphicsState().getCurrentTransformationMatrix(); AffineTransform at = ctm.createAffineTransform(); if (!pdImage.getInterpolate()) { boolean isScaledUp = pdImage.getWidth() < Math.round(at.getScaleX()) || pdImage.getHeight() < Math.round(at.getScaleY()); // if the image is scaled down, we use smooth interpolation, eg PDFBOX-2364 // only when scaled up do we use nearest neighbour, eg PDFBOX-2302 / mori-cvpr01.pdf // stencils are excluded from this rule (see survey.pdf) if (isScaledUp || pdImage.isStencil()) { graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); } } if (pdImage.isStencil()) { if (getGraphicsState().getNonStrokingColor().getColorSpace() instanceof PDPattern) { // The earlier code for stencils (see "else") doesn't work with patterns because the // CTM is not taken into consideration. // this code is based on the fact that it is easily possible to draw the mask and // the paint at the correct place with the existing code, but not in one step. // Thus what we do is to draw both in separate images, then combine the two and draw // the result. // Note that the device scale is not used. In theory, some patterns can get better // at higher resolutions but the stencil would become more and more "blocky". // If anybody wants to do this, have a look at the code in showTransparencyGroup(). // draw the paint Paint paint = getNonStrokingPaint(); Rectangle2D unitRect = new Rectangle2D.Float(0, 0, 1, 1); Rectangle2D bounds = at.createTransformedShape(unitRect).getBounds2D(); BufferedImage renderedPaint = new BufferedImage((int) Math.ceil(bounds.getWidth()), (int) Math.ceil(bounds.getHeight()), BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) renderedPaint.getGraphics(); g.translate(-bounds.getMinX(), -bounds.getMinY()); g.setPaint(paint); g.fill(bounds); g.dispose(); // draw the mask BufferedImage mask = pdImage.getImage(); BufferedImage renderedMask = new BufferedImage((int) Math.ceil(bounds.getWidth()), (int) Math.ceil(bounds.getHeight()), BufferedImage.TYPE_INT_RGB); g = (Graphics2D) renderedMask.getGraphics(); g.translate(-bounds.getMinX(), -bounds.getMinY()); AffineTransform imageTransform = new AffineTransform(at); imageTransform.scale(1.0 / mask.getWidth(), -1.0 / mask.getHeight()); imageTransform.translate(0, -mask.getHeight()); g.drawImage(mask, imageTransform, null); g.dispose(); // apply the mask final int[] transparent = new int[4]; int[] alphaPixel = null; WritableRaster raster = renderedPaint.getRaster(); WritableRaster alpha = renderedMask.getRaster(); int h = renderedMask.getRaster().getHeight(); int w = renderedMask.getRaster().getWidth(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { alphaPixel = alpha.getPixel(x, y, alphaPixel); if (alphaPixel[0] == 255) { raster.setPixel(x, y, transparent); } } } // draw the image setClip(); graphics.setComposite(getGraphicsState().getNonStrokingJavaComposite()); graphics.drawImage(renderedPaint, AffineTransform.getTranslateInstance(bounds.getMinX(), bounds.getMinY()), null); } else { // fill the image with stenciled paint BufferedImage image = pdImage.getStencilImage(getNonStrokingPaint()); // draw the image drawBufferedImage(image, at); } } else { // draw the image drawBufferedImage(pdImage.getImage(), at); } if (!pdImage.getInterpolate()) { // JDK 1.7 has a bug where rendering hints are reset by the above call to // the setRenderingHint method, so we re-set all hints, see PDFBOX-2302 setRenderingHints(); } } private void drawBufferedImage(BufferedImage image, AffineTransform at) throws IOException { graphics.setComposite(getGraphicsState().getNonStrokingJavaComposite()); setClip(); PDSoftMask softMask = getGraphicsState().getSoftMask(); if (softMask != null) { AffineTransform imageTransform = new AffineTransform(at); imageTransform.scale(1, -1); imageTransform.translate(0, -1); Paint awtPaint = new TexturePaint(image, new Rectangle2D.Double(imageTransform.getTranslateX(), imageTransform.getTranslateY(), imageTransform.getScaleX(), imageTransform.getScaleY())); awtPaint = applySoftMaskToPaint(awtPaint, softMask); graphics.setPaint(awtPaint); Rectangle2D unitRect = new Rectangle2D.Float(0, 0, 1, 1); graphics.fill(at.createTransformedShape(unitRect)); } else { COSBase transfer = getGraphicsState().getTransfer(); if (transfer instanceof COSArray || transfer instanceof COSDictionary) { image = applyTransferFunction(image, transfer); } int width = image.getWidth(null); int height = image.getHeight(null); AffineTransform imageTransform = new AffineTransform(at); imageTransform.scale(1.0 / width, -1.0 / height); imageTransform.translate(0, -height); graphics.drawImage(image, imageTransform, null); } } private BufferedImage applyTransferFunction(BufferedImage image, COSBase transfer) throws IOException { BufferedImage bim; if (image.getColorModel().hasAlpha()) { bim = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); } else { bim = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); } // prepare transfer functions (either one per color or one for all) // and maps (actually arrays[256] to be faster) to avoid calculating values several times Integer rMap[], gMap[], bMap[]; PDFunction rf, gf, bf; if (transfer instanceof COSArray) { COSArray ar = (COSArray) transfer; rf = PDFunction.create(ar.getObject(0)); gf = PDFunction.create(ar.getObject(1)); bf = PDFunction.create(ar.getObject(2)); rMap = new Integer[256]; gMap = new Integer[256]; bMap = new Integer[256]; } else { rf = PDFunction.create(transfer); gf = rf; bf = rf; rMap = new Integer[256]; gMap = rMap; bMap = rMap; } // apply the transfer function to each color, but keep alpha float[] input = new float[1]; for (int x = 0; x < image.getWidth(); ++x) { for (int y = 0; y < image.getHeight(); ++y) { int rgb = image.getRGB(x, y); int ri = (rgb >> 16) & 0xFF; int gi = (rgb >> 8) & 0xFF; int bi = rgb & 0xFF; int ro, go, bo; if (rMap[ri] != null) { ro = rMap[ri]; } else { input[0] = (ri & 0xFF) / 255f; ro = (int) (rf.eval(input)[0] * 255); rMap[ri] = ro; } if (gMap[gi] != null) { go = gMap[gi]; } else { input[0] = (gi & 0xFF) / 255f; go = (int) (gf.eval(input)[0] * 255); gMap[gi] = go; } if (bMap[bi] != null) { bo = bMap[bi]; } else { input[0] = (bi & 0xFF) / 255f; bo = (int) (bf.eval(input)[0] * 255); bMap[bi] = bo; } bim.setRGB(x, y, (rgb & 0xFF000000) | (ro << 16) | (go << 8) | bo); } } return bim; } @Override public void shadingFill(COSName shadingName) throws IOException { PDShading shading = getResources().getShading(shadingName); if (shading == null) { LOG.error("shading " + shadingName + " does not exist in resources dictionary"); return; } Matrix ctm = getGraphicsState().getCurrentTransformationMatrix(); Paint paint = shading.toPaint(ctm); paint = applySoftMaskToPaint(paint, getGraphicsState().getSoftMask()); graphics.setComposite(getGraphicsState().getNonStrokingJavaComposite()); graphics.setPaint(paint); graphics.setClip(null); lastClip = null; // get the transformed BBox and intersect with current clipping path // need to do it here and not in shading getRaster() because it may have been rotated PDRectangle bbox = shading.getBBox(); if (bbox != null) { Area bboxArea = new Area(bbox.transform(ctm)); bboxArea.intersect(getGraphicsState().getCurrentClippingPath()); graphics.fill(bboxArea); } else { graphics.fill(getGraphicsState().getCurrentClippingPath()); } } @Override public void showAnnotation(PDAnnotation annotation) throws IOException { lastClip = null; // TODO support more annotation flags (Invisible, NoZoom, NoRotate) // Example for NoZoom can be found in p5 of PDFBOX-2348 int deviceType = graphics.getDeviceConfiguration().getDevice().getType(); if (deviceType == GraphicsDevice.TYPE_PRINTER && !annotation.isPrinted()) { return; } if (deviceType == GraphicsDevice.TYPE_RASTER_SCREEN && annotation.isNoView()) { return; } if (annotation.isHidden()) { return; } super.showAnnotation(annotation); if (annotation.getAppearance() == null) { if (annotation instanceof PDAnnotationLink) { drawAnnotationLinkBorder((PDAnnotationLink) annotation); } if (annotation instanceof PDAnnotationMarkup && annotation.getSubtype().equals(PDAnnotationMarkup.SUB_TYPE_INK)) { drawAnnotationInk((PDAnnotationMarkup) annotation); } } } private static class AnnotationBorder { private float[] dashArray = null; private boolean underline = false; private float width = 0; private PDColor color; } // return border info. BorderStyle must be provided as parameter because // method is not available in the base class private AnnotationBorder getAnnotationBorder(PDAnnotation annotation, PDBorderStyleDictionary borderStyle) { AnnotationBorder ab = new AnnotationBorder(); COSArray border = annotation.getBorder(); if (borderStyle == null) { if (border.getObject(2) instanceof COSNumber) { ab.width = ((COSNumber) border.getObject(2)).floatValue(); } if (border.size() > 3) { COSBase base3 = border.getObject(3); if (base3 instanceof COSArray) { ab.dashArray = ((COSArray) base3).toFloatArray(); } } } else { ab.width = borderStyle.getWidth(); if (borderStyle.getStyle().equals(PDBorderStyleDictionary.STYLE_DASHED)) { ab.dashArray = borderStyle.getDashStyle().getDashArray(); } if (borderStyle.getStyle().equals(PDBorderStyleDictionary.STYLE_UNDERLINE)) { ab.underline = true; } } ab.color = annotation.getColor(); if (ab.color == null) { // spec is unclear, but black seems to be the right thing to do ab.color = new PDColor(new float[] { 0 }, PDDeviceGray.INSTANCE); } if (ab.dashArray != null) { boolean allZero = true; for (float f : ab.dashArray) { if (f != 0) { allZero = false; break; } } if (allZero) { ab.dashArray = null; } } return ab; } private void drawAnnotationLinkBorder(PDAnnotationLink link) throws IOException { AnnotationBorder ab = getAnnotationBorder(link, link.getBorderStyle()); if (ab.width == 0 || ab.color.getComponents().length == 0) { return; } PDRectangle rectangle = link.getRectangle(); Stroke oldStroke = graphics.getStroke(); graphics.setPaint(getPaint(ab.color)); BasicStroke stroke = new BasicStroke(ab.width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, ab.dashArray, 0); graphics.setStroke(stroke); graphics.setClip(null); if (ab.underline) { graphics.drawLine((int) rectangle.getLowerLeftX(), (int) rectangle.getLowerLeftY(), (int) (rectangle.getLowerLeftX() + rectangle.getWidth()), (int) rectangle.getLowerLeftY()); } else { graphics.drawRect((int) rectangle.getLowerLeftX(), (int) rectangle.getLowerLeftY(), (int) rectangle.getWidth(), (int) rectangle.getHeight()); } graphics.setStroke(oldStroke); } private void drawAnnotationInk(PDAnnotationMarkup inkAnnotation) throws IOException { if (!inkAnnotation.getCOSObject().containsKey(COSName.INKLIST)) { return; } // TODO there should be an InkAnnotation class with a getInkList method COSBase base = inkAnnotation.getCOSObject().getDictionaryObject(COSName.INKLIST); if (!(base instanceof COSArray)) { return; } // PDF spec does not mention /Border for ink annotations, but it is used if /BS is not available AnnotationBorder ab = getAnnotationBorder(inkAnnotation, inkAnnotation.getBorderStyle()); if (ab.width == 0 || ab.color.getComponents().length == 0) { return; } graphics.setPaint(getPaint(ab.color)); Stroke oldStroke = graphics.getStroke(); BasicStroke stroke = new BasicStroke(ab.width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, ab.dashArray, 0); graphics.setStroke(stroke); graphics.setClip(null); COSArray pathsArray = (COSArray) base; for (COSBase baseElement : pathsArray.toList()) { if (!(baseElement instanceof COSArray)) { continue; } COSArray pathArray = (COSArray) baseElement; int nPoints = pathArray.size() / 2; // "When drawn, the points shall be connected by straight lines or curves // in an implementation-dependent way" - we do lines. GeneralPath path = new GeneralPath(); for (int i = 0; i < nPoints; ++i) { COSBase bx = pathArray.getObject(i * 2); COSBase by = pathArray.getObject(i * 2 + 1); if (bx instanceof COSNumber && by instanceof COSNumber) { float x = ((COSNumber) bx).floatValue(); float y = ((COSNumber) by).floatValue(); if (i == 0) { path.moveTo(x, y); } else { path.lineTo(x, y); } } } graphics.draw(path); } graphics.setStroke(oldStroke); } @Override public void showTransparencyGroup(PDTransparencyGroup form) throws IOException { TransparencyGroup group = new TransparencyGroup(form, false, getGraphicsState().getCurrentTransformationMatrix(), null); BufferedImage image = group.getImage(); if (image == null) { // image is empty, don't bother return; } graphics.setComposite(getGraphicsState().getNonStrokingJavaComposite()); setClip(); // both the DPI xform and the CTM were already applied to the group, so all we do // here is draw it directly onto the Graphics2D device at the appropriate position PDRectangle bbox = group.getBBox(); AffineTransform prev = graphics.getTransform(); Matrix m = new Matrix(xform); float xScale = Math.abs(m.getScalingFactorX()); float yScale = Math.abs(m.getScalingFactorY()); // adjust the initial translation (includes the translation used to "help" the rotation) graphics.setTransform( AffineTransform.getTranslateInstance(xform.getTranslateX(), xform.getTranslateY())); graphics.rotate(Math.toRadians(pageRotation)); // adjust bbox (x,y) position at the initial scale + cropbox float x = bbox.getLowerLeftX() - pageSize.getLowerLeftX(); float y = pageSize.getUpperRightY() - bbox.getUpperRightY(); graphics.translate(x * xScale, y * yScale); if (flipTG) { graphics.translate(0, image.getHeight()); graphics.scale(1, -1); } PDSoftMask softMask = getGraphicsState().getSoftMask(); if (softMask != null) { Paint awtPaint = new TexturePaint(image, new Rectangle2D.Float(0, 0, image.getWidth(), image.getHeight())); awtPaint = applySoftMaskToPaint(awtPaint, softMask); graphics.setPaint(awtPaint); graphics.fill(new Rectangle2D.Float(0, 0, bbox.getWidth() * xScale, bbox.getHeight() * yScale)); } else { graphics.drawImage(image, null, null); } graphics.setTransform(prev); } /** * Transparency group. **/ private final class TransparencyGroup { private final BufferedImage image; private final PDRectangle bbox; private final int minX; private final int minY; private final int width; private final int height; /** * Creates a buffered image for a transparency group result. * * @param form the transparency group of the form or soft mask. * @param isSoftMask true if this is a soft mask. * @param ctm the relevant current transformation matrix. For soft masks, this is the CTM at the time the soft * mask is set (not at the time the soft mask is used for fill/stroke!), for forms, this is the CTM at the time * the form is invoked. * @param backdropColor the color according to the /bc entry to be used for luminosity soft masks. * @throws IOException */ private TransparencyGroup(PDTransparencyGroup form, boolean isSoftMask, Matrix ctm, PDColor backdropColor) throws IOException { Graphics2D g2dOriginal = graphics; Area lastClipOriginal = lastClip; // get the CTM x Form Matrix transform Matrix transform = Matrix.concatenate(ctm, form.getMatrix()); // transform the bbox GeneralPath transformedBox = form.getBBox().transform(transform); // clip the bbox to prevent giant bboxes from consuming all memory Area clip = (Area) getGraphicsState().getCurrentClippingPath().clone(); clip.intersect(new Area(transformedBox)); Rectangle2D clipRect = clip.getBounds2D(); if (clipRect.isEmpty()) { image = null; bbox = null; minX = 0; minY = 0; width = 0; height = 0; return; } this.bbox = new PDRectangle((float) clipRect.getX(), (float) clipRect.getY(), (float) clipRect.getWidth(), (float) clipRect.getHeight()); // apply the underlying Graphics2D device's DPI transform Matrix m = new Matrix(xform); AffineTransform dpiTransform = AffineTransform.getScaleInstance( Math.abs(m.getScalingFactorX()), Math.abs(m.getScalingFactorY())); Rectangle2D bounds = dpiTransform.createTransformedShape(clip.getBounds2D()) .getBounds2D(); minX = (int) Math.floor(bounds.getMinX()); minY = (int) Math.floor(bounds.getMinY()); int maxX = (int) Math.floor(bounds.getMaxX()) + 1; int maxY = (int) Math.floor(bounds.getMaxY()) + 1; width = maxX - minX; height = maxY - minY; // FIXME - color space if (isGray(form.getGroup().getColorSpace())) { image = create2ByteGrayAlphaImage(width, height); } else { image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); } Graphics2D g = image.createGraphics(); if (isSoftMask && backdropColor != null) { // "If the subtype is Luminosity, the transparency group XObject G shall be // composited with a fully opaque backdrop whose colour is everywhere defined // by the soft-mask dictionary's BC entry." g.setBackground(new Color(backdropColor.toRGB())); g.clearRect(0, 0, width, height); } // flip y-axis g.translate(0, image.getHeight()); g.scale(1, -1); boolean oldFlipTG = flipTG; flipTG = false; // apply device transform (DPI) // the initial translation is ignored, because we're not writing into the initial graphics device g.transform(dpiTransform); AffineTransform xformOriginal = xform; xform = AffineTransform.getScaleInstance(m.getScalingFactorX(), m.getScalingFactorY()); PDRectangle pageSizeOriginal = pageSize; pageSize = new PDRectangle(minX / Math.abs(m.getScalingFactorX()), minY / Math.abs(m.getScalingFactorY()), (float) bounds.getWidth() / Math.abs(m.getScalingFactorX()), (float) bounds.getHeight() / Math.abs(m.getScalingFactorY())); int pageRotationOriginal = pageRotation; pageRotation = 0; int clipWindingRuleOriginal = clipWindingRule; clipWindingRule = -1; GeneralPath linePathOriginal = linePath; linePath = new GeneralPath(); // adjust the origin g.translate(-clipRect.getX(), -clipRect.getY()); graphics = g; setRenderingHints(); try { if (isSoftMask) { processSoftMask(form); } else { processTransparencyGroup(form); } } finally { flipTG = oldFlipTG; lastClip = lastClipOriginal; graphics.dispose(); graphics = g2dOriginal; clipWindingRule = clipWindingRuleOriginal; linePath = linePathOriginal; pageSize = pageSizeOriginal; xform = xformOriginal; pageRotation = pageRotationOriginal; } } // http://stackoverflow.com/a/21181943/535646 private BufferedImage create2ByteGrayAlphaImage(int width, int height) { /** * gray + alpha */ int[] bandOffsets = new int[] { 1, 0 }; int bands = bandOffsets.length; /** * Color Model usesd for raw GRAY + ALPHA */ final ColorModel CM_GRAY_ALPHA = new ComponentColorModel( ColorSpace.getInstance(ColorSpace.CS_GRAY), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); // Init data buffer of type byte DataBuffer buffer = new DataBufferByte(width * height * bands); // Wrap the data buffer in a raster WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, width * bands, bands, bandOffsets, new Point(0, 0)); // Create a custom BufferedImage with the raster and a suitable color model return new BufferedImage(CM_GRAY_ALPHA, raster, false, null); } private boolean isGray(PDColorSpace colorSpace) { if (colorSpace instanceof PDDeviceGray) { return true; } if (colorSpace instanceof PDICCBased) { try { return ((PDICCBased) colorSpace) .getAlternateColorSpace() instanceof PDDeviceGray; } catch (IOException ex) { return false; } } return false; } public BufferedImage getImage() { return image; } public PDRectangle getBBox() { return bbox; } public Rectangle2D getBounds() { Point2D size = new Point2D.Double(pageSize.getWidth(), pageSize.getHeight()); // apply the underlying Graphics2D device's DPI transform and y-axis flip Matrix m = new Matrix(xform); AffineTransform dpiTransform = AffineTransform.getScaleInstance( Math.abs(m.getScalingFactorX()), Math.abs(m.getScalingFactorY())); size = dpiTransform.transform(size, size); // Flip y return new Rectangle2D.Double(minX - pageSize.getLowerLeftX() * m.getScalingFactorX(), size.getY() - minY - height + pageSize.getLowerLeftY() * m.getScalingFactorY(), width, height); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/PageDrawerParameters.java000066400000000000000000000032041320103431700275540ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import org.sejda.sambox.pdmodel.PDPage; /** * Parameters for a PageDrawer. This class ensures allows PDFRenderer and PageDrawer to share * private implementation data in a future-proof manner, while still allowing end-users to create * their own subclasses of PageDrawer. * * @author John Hewson */ public final class PageDrawerParameters { private final PDFRenderer renderer; private final PDPage page; /** * Package-private constructor. */ PageDrawerParameters(PDFRenderer renderer, PDPage page) { this.renderer = renderer; this.page = page; } /** * Returns the page. */ public PDPage getPage() { return page; } /** * Returns the renderer. */ PDFRenderer getRenderer() { return renderer; } } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/SoftMask.java000066400000000000000000000163411320103431700252440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import java.awt.Color; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import org.sejda.sambox.pdmodel.common.function.PDFunction; import org.sejda.sambox.pdmodel.common.function.PDFunctionTypeIdentity; import org.sejda.sambox.pdmodel.graphics.color.PDColor; /** * A Paint which applies a soft mask to an underlying Paint. * * @author Petr Slaby * @author John Hewson * @author Matthias Bläsing * @author Tilman Hausherr */ class SoftMask implements Paint { private static final ColorModel ARGB_COLOR_MODEL = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getColorModel(); private final Paint paint; private final BufferedImage mask; private final Rectangle2D bboxDevice; private int bc = 0; private final PDFunction transferFunction; /** * Creates a new soft mask paint. * * @param paint underlying paint. * @param mask soft mask * @param bboxDevice bbox of the soft mask in the underlying Graphics2D device space * @param backdropColor the color to be used outside the transparency group’s bounding box; if null, black will be * used. * @param transferFunction the transfer function, may be null. */ SoftMask(Paint paint, BufferedImage mask, Rectangle2D bboxDevice, PDColor backdropColor, PDFunction transferFunction) { this.paint = paint; this.mask = mask; this.bboxDevice = bboxDevice; if (transferFunction instanceof PDFunctionTypeIdentity) { this.transferFunction = null; } else { this.transferFunction = transferFunction; } if (backdropColor != null) { try { Color color = new Color(backdropColor.toRGB()); // http://stackoverflow.com/a/25463098/535646 bc = (299 * color.getRed() + 587 * color.getGreen() + 114 * color.getBlue()) / 1000; } catch (IOException ex) { // keep default } } } @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { PaintContext ctx = paint.createContext(cm, deviceBounds, userBounds, xform, hints); return new SoftPaintContext(ctx); } @Override public int getTransparency() { return TRANSLUCENT; } private class SoftPaintContext implements PaintContext { private final PaintContext context; SoftPaintContext(PaintContext context) { this.context = context; } @Override public ColorModel getColorModel() { return ARGB_COLOR_MODEL; } @Override public Raster getRaster(int x1, int y1, int w, int h) { Raster raster = context.getRaster(x1, y1, w, h); ColorModel rasterCM = context.getColorModel(); float[] input = null; Float[] map = null; if (transferFunction != null) { map = new Float[256]; input = new float[1]; } // buffer WritableRaster output = getColorModel().createCompatibleWritableRaster(w, h); // the soft mask has its own bbox x1 = x1 - (int) bboxDevice.getX(); y1 = y1 - (int) bboxDevice.getY(); int[] gray = new int[4]; Object pixelInput = null; int[] pixelOutput = new int[4]; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { pixelInput = raster.getDataElements(x, y, pixelInput); pixelOutput[0] = rasterCM.getRed(pixelInput); pixelOutput[1] = rasterCM.getGreen(pixelInput); pixelOutput[2] = rasterCM.getBlue(pixelInput); pixelOutput[3] = rasterCM.getAlpha(pixelInput); // get the alpha value from the gray mask, if within mask bounds gray[0] = 0; if (x1 + x >= 0 && y1 + y >= 0 && x1 + x < mask.getWidth() && y1 + y < mask.getHeight()) { mask.getRaster().getPixel(x1 + x, y1 + y, gray); int g = gray[0]; if (transferFunction != null) { // apply transfer function try { if (map[g] != null) { // was calculated before pixelOutput[3] = Math.round(pixelOutput[3] * map[g]); } else { // calculate and store in map input[0] = g / 255f; float f = transferFunction.eval(input)[0]; map[g] = f; pixelOutput[3] = Math.round(pixelOutput[3] * f); } } catch (IOException ex) { // ignore exception, treat as outside pixelOutput[3] = Math.round(pixelOutput[3] * (bc / 255f)); } } else { pixelOutput[3] = Math.round(pixelOutput[3] * (g / 255f)); } } else { pixelOutput[3] = Math.round(pixelOutput[3] * (bc / 255f)); } output.setPixel(x, y, pixelOutput); } } return output; } @Override public void dispose() { } } } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/TTFGlyph2D.java000066400000000000000000000125641320103431700253470ustar00rootroot00000000000000/* 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. */ package org.sejda.sambox.rendering; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.fontbox.ttf.HeaderTable; import org.apache.fontbox.ttf.TrueTypeFont; import org.sejda.sambox.pdmodel.font.PDCIDFontType2; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.font.PDTrueTypeFont; import org.sejda.sambox.pdmodel.font.PDType0Font; import org.sejda.sambox.pdmodel.font.PDVectorFont; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class provides a glyph to GeneralPath conversion for TrueType and OpenType fonts. */ final class TTFGlyph2D implements Glyph2D { private static final Logger LOG = LoggerFactory.getLogger(TTFGlyph2D.class); private final PDFont font; private final TrueTypeFont ttf; private PDVectorFont vectorFont; private float scale = 1.0f; private boolean hasScaling; private final Map glyphs = new HashMap(); private final boolean isCIDFont; /** * Constructor. * * @param ttfFont TrueType font */ TTFGlyph2D(PDTrueTypeFont ttfFont) throws IOException { this(ttfFont.getTrueTypeFont(), ttfFont, false); vectorFont = ttfFont; } /** * Constructor. * * @param type0Font Type0 font, with CIDFontType2 descendant */ TTFGlyph2D(PDType0Font type0Font) throws IOException { this(((PDCIDFontType2) type0Font.getDescendantFont()).getTrueTypeFont(), type0Font, true); vectorFont = type0Font; } private TTFGlyph2D(TrueTypeFont ttf, PDFont font, boolean isCIDFont) throws IOException { this.font = font; this.ttf = ttf; this.isCIDFont = isCIDFont; // get units per em, which is used as scaling factor HeaderTable header = this.ttf.getHeader(); if (header != null && header.getUnitsPerEm() != 1000) { // in most case the scaling factor is set to 1.0f // due to the fact that units per em is set to 1000 scale = 1000f / header.getUnitsPerEm(); hasScaling = true; } } @Override public GeneralPath getPathForCharacterCode(int code) throws IOException { int gid = getGIDForCharacterCode(code); return getPathForGID(gid, code); } // Try to map the given code to the corresponding glyph-ID private int getGIDForCharacterCode(int code) throws IOException { if (isCIDFont) { return ((PDType0Font) font).codeToGID(code); } return ((PDTrueTypeFont) font).codeToGID(code); } /** * Returns the path describing the glyph for the given glyphId. * * @param gid the GID * @param code the character code * * @return the GeneralPath for the given glyphId */ public GeneralPath getPathForGID(int gid, int code) throws IOException { GeneralPath glyphPath = glyphs.get(gid); if (glyphPath == null) { if (gid == 0 || gid >= ttf.getMaximumProfile().getNumGlyphs()) { if (isCIDFont) { int cid = ((PDType0Font) font).codeToCID(code); String cidHex = String.format("%04x", cid); LOG.warn("No glyph for " + code + " (CID " + cidHex + ") in font " + font.getName()); } else { LOG.warn("No glyph for " + code + " in font " + font.getName()); } } GeneralPath glyph = vectorFont.getPath(code); // Acrobat only draws GID 0 for embedded or "Standard 14" fonts, see PDFBOX-2372 if (gid == 0 && !font.isEmbedded() && !font.isStandard14()) { glyph = null; } if (glyph == null) { // empty glyph (e.g. space, newline) glyphPath = new GeneralPath(); glyphs.put(gid, glyphPath); } else { glyphPath = glyph; if (hasScaling) { AffineTransform atScale = AffineTransform.getScaleInstance(scale, scale); glyphPath.transform(atScale); } glyphs.put(gid, glyphPath); } } // todo: expensive return (GeneralPath) glyphPath.clone(); } @Override public void dispose() { glyphs.clear(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/TilingPaint.java000066400000000000000000000217521320103431700257410ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.TexturePaint; import java.awt.Transparency; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.pattern.PDTilingPattern; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * AWT Paint for a tiling pattern, which consists of a small repeating graphical figure. * * @author John Hewson */ class TilingPaint implements Paint { private static final Logger LOG = LoggerFactory.getLogger(TilingPaint.class); private final TexturePaint paint; private final Matrix patternMatrix; private static final int MAXEDGE; private static final String DEFAULTMAXEDGE = "3000"; static { String s = System.getProperty("org.sambox.rendering.tilingpaint.maxedge", DEFAULTMAXEDGE); int val; try { val = Integer.parseInt(s); } catch (NumberFormatException ex) { LOG.error("Default will be used", ex); val = Integer.parseInt(DEFAULTMAXEDGE); } MAXEDGE = val; } /** * Creates a new colored tiling Paint, i.e. one that has its own colors. * * @param drawer renderer to render the page * @param pattern tiling pattern dictionary * @param xform device scale transform * * @throws java.io.IOException if something goes wrong while drawing the pattern */ TilingPaint(PageDrawer drawer, PDTilingPattern pattern, AffineTransform xform) throws IOException { this(drawer, pattern, null, null, xform); } /** * Creates a new tiling Paint. The parameters color and colorSpace must be null for a colored tiling Paint (because * it has its own colors), and non null for an uncolored tiling Paint. * * @param drawer renderer to render the page * @param pattern tiling pattern dictionary * @param colorSpace color space for this tiling * @param color color for this tiling * @param xform device scale transform * * @throws java.io.IOException if something goes wrong while drawing the pattern */ TilingPaint(PageDrawer drawer, PDTilingPattern pattern, PDColorSpace colorSpace, PDColor color, AffineTransform xform) throws IOException { // pattern space -> user space patternMatrix = Matrix.concatenate(drawer.getInitialMatrix(), pattern.getMatrix()); Rectangle2D anchorRect = getAnchorRect(pattern); paint = new TexturePaint(getImage(drawer, pattern, colorSpace, color, xform, anchorRect), anchorRect); } /** * Not called in TexturePaint subclasses, which is why we wrap TexturePaint. */ @Override public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { AffineTransform xformPattern = (AffineTransform)xform.clone(); // applies the pattern matrix with scaling removed AffineTransform patternNoScale = patternMatrix.createAffineTransform(); patternNoScale.scale(1 / patternMatrix.getScalingFactorX(), 1 / patternMatrix.getScalingFactorY()); xformPattern.concatenate(patternNoScale); return paint.createContext(cm, deviceBounds, userBounds, xformPattern, hints); } /** * Returns the pattern image in parent stream coordinates. */ private BufferedImage getImage(PageDrawer drawer, PDTilingPattern pattern, PDColorSpace colorSpace, PDColor color, AffineTransform xform, Rectangle2D anchorRect) throws IOException { float width = (float) Math.abs(anchorRect.getWidth()); float height = (float) Math.abs(anchorRect.getHeight()); // device scale transform (i.e. DPI) (see PDFBOX-1466.pdf) Matrix xformMatrix = new Matrix(xform); float xScale = Math.abs(xformMatrix.getScalingFactorX()); float yScale = Math.abs(xformMatrix.getScalingFactorY()); width *= xScale; height *= yScale; int rasterWidth = Math.max(1, ceiling(width)); int rasterHeight = Math.max(1, ceiling(height)); BufferedImage image = new BufferedImage(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = image.createGraphics(); // flip a -ve YStep around its own axis (see gs-bugzilla694385.pdf) if (pattern.getYStep() < 0) { graphics.translate(0, rasterHeight); graphics.scale(1, -1); } // flip a -ve XStep around its own axis if (pattern.getXStep() < 0) { graphics.translate(rasterWidth, 0); graphics.scale(-1, 1); } // device scale transform (i.e. DPI) graphics.scale(xScale, yScale); // apply only the scaling from the pattern transform, doing scaling here improves the // image quality and prevents large scale-down factors from creating huge tiling cells. Matrix newPatternMatrix; newPatternMatrix = Matrix.getScaleInstance( Math.abs(patternMatrix.getScalingFactorX()), Math.abs(patternMatrix.getScalingFactorY())); // move origin to (0,0) newPatternMatrix.concatenate( Matrix.getTranslateInstance(-pattern.getBBox().getLowerLeftX(), -pattern.getBBox().getLowerLeftY())); // render using PageDrawer drawer.drawTilingPattern(graphics, pattern, colorSpace, color, newPatternMatrix); graphics.dispose(); return image; } /** * Returns the closest integer which is larger than the given number. * Uses BigDecimal to avoid floating point error which would cause gaps in the tiling. */ private static int ceiling(double num) { BigDecimal decimal = new BigDecimal(num); decimal = decimal.setScale(5, RoundingMode.CEILING); // 5 decimal places of accuracy return decimal.intValue(); } @Override public int getTransparency() { return Transparency.TRANSLUCENT; } /** * Returns the anchor rectangle, which includes the XStep/YStep and scaling. */ private Rectangle2D getAnchorRect(PDTilingPattern pattern) { float xStep = pattern.getXStep(); if (xStep == 0) { xStep = pattern.getBBox().getWidth(); } float yStep = pattern.getYStep(); if (yStep == 0) { yStep = pattern.getBBox().getHeight(); } float xScale = patternMatrix.getScalingFactorX(); float yScale = patternMatrix.getScalingFactorY(); float width = xStep * xScale; float height = yStep * yScale; if (Math.abs(width * height) > MAXEDGE * MAXEDGE) { // PDFBOX-3653: prevent huge sizes LOG.info("Pattern surface is too large, will be clipped"); LOG.info("width: " + width + ", height: " + height); LOG.info("XStep: " + xStep + ", YStep: " + yStep); LOG.info("bbox: " + pattern.getBBox()); LOG.info("pattern matrix: " + pattern.getMatrix()); LOG.info("concatenated matrix: " + patternMatrix); width = Math.min(MAXEDGE, Math.abs(width)) * Math.signum(width); height = Math.min(MAXEDGE, Math.abs(height)) * Math.signum(height); // TODO better solution needed } // returns the anchor rect with scaling applied PDRectangle anchor = pattern.getBBox(); return new Rectangle2D.Float(anchor.getLowerLeftX() * xScale, anchor.getLowerLeftY() * yScale, width, height); } } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/TilingPaintFactory.java000066400000000000000000000133161320103431700272660ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import java.awt.geom.AffineTransform; import java.io.IOException; import java.util.Map; import java.util.WeakHashMap; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.pdmodel.graphics.color.PDColor; import org.sejda.sambox.pdmodel.graphics.color.PDColorSpace; import org.sejda.sambox.pdmodel.graphics.pattern.PDTilingPattern; import org.sejda.sambox.util.Matrix; /** * Factory class to cache TilingPaint generation. * * @author Tilman Hausherr */ class TilingPaintFactory { private final PageDrawer drawer; private final Map weakCache = new WeakHashMap<>(); TilingPaintFactory(PageDrawer drawer) { this.drawer = drawer; } TilingPaint create(PDTilingPattern pattern, PDColorSpace colorSpace, PDColor color, AffineTransform xform) throws IOException { TilingPaint paint; TilingPaintParameter tilingPaintParameter = new TilingPaintParameter( drawer.getInitialMatrix(), pattern.getCOSObject(), colorSpace, color, xform); paint = weakCache.get(tilingPaintParameter); if (paint == null) { paint = new TilingPaint(drawer, pattern, colorSpace, color, xform); weakCache.put(tilingPaintParameter, paint); } return paint; } // class to characterize a TilingPaint object. It is important that TilingPaint does not // keep any objects from this class, so that the weak cache works. private static class TilingPaintParameter { private final Matrix matrix; private final COSDictionary patternDict; private final PDColorSpace colorSpace; private final PDColor color; private final AffineTransform xform; private TilingPaintParameter(Matrix matrix, COSDictionary patternDict, PDColorSpace colorSpace, PDColor color, AffineTransform xform) { this.matrix = matrix.clone(); this.patternDict = patternDict; this.colorSpace = colorSpace; this.color = color; this.xform = xform; } // this may not catch all equals, but at least those related to one resource dictionary. // it isn't needed to investigate further because matrix or transform would be different anyway. @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final TilingPaintParameter other = (TilingPaintParameter) obj; if (this.matrix != other.matrix && (this.matrix == null || !this.matrix.equals(other.matrix))) { return false; } if (this.patternDict != other.patternDict && (this.patternDict == null || !this.patternDict.equals(other.patternDict))) { return false; } if (this.colorSpace != other.colorSpace && (this.colorSpace == null || !this.colorSpace.equals(other.colorSpace))) { return false; } if (this.color == null && other.color != null) { return false; } if (this.color != null && other.color == null) { return false; } if (this.color != null && this.color.getColorSpace() != other.color.getColorSpace()) { return false; } try { if (this.color != other.color && this.color.toRGB() != other.color.toRGB()) { return false; } } catch (IOException ex) { return false; } return !(this.xform != other.xform && (this.xform == null || !this.xform.equals(other.xform))); } @Override public int hashCode() { int hash = 7; hash = 23 * hash + (this.matrix != null ? this.matrix.hashCode() : 0); hash = 23 * hash + (this.patternDict != null ? this.patternDict.hashCode() : 0); hash = 23 * hash + (this.colorSpace != null ? this.colorSpace.hashCode() : 0); hash = 23 * hash + (this.color != null ? this.color.hashCode() : 0); hash = 23 * hash + (this.xform != null ? this.xform.hashCode() : 0); return hash; } @Override public String toString() { return "TilingPaintParameter{" + "matrix=" + matrix + ", pattern=" + patternDict + ", colorSpace=" + colorSpace + ", color=" + color + ", xform=" + xform + '}'; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/rendering/Type1Glyph2D.java000066400000000000000000000051731320103431700257120ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.rendering; import java.awt.geom.GeneralPath; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.pdmodel.font.PDSimpleFont; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Glyph to GeneralPath conversion for Type 1 PFB and CFF, and TrueType fonts with a 'post' table. */ final class Type1Glyph2D implements Glyph2D { private static final Logger LOG = LoggerFactory.getLogger(Type1Glyph2D.class); private final Map cache = new HashMap(); private final PDSimpleFont font; /** * Constructor. * * @param font PDF Type1 font. */ Type1Glyph2D(PDSimpleFont font) { this.font = font; } @Override public GeneralPath getPathForCharacterCode(int code) { // cache GeneralPath path = cache.get(code); if (path == null) { // fetch try { String name = font.getEncoding().getName(code); if (!font.hasGlyph(name)) { LOG.warn("No glyph for " + code + " (" + name + ") in font " + font.getName()); } // todo: can this happen? should it be encapsulated? path = font.getPath(name); if (path == null) { path = font.getPath(".notdef"); } cache.put(code, path); return path; } catch (IOException e) { // todo: escalate this error? LOG.error("Glyph rendering failed", e); path = new GeneralPath(); } } return path; } @Override public void dispose() { cache.clear(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/text/000077500000000000000000000000001320103431700216545ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/text/PDFMarkedContentExtractor.java000066400000000000000000000212441320103431700275060ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.text; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import org.sejda.sambox.contentstream.operator.markedcontent.BeginMarkedContentSequence; import org.sejda.sambox.contentstream.operator.markedcontent.BeginMarkedContentSequenceWithProperties; import org.sejda.sambox.contentstream.operator.markedcontent.DrawObject; import org.sejda.sambox.contentstream.operator.markedcontent.EndMarkedContentSequence; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; import org.sejda.sambox.pdmodel.graphics.PDXObject; /** * This is an stream engine to extract the marked content of a pdf. * * @author Johannes Koch */ public class PDFMarkedContentExtractor extends PDFTextStreamEngine { private final boolean suppressDuplicateOverlappingText = true; private final List markedContents = new ArrayList<>(); private final Stack currentMarkedContents = new Stack<>(); private final Map> characterListMapping = new HashMap<>(); /** * Instantiate a new PDFTextStripper object. */ public PDFMarkedContentExtractor() throws IOException { this(null); } /** * Constructor. Will apply encoding-specific conversions to the output text. * * @param encoding The encoding that the output will be written in. */ public PDFMarkedContentExtractor(String encoding) throws IOException { addOperator(new BeginMarkedContentSequenceWithProperties()); addOperator(new BeginMarkedContentSequence()); addOperator(new EndMarkedContentSequence()); addOperator(new DrawObject()); // todo: DP - Marked Content Point // todo: MP - Marked Content Point with Properties } /** * This will determine of two floating point numbers are within a specified variance. * * @param first The first number to compare to. * @param second The second number to compare to. * @param variance The allowed variance. */ private boolean within( float first, float second, float variance ) { return second > first - variance && second < first + variance; } public void beginMarkedContentSequence(COSName tag, COSDictionary properties) { PDMarkedContent markedContent = PDMarkedContent.create(tag, properties); if (this.currentMarkedContents.isEmpty()) { this.markedContents.add(markedContent); } else { PDMarkedContent currentMarkedContent = this.currentMarkedContents.peek(); if (currentMarkedContent != null) { currentMarkedContent.addMarkedContent(markedContent); } } this.currentMarkedContents.push(markedContent); } public void endMarkedContentSequence() { if (!this.currentMarkedContents.isEmpty()) { this.currentMarkedContents.pop(); } } public void xobject(PDXObject xobject) { if (!this.currentMarkedContents.isEmpty()) { this.currentMarkedContents.peek().addXObject(xobject); } } /** * This will process a TextPosition object and add the * text to the list of characters on a page. It takes care of * overlapping text. * * @param text The text to process. */ @Override protected void processTextPosition( TextPosition text ) { boolean showCharacter = true; if( this.suppressDuplicateOverlappingText ) { showCharacter = false; String textCharacter = text.getUnicode(); float textX = text.getX(); float textY = text.getY(); List sameTextCharacters = this.characterListMapping.get( textCharacter ); if( sameTextCharacters == null ) { sameTextCharacters = new ArrayList(); this.characterListMapping.put( textCharacter, sameTextCharacters ); } // RDD - Here we compute the value that represents the end of the rendered // text. This value is used to determine whether subsequent text rendered // on the same line overwrites the current text. // // We subtract any positive padding to handle cases where extreme amounts // of padding are applied, then backed off (not sure why this is done, but there // are cases where the padding is on the order of 10x the character width, and // the TJ just backs up to compensate after each character). Also, we subtract // an amount to allow for kerning (a percentage of the width of the last // character). // boolean suppressCharacter = false; float tolerance = (text.getWidth()/textCharacter.length())/3.0f; for (TextPosition sameTextCharacter : sameTextCharacters) { TextPosition character = sameTextCharacter; String charCharacter = character.getUnicode(); float charX = character.getX(); float charY = character.getY(); //only want to suppress if( charCharacter != null && //charCharacter.equals( textCharacter ) && within( charX, textX, tolerance ) && within( charY, textY, tolerance ) ) { suppressCharacter = true; break; } } if( !suppressCharacter ) { sameTextCharacters.add( text ); showCharacter = true; } } if( showCharacter ) { List textList = new ArrayList<>(); /* In the wild, some PDF encoded documents put diacritics (accents on * top of characters) into a separate Tj element. When displaying them * graphically, the two chunks get overlayed. With text output though, * we need to do the overlay. This code recombines the diacritic with * its associated character if the two are consecutive. */ if(textList.isEmpty()) { textList.add(text); } else { /* test if we overlap the previous entry. * Note that we are making an assumption that we need to only look back * one TextPosition to find what we are overlapping. * This may not always be true. */ TextPosition previousTextPosition = textList.get(textList.size() - 1); if(text.isDiacritic() && previousTextPosition.contains(text)) { previousTextPosition.mergeDiacritic(text); } /* If the previous TextPosition was the diacritic, merge it into this * one and remove it from the list. */ else if(previousTextPosition.isDiacritic() && text.contains(previousTextPosition)) { text.mergeDiacritic(previousTextPosition); textList.remove(textList.size()-1); textList.add(text); } else { textList.add(text); } } if (!this.currentMarkedContents.isEmpty()) { this.currentMarkedContents.peek().addText(text); } } } public List getMarkedContents() { return this.markedContents; } } sambox-1.1.19/src/main/java/org/sejda/sambox/text/PDFTextStreamEngine.java000066400000000000000000000320371320103431700263040ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.text; import java.io.IOException; import java.io.InputStream; import org.apache.fontbox.ttf.TrueTypeFont; import org.apache.fontbox.util.BoundingBox; import org.sejda.sambox.contentstream.PDFStreamEngine; import org.sejda.sambox.contentstream.operator.DrawObject; import org.sejda.sambox.contentstream.operator.state.Concatenate; import org.sejda.sambox.contentstream.operator.state.Restore; import org.sejda.sambox.contentstream.operator.state.Save; import org.sejda.sambox.contentstream.operator.state.SetGraphicsStateParameters; import org.sejda.sambox.contentstream.operator.state.SetMatrix; import org.sejda.sambox.contentstream.operator.text.BeginText; import org.sejda.sambox.contentstream.operator.text.EndText; import org.sejda.sambox.contentstream.operator.text.MoveText; import org.sejda.sambox.contentstream.operator.text.MoveTextSetLeading; import org.sejda.sambox.contentstream.operator.text.NextLine; import org.sejda.sambox.contentstream.operator.text.SetCharSpacing; import org.sejda.sambox.contentstream.operator.text.SetFontAndSize; import org.sejda.sambox.contentstream.operator.text.SetTextHorizontalScaling; import org.sejda.sambox.contentstream.operator.text.SetTextLeading; import org.sejda.sambox.contentstream.operator.text.SetTextRenderingMode; import org.sejda.sambox.contentstream.operator.text.SetTextRise; import org.sejda.sambox.contentstream.operator.text.SetWordSpacing; import org.sejda.sambox.contentstream.operator.text.ShowText; import org.sejda.sambox.contentstream.operator.text.ShowTextAdjusted; import org.sejda.sambox.contentstream.operator.text.ShowTextLine; import org.sejda.sambox.contentstream.operator.text.ShowTextLineAndSpace; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.font.PDCIDFont; import org.sejda.sambox.pdmodel.font.PDCIDFontType2; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.pdmodel.font.PDFontDescriptor; import org.sejda.sambox.pdmodel.font.PDSimpleFont; import org.sejda.sambox.pdmodel.font.PDTrueTypeFont; import org.sejda.sambox.pdmodel.font.PDType0Font; import org.sejda.sambox.pdmodel.font.PDType3Font; import org.sejda.sambox.pdmodel.font.encoding.GlyphList; import org.sejda.sambox.pdmodel.graphics.state.PDGraphicsState; import org.sejda.sambox.util.Matrix; import org.sejda.sambox.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * PDFStreamEngine subclass for advanced processing of text via TextPosition. * * @see org.apache.pdfbox.text.TextPosition * @author Ben Litchfield * @author John Hewson */ public class PDFTextStreamEngine extends PDFStreamEngine { private static final Logger LOG = LoggerFactory.getLogger(PDFTextStreamEngine.class); private int pageRotation; private PDRectangle cropBox; private Matrix translateMatrix; private final GlyphList glyphList; /** * Constructor. */ public PDFTextStreamEngine() throws IOException { addOperator(new BeginText()); addOperator(new Concatenate()); addOperator(new DrawObject()); // special text version addOperator(new EndText()); addOperator(new SetGraphicsStateParameters()); addOperator(new Save()); addOperator(new Restore()); addOperator(new NextLine()); addOperator(new SetCharSpacing()); addOperator(new MoveText()); addOperator(new MoveTextSetLeading()); addOperator(new SetFontAndSize()); addOperator(new ShowText()); addOperator(new ShowTextAdjusted()); addOperator(new SetTextLeading()); addOperator(new SetMatrix()); addOperator(new SetTextRenderingMode()); addOperator(new SetTextRise()); addOperator(new SetWordSpacing()); addOperator(new SetTextHorizontalScaling()); addOperator(new ShowTextLine()); addOperator(new ShowTextLineAndSpace()); // load additional glyph list for Unicode mapping String path = "org/sejda/sambox/resources/glyphlist/additional.txt"; InputStream input = GlyphList.class.getClassLoader().getResourceAsStream(path); glyphList = new GlyphList(GlyphList.getAdobeGlyphList(), input); } /** * This will initialise and process the contents of the stream. * * @param page the page to process * @throws java.io.IOException if there is an error accessing the stream. */ @Override public void processPage(PDPage page) throws IOException { this.pageRotation = page.getRotation(); this.cropBox = page.getCropBox(); if (cropBox.getLowerLeftX() == 0 && cropBox.getLowerLeftY() == 0) { translateMatrix = null; } else { // translation matrix for cropbox translateMatrix = Matrix.getTranslateInstance(-cropBox.getLowerLeftX(), -cropBox.getLowerLeftY()); } super.processPage(page); } /** * This method was originally written by Ben Litchfield for PDFStreamEngine. */ @Override protected void showGlyph(Matrix textRenderingMatrix, PDFont font, int code, String unicode, Vector displacement) throws IOException { // // legacy calculations which were previously in PDFStreamEngine // PDGraphicsState state = getGraphicsState(); Matrix ctm = state.getCurrentTransformationMatrix(); float fontSize = state.getTextState().getFontSize(); float horizontalScaling = state.getTextState().getHorizontalScaling() / 100f; Matrix textMatrix = getTextMatrix(); BoundingBox bbox = font.getBoundingBox(); if (bbox.getLowerLeftY() < Short.MIN_VALUE) { // PDFBOX-2158 and PDFBOX-3130 // files by Salmat eSolutions / ClibPDF Library bbox.setLowerLeftY(-(bbox.getLowerLeftY() + 65536)); } // 1/2 the bbox is used as the height todo: why? float glyphHeight = bbox.getHeight() / 2; // sometimes the bbox has very high values, but CapHeight is OK PDFontDescriptor fontDescriptor = font.getFontDescriptor(); if (fontDescriptor != null) { float capHeight = fontDescriptor.getCapHeight(); if (capHeight != 0 && (capHeight < glyphHeight || glyphHeight == 0)) { glyphHeight = capHeight; } } // transformPoint from glyph space -> text space float height; if (font instanceof PDType3Font) { height = font.getFontMatrix().transformPoint(0, glyphHeight).y; } else { height = glyphHeight / 1000; } float displacementX = displacement.getX(); // the sorting algorithm is based on the width of the character. As the displacement // for vertical characters doesn't provide any suitable value for it, we have to // calculate our own if (font.isVertical()) { displacementX = font.getWidth(code) / 1000; // there may be an additional scaling factor for true type fonts TrueTypeFont ttf = null; if (font instanceof PDTrueTypeFont) { ttf = ((PDTrueTypeFont) font).getTrueTypeFont(); } else if (font instanceof PDType0Font) { PDCIDFont cidFont = ((PDType0Font) font).getDescendantFont(); if (cidFont instanceof PDCIDFontType2) { ttf = ((PDCIDFontType2) cidFont).getTrueTypeFont(); } } if (ttf != null && ttf.getUnitsPerEm() != 1000) { displacementX *= 1000f / ttf.getUnitsPerEm(); } } // (modified) combined displacement, this is calculated *without* taking the character // spacing and word spacing into account, due to legacy code in TextStripper float tx = displacementX * fontSize * horizontalScaling; float ty = displacement.getY() * fontSize; // (modified) combined displacement matrix Matrix td = Matrix.getTranslateInstance(tx, ty); // (modified) text rendering matrix Matrix nextTextRenderingMatrix = td.multiply(textMatrix).multiply(ctm); // text space -> device space float nextX = nextTextRenderingMatrix.getTranslateX(); float nextY = nextTextRenderingMatrix.getTranslateY(); // (modified) width and height calculations float dxDisplay = nextX - textRenderingMatrix.getTranslateX(); float dyDisplay = height * textRenderingMatrix.getScalingFactorY(); // // start of the original method // // Note on variable names. There are three different units being used in this code. // Character sizes are given in glyph units, text locations are initially given in text // units, and we want to save the data in display units. The variable names should end with // Text or Disp to represent if the values are in text or disp units (no glyph units are // saved). float glyphSpaceToTextSpaceFactor = 1 / 1000f; if (font instanceof PDType3Font) { glyphSpaceToTextSpaceFactor = font.getFontMatrix().getScaleX(); } float spaceWidthText = 0; try { // to avoid crash as described in PDFBOX-614, see what the space displacement should be spaceWidthText = font.getSpaceWidth() * glyphSpaceToTextSpaceFactor; } catch (Throwable exception) { LOG.warn(exception.getMessage(), exception); } if (spaceWidthText == 0) { spaceWidthText = font.getAverageFontWidth() * glyphSpaceToTextSpaceFactor; // the average space width appears to be higher than necessary so make it smaller spaceWidthText *= .80f; } if (spaceWidthText == 0) { spaceWidthText = 1.0f; // if could not find font, use a generic value } // the space width has to be transformed into display units float spaceWidthDisplay = spaceWidthText * textRenderingMatrix.getScalingFactorX(); // use our additional glyph list for Unicode mapping unicode = font.toUnicode(code, glyphList); // when there is no Unicode mapping available, Acrobat simply coerces the character code // into Unicode, so we do the same. Subclasses of PDFStreamEngine don't necessarily want // this, which is why we leave it until this point in PDFTextStreamEngine. if (unicode == null) { if (font instanceof PDSimpleFont) { char c = (char) code; unicode = new String(new char[] { c }); } else { // Acrobat doesn't seem to coerce composite font's character codes, instead it // skips them. See the "allah2.pdf" TestTextStripper file. // return; // this seems to work better though char c = (char) code; unicode = new String(new char[] { c }); } } // adjust for cropbox if needed Matrix translatedTextRenderingMatrix; if (translateMatrix == null) { translatedTextRenderingMatrix = textRenderingMatrix; } else { translatedTextRenderingMatrix = Matrix.concatenate(translateMatrix, textRenderingMatrix); nextX -= cropBox.getLowerLeftX(); nextY -= cropBox.getLowerLeftY(); } processTextPosition(new TextPosition(pageRotation, cropBox.getWidth(), cropBox.getHeight(), translatedTextRenderingMatrix, nextX, nextY, Math.abs(dyDisplay), dxDisplay, Math.abs(spaceWidthDisplay), unicode, new int[] { code }, font, fontSize, (int) (fontSize * textMatrix.getScalingFactorX()))); } /** * A method provided as an event interface to allow a subclass to perform some specific functionality when text * needs to be processed. * * @param text The text to be processed. */ protected void processTextPosition(TextPosition text) { // subclasses can override to provide specific functionality } } sambox-1.1.19/src/main/java/org/sejda/sambox/text/PDFTextStripper.java000066400000000000000000002127231320103431700255350ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.text; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.text.Normalizer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.SortedSet; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.TreeSet; import java.util.regex.Pattern; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.PDPageTree; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; import org.sejda.sambox.pdmodel.interactive.pagenavigation.PDThreadBead; import org.sejda.sambox.util.BidiUtils; import org.sejda.sambox.util.QuickSort; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class will take a pdf document and strip out all of the text and ignore the formatting and such. Please note; it * is up to clients of this class to verify that a specific user has the correct permissions to extract text from the * PDF document. * * The basic flow of this process is that we get a document and use a series of processXXX() functions that work on * smaller and smaller chunks of the page. Eventually, we fully process each page and then print it. * * @author Ben Litchfield */ public class PDFTextStripper extends PDFTextStreamEngine { private static float defaultIndentThreshold = 2.0f; private static float defaultDropThreshold = 2.5f; private static final boolean useCustomQuickSort; private static final Logger LOG = LoggerFactory.getLogger(PDFTextStripper.class); // enable the ability to set the default indent/drop thresholds // with -D system properties: // pdftextstripper.indent // pdftextstripper.drop static { String strDrop = null, strIndent = null; try { String className = PDFTextStripper.class.getSimpleName().toLowerCase(); String prop = className + ".indent"; strIndent = System.getProperty(prop); prop = className + ".drop"; strDrop = System.getProperty(prop); } catch (SecurityException e) { // PDFBOX-1946 when run in an applet // ignore and use default } if (strIndent != null && strIndent.length() > 0) { try { defaultIndentThreshold = Float.parseFloat(strIndent); } catch (NumberFormatException nfe) { // ignore and use default } } if (strDrop != null && strDrop.length() > 0) { try { defaultDropThreshold = Float.parseFloat(strDrop); } catch (NumberFormatException nfe) { // ignore and use default } } } static { // check if we need to use the custom quicksort algorithm as a // workaround to the PDFBOX-1512 transitivity issue of TextPositionComparator: boolean is16orLess = false; try { String version = System.getProperty("java.specification.version"); StringTokenizer st = new StringTokenizer(version, "."); int majorVersion = Integer.parseInt(st.nextToken()); int minorVersion = 0; if (st.hasMoreTokens()) { minorVersion = Integer.parseInt(st.nextToken()); } is16orLess = majorVersion == 1 && minorVersion <= 6; } catch (SecurityException x) { // when run in an applet ignore and use default // assume 1.7 or higher so that quicksort is used } catch (NumberFormatException nfe) { // should never happen, but if it does, // assume 1.7 or higher so that quicksort is used } useCustomQuickSort = !is16orLess; } /** * The platform's line separator. */ protected final String LINE_SEPARATOR = System.getProperty("line.separator"); private String lineSeparator = LINE_SEPARATOR; private String wordSeparator = " "; private String paragraphStart = ""; private String paragraphEnd = ""; private String pageStart = ""; private String pageEnd = LINE_SEPARATOR; private String articleStart = ""; private String articleEnd = ""; private int currentPageNo = 0; private int startPage = 1; private int endPage = Integer.MAX_VALUE; private PDOutlineItem startBookmark = null; // 1-based bookmark pages private int startBookmarkPageNumber = -1; private int endBookmarkPageNumber = -1; private PDOutlineItem endBookmark = null; private boolean suppressDuplicateOverlappingText = true; private boolean shouldSeparateByBeads = true; private boolean sortByPosition = false; private boolean addMoreFormatting = false; private float indentThreshold = defaultIndentThreshold; private float dropThreshold = defaultDropThreshold; // we will need to estimate where to add spaces, these are used to help guess private float spacingTolerance = .5f; private float averageCharTolerance = .3f; private List beadRectangles = null; /** * The charactersByArticle is used to extract text by article divisions. For example a PDF that has two columns like * a newspaper, we want to extract the first column and then the second column. In this example the PDF would have 2 * beads(or articles), one for each column. The size of the charactersByArticle would be 5, because not all text on * the screen will fall into one of the articles. The five divisions are shown below * * Text before first article first article text text between first article and second article second article text * text after second article * * Most PDFs won't have any beads, so charactersByArticle will contain a single entry. */ protected ArrayList> charactersByArticle = new ArrayList<>(); private Map>> characterListMapping = new HashMap>>(); protected PDDocument document; protected Writer output; /** * True if we started a paragraph but haven't ended it yet. */ private boolean inParagraph; /** * Instantiate a new PDFTextStripper object. * * @throws IOException If there is an error loading the properties. */ public PDFTextStripper() throws IOException { } /** * This will return the text of a document. See writeText.
* NOTE: The document must not be encrypted when coming into this method. * * @param doc The document to get the text from. * @return The text of the PDF document. * @throws IOException if the doc state is invalid or it is encrypted. */ public String getText(PDDocument doc) throws IOException { StringWriter outputStream = new StringWriter(); writeText(doc, outputStream); return outputStream.toString(); } private void resetEngine() { currentPageNo = 0; document = null; if (charactersByArticle != null) { charactersByArticle.clear(); } if (characterListMapping != null) { characterListMapping.clear(); } } /** * This will take a PDDocument and write the text of that document to the print writer. * * @param doc The document to get the data from. * @param outputStream The location to put the text. * * @throws IOException If the doc is in an invalid state. */ public void writeText(PDDocument doc, Writer outputStream) throws IOException { resetEngine(); document = doc; output = outputStream; if (getAddMoreFormatting()) { paragraphEnd = lineSeparator; pageStart = lineSeparator; articleStart = lineSeparator; articleEnd = lineSeparator; } startDocument(document); processPages(document.getPages()); endDocument(document); } /** * This will process all of the pages and the text that is in them. * * @param pages The pages object in the document. * * @throws IOException If there is an error parsing the text. */ protected void processPages(PDPageTree pages) throws IOException { PDPage startBookmarkPage = startBookmark == null ? null : startBookmark.findDestinationPage(document); if (startBookmarkPage != null) { startBookmarkPageNumber = pages.indexOf(startBookmarkPage) + 1; } else { // -1 = undefined startBookmarkPageNumber = -1; } PDPage endBookmarkPage = endBookmark == null ? null : endBookmark.findDestinationPage(document); if (endBookmarkPage != null) { endBookmarkPageNumber = pages.indexOf(endBookmarkPage) + 1; } else { // -1 = undefined endBookmarkPageNumber = -1; } if (startBookmarkPageNumber == -1 && startBookmark != null && endBookmarkPageNumber == -1 && endBookmark != null && startBookmark.getCOSObject() == endBookmark.getCOSObject()) { // this is a special case where both the start and end bookmark // are the same but point to nothing. In this case // we will not extract any text. startBookmarkPageNumber = 0; endBookmarkPageNumber = 0; } for (PDPage page : pages) { currentPageNo++; LOG.trace("Processing page {}", currentPageNo); if (page.hasContents()) { try { processPage(page); } catch (IOException e) { LOG.warn("Unable to extract text from page " + currentPageNo, e); } } } } /** * This method is available for subclasses of this class. It will be called before processing of the document start. * * @param document The PDF document that is being processed. * @throws IOException If an IO error occurs. */ protected void startDocument(PDDocument document) throws IOException { // no default implementation, but available for subclasses } /** * This method is available for subclasses of this class. It will be called after processing of the document * finishes. * * @param document The PDF document that is being processed. * @throws IOException If an IO error occurs. */ protected void endDocument(PDDocument document) throws IOException { // no default implementation, but available for subclasses } /** * This will process the contents of a page. * * @param page The page to process. * * @throws IOException If there is an error processing the page. */ @Override public void processPage(PDPage page) throws IOException { if (currentPageNo >= startPage && currentPageNo <= endPage && (startBookmarkPageNumber == -1 || currentPageNo >= startBookmarkPageNumber) && (endBookmarkPageNumber == -1 || currentPageNo <= endBookmarkPageNumber)) { startPage(page); int numberOfArticleSections = 1; if (shouldSeparateByBeads) { fillBeadRectangles(page); numberOfArticleSections += beadRectangles.size() * 2; } int originalSize = charactersByArticle.size(); charactersByArticle.ensureCapacity(numberOfArticleSections); int lastIndex = Math.max(numberOfArticleSections, originalSize); for (int i = 0; i < lastIndex; i++) { if (i < originalSize) { charactersByArticle.get(i).clear(); } else { if (numberOfArticleSections < originalSize) { charactersByArticle.remove(i); } else { charactersByArticle.add(new ArrayList()); } } } characterListMapping.clear(); super.processPage(page); writePage(); endPage(page); } } private void fillBeadRectangles(PDPage page) { beadRectangles = new ArrayList<>(); for (PDThreadBead bead : page.getThreadBeads()) { if (bead == null) { // can't skip, because of null entry handling in processTextPosition() beadRectangles.add(null); continue; } PDRectangle rect = bead.getRectangle(); // bead rectangle is in PDF coordinates (y=0 is bottom), // glyphs are in image coordinates (y=0 is top), // so we must flip PDRectangle mediaBox = page.getMediaBox(); float upperRightY = mediaBox.getUpperRightY() - rect.getLowerLeftY(); float lowerLeftY = mediaBox.getUpperRightY() - rect.getUpperRightY(); rect.setLowerLeftY(lowerLeftY); rect.setUpperRightY(upperRightY); // adjust for cropbox PDRectangle cropBox = page.getCropBox(); if (cropBox.getLowerLeftX() != 0 || cropBox.getLowerLeftY() != 0) { rect.setLowerLeftX(rect.getLowerLeftX() - cropBox.getLowerLeftX()); rect.setLowerLeftY(rect.getLowerLeftY() - cropBox.getLowerLeftY()); rect.setUpperRightX(rect.getUpperRightX() - cropBox.getLowerLeftX()); rect.setUpperRightY(rect.getUpperRightY() - cropBox.getLowerLeftY()); } beadRectangles.add(rect); } } /** * Start a new article, which is typically defined as a column on a single page (also referred to as a bead). This * assumes that the primary direction of text is left to right. Default implementation is to do nothing. Subclasses * may provide additional information. * * @throws IOException If there is any error writing to the stream. */ protected void startArticle() throws IOException { startArticle(true); } /** * Start a new article, which is typically defined as a column on a single page (also referred to as a bead). * Default implementation is to do nothing. Subclasses may provide additional information. * * @param isLTR true if primary direction of text is left to right. * @throws IOException If there is any error writing to the stream. */ protected void startArticle(boolean isLTR) throws IOException { output.write(getArticleStart()); } /** * End an article. Default implementation is to do nothing. Subclasses may provide additional information. * * @throws IOException If there is any error writing to the stream. */ protected void endArticle() throws IOException { output.write(getArticleEnd()); } /** * Start a new page. Default implementation is to do nothing. Subclasses may provide additional information. * * @param page The page we are about to process. * * @throws IOException If there is any error writing to the stream. */ protected void startPage(PDPage page) throws IOException { // default is to do nothing } /** * End a page. Default implementation is to do nothing. Subclasses may provide additional information. * * @param page The page we are about to process. * * @throws IOException If there is any error writing to the stream. */ protected void endPage(PDPage page) throws IOException { // default is to do nothing } private static final float END_OF_LAST_TEXT_X_RESET_VALUE = -1; private static final float MAX_Y_FOR_LINE_RESET_VALUE = -Float.MAX_VALUE; private static final float EXPECTED_START_OF_NEXT_WORD_X_RESET_VALUE = -Float.MAX_VALUE; private static final float MAX_HEIGHT_FOR_LINE_RESET_VALUE = -1; private static final float MIN_Y_TOP_FOR_LINE_RESET_VALUE = Float.MAX_VALUE; private static final float LAST_WORD_SPACING_RESET_VALUE = -1; /** * This will print the text of the processed page to "output". It will estimate, based on the coordinates of the * text, where newlines and word spacings should be placed. The text will be sorted only if that feature was * enabled. * * @throws IOException If there is an error writing the text. */ protected void writePage() throws IOException { float maxYForLine = MAX_Y_FOR_LINE_RESET_VALUE; float minYTopForLine = MIN_Y_TOP_FOR_LINE_RESET_VALUE; float endOfLastTextX = END_OF_LAST_TEXT_X_RESET_VALUE; float lastWordSpacing = LAST_WORD_SPACING_RESET_VALUE; float maxHeightForLine = MAX_HEIGHT_FOR_LINE_RESET_VALUE; PositionWrapper lastPosition = null; PositionWrapper lastLineStartPosition = null; boolean startOfPage = true; // flag to indicate start of page boolean startOfArticle; if (charactersByArticle.size() > 0) { writePageStart(); } for (List textList : charactersByArticle) { if (getSortByPosition()) { TextPositionComparator comparator = new TextPositionComparator(); // because the TextPositionComparator is not transitive, but // JDK7+ enforces transitivity on comparators, we need to use // a custom quicksort implementation (which is slower, unfortunately). if (useCustomQuickSort) { QuickSort.sort(textList, comparator); } else { Collections.sort(textList, comparator); } } startArticle(); startOfArticle = true; // Now cycle through to print the text. // We queue up a line at a time before we print so that we can convert // the line from presentation form to logical form (if needed). List line = new ArrayList(); Iterator textIter = textList.iterator(); // PDF files don't always store spaces. We will need to guess where we should add // spaces based on the distances between TextPositions. Historically, this was done // based on the size of the space character provided by the font. In general, this // worked but there were cases where it did not work. Calculating the average character // width and using that as a metric works better in some cases but fails in some cases // where the spacing worked. So we use both. NOTE: Adobe reader also fails on some of // these examples. // Keeps track of the previous average character width float previousAveCharWidth = -1; while (textIter.hasNext()) { TextPosition position = textIter.next(); PositionWrapper current = new PositionWrapper(position); String characterValue = position.getUnicode(); // Resets the average character width when we see a change in font // or a change in the font size if (lastPosition != null && (position.getFont() != lastPosition.getTextPosition() .getFont() || position.getFontSize() != lastPosition.getTextPosition().getFontSize())) { previousAveCharWidth = -1; } float positionX; float positionY; float positionWidth; float positionHeight; // If we are sorting, then we need to use the text direction // adjusted coordinates, because they were used in the sorting. if (getSortByPosition()) { positionX = position.getXDirAdj(); positionY = position.getYDirAdj(); positionWidth = position.getWidthDirAdj(); positionHeight = position.getHeightDir(); } else { positionX = position.getX(); positionY = position.getY(); positionWidth = position.getWidth(); positionHeight = position.getHeight(); } // The current amount of characters in a word int wordCharCount = position.getIndividualWidths().length; // Estimate the expected width of the space based on the // space character with some margin. float wordSpacing = position.getWidthOfSpace(); float deltaSpace; if (wordSpacing == 0 || Float.isNaN(wordSpacing)) { deltaSpace = Float.MAX_VALUE; } else { if (lastWordSpacing < 0) { deltaSpace = wordSpacing * getSpacingTolerance(); } else { deltaSpace = (wordSpacing + lastWordSpacing) / 2f * getSpacingTolerance(); } } // Estimate the expected width of the space based on the average character width // with some margin. This calculation does not make a true average (average of // averages) but we found that it gave the best results after numerous experiments. // Based on experiments we also found that .3 worked well. float averageCharWidth; if (previousAveCharWidth < 0) { averageCharWidth = positionWidth / wordCharCount; } else { averageCharWidth = (previousAveCharWidth + positionWidth / wordCharCount) / 2f; } float deltaCharWidth = averageCharWidth * getAverageCharTolerance(); // Compares the values obtained by the average method and the wordSpacing method // and picks the smaller number. float expectedStartOfNextWordX = EXPECTED_START_OF_NEXT_WORD_X_RESET_VALUE; if (endOfLastTextX != END_OF_LAST_TEXT_X_RESET_VALUE) { if (deltaCharWidth > deltaSpace) { expectedStartOfNextWordX = endOfLastTextX + deltaSpace; } else { expectedStartOfNextWordX = endOfLastTextX + deltaCharWidth; } } if (lastPosition != null) { if (startOfArticle) { lastPosition.setArticleStart(); startOfArticle = false; } // RDD - Here we determine whether this text object is on the current // line. We use the lastBaselineFontSize to handle the superscript // case, and the size of the current font to handle the subscript case. // Text must overlap with the last rendered baseline text by at least // a small amount in order to be considered as being on the same line. // XXX BC: In theory, this check should really check if the next char is in // full range seen in this line. This is what I tried to do with minYTopForLine, // but this caused a lot of regression test failures. So, I'm leaving it be for // now if (!overlap(positionY, positionHeight, maxYForLine, maxHeightForLine)) { writeLine(normalize(line)); line.clear(); lastLineStartPosition = handleLineSeparation(current, lastPosition, lastLineStartPosition, maxHeightForLine); expectedStartOfNextWordX = EXPECTED_START_OF_NEXT_WORD_X_RESET_VALUE; maxYForLine = MAX_Y_FOR_LINE_RESET_VALUE; maxHeightForLine = MAX_HEIGHT_FOR_LINE_RESET_VALUE; minYTopForLine = MIN_Y_TOP_FOR_LINE_RESET_VALUE; } // test if our TextPosition starts after a new word would be expected to start if (expectedStartOfNextWordX != EXPECTED_START_OF_NEXT_WORD_X_RESET_VALUE && expectedStartOfNextWordX < positionX && // only bother adding a space if the last character was not a space lastPosition.getTextPosition().getUnicode() != null && !lastPosition.getTextPosition().getUnicode().endsWith(" ")) { line.add(LineItem.getWordSeparator()); } } if (positionY >= maxYForLine) { maxYForLine = positionY; } // RDD - endX is what PDF considers to be the x coordinate of the // end position of the text. We use it in computing our metrics below. endOfLastTextX = positionX + positionWidth; // add it to the list if (characterValue != null) { if (startOfPage && lastPosition == null) { writeParagraphStart();// not sure this is correct for RTL? } line.add(new LineItem(position)); } maxHeightForLine = Math.max(maxHeightForLine, positionHeight); minYTopForLine = Math.min(minYTopForLine, positionY - positionHeight); lastPosition = current; if (startOfPage) { lastPosition.setParagraphStart(); lastPosition.setLineStart(); lastLineStartPosition = lastPosition; startOfPage = false; } lastWordSpacing = wordSpacing; previousAveCharWidth = averageCharWidth; } // print the final line if (line.size() > 0) { writeLine(normalize(line)); writeParagraphEnd(); } endArticle(); } writePageEnd(); } private boolean overlap(float y1, float height1, float y2, float height2) { return within(y1, y2, .1f) || y2 <= y1 && y2 >= y1 - height1 || y1 <= y2 && y1 >= y2 - height2; } /** * Write the line separator value to the output stream. * * @throws IOException If there is a problem writing out the lineseparator to the document. */ protected void writeLineSeparator() throws IOException { output.write(getLineSeparator()); } /** * Write the word separator value to the output stream. * * @throws IOException If there is a problem writing out the wordseparator to the document. */ protected void writeWordSeparator() throws IOException { output.write(getWordSeparator()); } /** * Write the string in TextPosition to the output stream. * * @param text The text to write to the stream. * @throws IOException If there is an error when writing the text. */ protected void writeCharacters(TextPosition text) throws IOException { output.write(text.getUnicode()); } /** * Write a Java string to the output stream. The default implementation will ignore the textPositions * and just calls {@link #writeString(String)}. * * @param text The text to write to the stream. * @param textPositions The TextPositions belonging to the text. * @throws IOException If there is an error when writing the text. */ protected void writeString(String text, List textPositions) throws IOException { writeString(text); } /** * Write a Java string to the output stream. * * @param text The text to write to the stream. * @throws IOException If there is an error when writing the text. */ protected void writeString(String text) throws IOException { output.write(text); } /** * This will determine of two floating point numbers are within a specified variance. * * @param first The first number to compare to. * @param second The second number to compare to. * @param variance The allowed variance. */ private boolean within(float first, float second, float variance) { return second < first + variance && second > first - variance; } /** * This will process a TextPosition object and add the text to the list of characters on a page. It takes care of * overlapping text. * * @param text The text to process. */ @Override protected void processTextPosition(TextPosition text) { boolean showCharacter = true; if (suppressDuplicateOverlappingText) { showCharacter = false; String textCharacter = text.getUnicode(); float textX = text.getX(); float textY = text.getY(); TreeMap> sameTextCharacters = characterListMapping .get(textCharacter); if (sameTextCharacters == null) { sameTextCharacters = new TreeMap>(); characterListMapping.put(textCharacter, sameTextCharacters); } // RDD - Here we compute the value that represents the end of the rendered // text. This value is used to determine whether subsequent text rendered // on the same line overwrites the current text. // // We subtract any positive padding to handle cases where extreme amounts // of padding are applied, then backed off (not sure why this is done, but there // are cases where the padding is on the order of 10x the character width, and // the TJ just backs up to compensate after each character). Also, we subtract // an amount to allow for kerning (a percentage of the width of the last // character). boolean suppressCharacter = false; float tolerance = text.getWidth() / textCharacter.length() / 3.0f; SortedMap> xMatches = sameTextCharacters.subMap(textX - tolerance, textX + tolerance); for (TreeSet xMatch : xMatches.values()) { SortedSet yMatches = xMatch.subSet(textY - tolerance, textY + tolerance); if (!yMatches.isEmpty()) { suppressCharacter = true; break; } } if (!suppressCharacter) { TreeSet ySet = sameTextCharacters.get(textX); if (ySet == null) { ySet = new TreeSet(); sameTextCharacters.put(textX, ySet); } ySet.add(textY); showCharacter = true; } } if (showCharacter) { // if we are showing the character then we need to determine which article it belongs to int foundArticleDivisionIndex = -1; int notFoundButFirstLeftAndAboveArticleDivisionIndex = -1; int notFoundButFirstLeftArticleDivisionIndex = -1; int notFoundButFirstAboveArticleDivisionIndex = -1; float x = text.getX(); float y = text.getY(); if (shouldSeparateByBeads) { for (int i = 0; i < beadRectangles.size() && foundArticleDivisionIndex == -1; i++) { PDRectangle rect = beadRectangles.get(i); if (rect != null) { if (rect.contains(x, y)) { foundArticleDivisionIndex = i * 2 + 1; } else if ((x < rect.getLowerLeftX() || y < rect.getUpperRightY()) && notFoundButFirstLeftAndAboveArticleDivisionIndex == -1) { notFoundButFirstLeftAndAboveArticleDivisionIndex = i * 2; } else if (x < rect.getLowerLeftX() && notFoundButFirstLeftArticleDivisionIndex == -1) { notFoundButFirstLeftArticleDivisionIndex = i * 2; } else if (y < rect.getUpperRightY() && notFoundButFirstAboveArticleDivisionIndex == -1) { notFoundButFirstAboveArticleDivisionIndex = i * 2; } } else { foundArticleDivisionIndex = 0; } } } else { foundArticleDivisionIndex = 0; } int articleDivisionIndex; if (foundArticleDivisionIndex != -1) { articleDivisionIndex = foundArticleDivisionIndex; } else if (notFoundButFirstLeftAndAboveArticleDivisionIndex != -1) { articleDivisionIndex = notFoundButFirstLeftAndAboveArticleDivisionIndex; } else if (notFoundButFirstLeftArticleDivisionIndex != -1) { articleDivisionIndex = notFoundButFirstLeftArticleDivisionIndex; } else if (notFoundButFirstAboveArticleDivisionIndex != -1) { articleDivisionIndex = notFoundButFirstAboveArticleDivisionIndex; } else { articleDivisionIndex = charactersByArticle.size() - 1; } List textList = charactersByArticle.get(articleDivisionIndex); // In the wild, some PDF encoded documents put diacritics (accents on // top of characters) into a separate Tj element. When displaying them // graphically, the two chunks get overlayed. With text output though, // we need to do the overlay. This code recombines the diacritic with // its associated character if the two are consecutive. if (textList.isEmpty()) { textList.add(text); } else { // test if we overlap the previous entry. // Note that we are making an assumption that we need to only look back // one TextPosition to find what we are overlapping. // This may not always be true. */ TextPosition previousTextPosition = textList.get(textList.size() - 1); if (text.isDiacritic() && previousTextPosition.contains(text)) { previousTextPosition.mergeDiacritic(text); } // If the previous TextPosition was the diacritic, merge it into this // one and remove it from the list. else if (previousTextPosition.isDiacritic() && text.contains(previousTextPosition)) { text.mergeDiacritic(previousTextPosition); textList.remove(textList.size() - 1); textList.add(text); } else { textList.add(text); } } } } /** * This is the page that the text extraction will start on. The pages start at page 1. For example in a 5 page PDF * document, if the start page is 1 then all pages will be extracted. If the start page is 4 then pages 4 and 5 will * be extracted. The default value is 1. * * @return Value of property startPage. */ public int getStartPage() { return startPage; } /** * This will set the first page to be extracted by this class. * * @param startPageValue New value of 1-based startPage property. */ public void setStartPage(int startPageValue) { startPage = startPageValue; } /** * This will get the last page that will be extracted. This is inclusive, for example if a 5 page PDF an endPage * value of 5 would extract the entire document, an end page of 2 would extract pages 1 and 2. This defaults to * Integer.MAX_VALUE such that all pages of the pdf will be extracted. * * @return Value of property endPage. */ public int getEndPage() { return endPage; } /** * This will set the last page to be extracted by this class. * * @param endPageValue New value of 1-based endPage property. */ public void setEndPage(int endPageValue) { endPage = endPageValue; } /** * Set the desired line separator for output text. The line.separator system property is used if the line separator * preference is not set explicitly using this method. * * @param separator The desired line separator string. */ public void setLineSeparator(String separator) { lineSeparator = separator; } /** * This will get the line separator. * * @return The desired line separator string. */ public String getLineSeparator() { return lineSeparator; } /** * This will get the word separator. * * @return The desired word separator string. */ public String getWordSeparator() { return wordSeparator; } /** * Set the desired word separator for output text. The PDFBox text extraction algorithm will output a space * character if there is enough space between two words. By default a space character is used. If you need and * accurate count of characters that are found in a PDF document then you might want to set the word separator to * the empty string. * * @param separator The desired page separator string. */ public void setWordSeparator(String separator) { wordSeparator = separator; } /** * @return Returns the suppressDuplicateOverlappingText. */ public boolean getSuppressDuplicateOverlappingText() { return suppressDuplicateOverlappingText; } /** * Get the current page number that is being processed. * * @return A 1 based number representing the current page. */ protected int getCurrentPageNo() { return currentPageNo; } /** * The output stream that is being written to. * * @return The stream that output is being written to. */ protected Writer getOutput() { return output; } /** * Character strings are grouped by articles. It is quite common that there will only be a single article. This * returns a List that contains List objects, the inner lists will contain TextPosition objects. * * @return A double List of TextPositions for all text strings on the page. */ protected List> getCharactersByArticle() { return charactersByArticle; } /** * By default the text stripper will attempt to remove text that overlapps each other. Word paints the same * character several times in order to make it look bold. By setting this to false all text will be extracted, which * means that certain sections will be duplicated, but better performance will be noticed. * * @param suppressDuplicateOverlappingTextValue The suppressDuplicateOverlappingText to set. */ public void setSuppressDuplicateOverlappingText(boolean suppressDuplicateOverlappingTextValue) { suppressDuplicateOverlappingText = suppressDuplicateOverlappingTextValue; } /** * This will tell if the text stripper should separate by beads. * * @return If the text will be grouped by beads. */ public boolean getSeparateByBeads() { return shouldSeparateByBeads; } /** * Set if the text stripper should group the text output by a list of beads. The default value is true! * * @param aShouldSeparateByBeads The new grouping of beads. */ public void setShouldSeparateByBeads(boolean aShouldSeparateByBeads) { shouldSeparateByBeads = aShouldSeparateByBeads; } /** * Get the bookmark where text extraction should end, inclusive. Default is null. * * @return The ending bookmark. */ public PDOutlineItem getEndBookmark() { return endBookmark; } /** * Set the bookmark where the text extraction should stop. * * @param aEndBookmark The ending bookmark. */ public void setEndBookmark(PDOutlineItem aEndBookmark) { endBookmark = aEndBookmark; } /** * Get the bookmark where text extraction should start, inclusive. Default is null. * * @return The starting bookmark. */ public PDOutlineItem getStartBookmark() { return startBookmark; } /** * Set the bookmark where text extraction should start, inclusive. * * @param aStartBookmark The starting bookmark. */ public void setStartBookmark(PDOutlineItem aStartBookmark) { startBookmark = aStartBookmark; } /** * This will tell if the text stripper should add some more text formatting. * * @return true if some more text formatting will be added */ public boolean getAddMoreFormatting() { return addMoreFormatting; } /** * There will some additional text formatting be added if addMoreFormatting is set to true. Default is false. * * @param newAddMoreFormatting Tell PDFBox to add some more text formatting */ public void setAddMoreFormatting(boolean newAddMoreFormatting) { addMoreFormatting = newAddMoreFormatting; } /** * This will tell if the text stripper should sort the text tokens before writing to the stream. * * @return true If the text tokens will be sorted before being written. */ public boolean getSortByPosition() { return sortByPosition; } /** * The order of the text tokens in a PDF file may not be in the same as they appear visually on the screen. For * example, a PDF writer may write out all text by font, so all bold or larger text, then make a second pass and * write out the normal text.
* The default is to not sort by position.
*
* A PDF writer could choose to write each character in a different order. By default PDFBox does not sort * the text tokens before processing them due to performance reasons. * * @param newSortByPosition Tell PDFBox to sort the text positions. */ public void setSortByPosition(boolean newSortByPosition) { sortByPosition = newSortByPosition; } /** * Get the current space width-based tolerance value that is being used to estimate where spaces in text should be * added. Note that the default value for this has been determined from trial and error. * * @return The current tolerance / scaling factor */ public float getSpacingTolerance() { return spacingTolerance; } /** * Set the space width-based tolerance value that is used to estimate where spaces in text should be added. Note * that the default value for this has been determined from trial and error. Setting this value larger will reduce * the number of spaces added. * * @param spacingToleranceValue tolerance / scaling factor to use */ public void setSpacingTolerance(float spacingToleranceValue) { spacingTolerance = spacingToleranceValue; } /** * Get the current character width-based tolerance value that is being used to estimate where spaces in text should * be added. Note that the default value for this has been determined from trial and error. * * @return The current tolerance / scaling factor */ public float getAverageCharTolerance() { return averageCharTolerance; } /** * Set the character width-based tolerance value that is used to estimate where spaces in text should be added. Note * that the default value for this has been determined from trial and error. Setting this value larger will reduce * the number of spaces added. * * @param averageCharToleranceValue average tolerance / scaling factor to use */ public void setAverageCharTolerance(float averageCharToleranceValue) { averageCharTolerance = averageCharToleranceValue; } /** * returns the multiple of whitespace character widths for the current text which the current line start can be * indented from the previous line start beyond which the current line start is considered to be a paragraph start. * * @return the number of whitespace character widths to use when detecting paragraph indents. */ public float getIndentThreshold() { return indentThreshold; } /** * sets the multiple of whitespace character widths for the current text which the current line start can be * indented from the previous line start beyond which the current line start is considered to be a paragraph start. * The default value is 2.0. * * @param indentThresholdValue the number of whitespace character widths to use when detecting paragraph indents. */ public void setIndentThreshold(float indentThresholdValue) { indentThreshold = indentThresholdValue; } /** * the minimum whitespace, as a multiple of the max height of the current characters beyond which the current line * start is considered to be a paragraph start. * * @return the character height multiple for max allowed whitespace between lines in the same paragraph. */ public float getDropThreshold() { return dropThreshold; } /** * sets the minimum whitespace, as a multiple of the max height of the current characters beyond which the current * line start is considered to be a paragraph start. The default value is 2.5. * * @param dropThresholdValue the character height multiple for max allowed whitespace between lines in the same * paragraph. */ public void setDropThreshold(float dropThresholdValue) { dropThreshold = dropThresholdValue; } /** * Returns the string which will be used at the beginning of a paragraph. * * @return the paragraph start string */ public String getParagraphStart() { return paragraphStart; } /** * Sets the string which will be used at the beginning of a paragraph. * * @param s the paragraph start string */ public void setParagraphStart(String s) { paragraphStart = s; } /** * Returns the string which will be used at the end of a paragraph. * * @return the paragraph end string */ public String getParagraphEnd() { return paragraphEnd; } /** * Sets the string which will be used at the end of a paragraph. * * @param s the paragraph end string */ public void setParagraphEnd(String s) { paragraphEnd = s; } /** * Returns the string which will be used at the beginning of a page. * * @return the page start string */ public String getPageStart() { return pageStart; } /** * Sets the string which will be used at the beginning of a page. * * @param pageStartValue the page start string */ public void setPageStart(String pageStartValue) { pageStart = pageStartValue; } /** * Returns the string which will be used at the end of a page. * * @return the page end string */ public String getPageEnd() { return pageEnd; } /** * Sets the string which will be used at the end of a page. * * @param pageEndValue the page end string */ public void setPageEnd(String pageEndValue) { pageEnd = pageEndValue; } /** * Returns the string which will be used at the beginning of an article. * * @return the article start string */ public String getArticleStart() { return articleStart; } /** * Sets the string which will be used at the beginning of an article. * * @param articleStartValue the article start string */ public void setArticleStart(String articleStartValue) { articleStart = articleStartValue; } /** * Returns the string which will be used at the end of an article. * * @return the article end string */ public String getArticleEnd() { return articleEnd; } /** * Sets the string which will be used at the end of an article. * * @param articleEndValue the article end string */ public void setArticleEnd(String articleEndValue) { articleEnd = articleEndValue; } /** * handles the line separator for a new line given the specified current and previous TextPositions. * * @param current the current text position * @param lastPosition the previous text position * @param lastLineStartPosition the last text position that followed a line separator. * @param maxHeightForLine max height for positions since lastLineStartPosition * @return start position of the last line * @throws IOException if something went wrong */ private PositionWrapper handleLineSeparation(PositionWrapper current, PositionWrapper lastPosition, PositionWrapper lastLineStartPosition, float maxHeightForLine) throws IOException { current.setLineStart(); isParagraphSeparation(current, lastPosition, lastLineStartPosition, maxHeightForLine); lastLineStartPosition = current; if (current.isParagraphStart()) { if (lastPosition.isArticleStart()) { if (lastPosition.isLineStart()) { writeLineSeparator(); } writeParagraphStart(); } else { writeLineSeparator(); writeParagraphSeparator(); } } else { writeLineSeparator(); } return lastLineStartPosition; } /** * tests the relationship between the last text position, the current text position and the last text position that * followed a line separator to decide if the gap represents a paragraph separation. This should only be * called for consecutive text positions that first pass the line separation test. *

* This base implementation tests to see if the lastLineStartPosition is null OR if the current vertical position * has dropped below the last text vertical position by at least 2.5 times the current text height OR if the current * horizontal position is indented by at least 2 times the current width of a space character. *

*

* This also attempts to identify text that is indented under a hanging indent. *

*

* This method sets the isParagraphStart and isHangingIndent flags on the current position object. *

* * @param position the current text position. This may have its isParagraphStart or isHangingIndent flags set upon * return. * @param lastPosition the previous text position (should not be null). * @param lastLineStartPosition the last text position that followed a line separator, or null. * @param maxHeightForLine max height for text positions since lasLineStartPosition. */ private void isParagraphSeparation(PositionWrapper position, PositionWrapper lastPosition, PositionWrapper lastLineStartPosition, float maxHeightForLine) { boolean result = false; if (lastLineStartPosition == null) { result = true; } else { float yGap = Math.abs(position.getTextPosition().getYDirAdj() - lastPosition.getTextPosition().getYDirAdj()); float newYVal = multiplyFloat(getDropThreshold(), maxHeightForLine); // do we need to flip this for rtl? float xGap = position.getTextPosition().getXDirAdj() - lastLineStartPosition.getTextPosition().getXDirAdj(); float newXVal = multiplyFloat(getIndentThreshold(), position.getTextPosition().getWidthOfSpace()); float positionWidth = multiplyFloat(0.25f, position.getTextPosition().getWidth()); if (yGap > newYVal) { result = true; } else if (xGap > newXVal) { // text is indented, but try to screen for hanging indent if (!lastLineStartPosition.isParagraphStart()) { result = true; } else { position.setHangingIndent(); } } else if (xGap < -position.getTextPosition().getWidthOfSpace()) { // text is left of previous line. Was it a hanging indent? if (!lastLineStartPosition.isParagraphStart()) { result = true; } } else if (Math.abs(xGap) < positionWidth) { // current horizontal position is within 1/4 a char of the last // linestart. We'll treat them as lined up. if (lastLineStartPosition.isHangingIndent()) { position.setHangingIndent(); } else if (lastLineStartPosition.isParagraphStart()) { // check to see if the previous line looks like // any of a number of standard list item formats Pattern liPattern = matchListItemPattern(lastLineStartPosition); if (liPattern != null) { Pattern currentPattern = matchListItemPattern(position); if (liPattern == currentPattern) { result = true; } } } } } if (result) { position.setParagraphStart(); } } private float multiplyFloat(float value1, float value2) { // multiply 2 floats and truncate the resulting value to 3 decimal places // to avoid wrong results when comparing with another float return Math.round(value1 * value2 * 1000) / 1000f; } /** * writes the paragraph separator string to the output. * * @throws IOException if something went wrong */ protected void writeParagraphSeparator() throws IOException { writeParagraphEnd(); writeParagraphStart(); } /** * Write something (if defined) at the start of a paragraph. * * @throws IOException if something went wrong */ protected void writeParagraphStart() throws IOException { if (inParagraph) { writeParagraphEnd(); inParagraph = false; } output.write(getParagraphStart()); inParagraph = true; } /** * Write something (if defined) at the end of a paragraph. * * @throws IOException if something went wrong */ protected void writeParagraphEnd() throws IOException { if (!inParagraph) { writeParagraphStart(); } output.write(getParagraphEnd()); inParagraph = false; } /** * Write something (if defined) at the start of a page. * * @throws IOException if something went wrong */ protected void writePageStart() throws IOException { output.write(getPageStart()); } /** * Write something (if defined) at the end of a page. * * @throws IOException if something went wrong */ protected void writePageEnd() throws IOException { output.write(getPageEnd()); } /** * returns the list item Pattern object that matches the text at the specified PositionWrapper or null if the text * does not match such a pattern. The list of Patterns tested against is given by the {@link #getListItemPatterns()} * method. To add to the list, simply override that method (if sub-classing) or explicitly supply your own list * using {@link #setListItemPatterns(List)}. * * @param pw position * @return the matching pattern */ private Pattern matchListItemPattern(PositionWrapper pw) { TextPosition tp = pw.getTextPosition(); String txt = tp.getUnicode(); return matchPattern(txt, getListItemPatterns()); } /** * a list of regular expressions that match commonly used list item formats, i.e. bullets, numbers, letters, Roman * numerals, etc. Not meant to be comprehensive. */ private static final String[] LIST_ITEM_EXPRESSIONS = { "\\.", "\\d+\\.", "\\[\\d+\\]", "\\d+\\)", "[A-Z]\\.", "[a-z]\\.", "[A-Z]\\)", "[a-z]\\)", "[IVXL]+\\.", "[ivxl]+\\.", }; private List listOfPatterns = null; /** * use to supply a different set of regular expression patterns for matching list item starts. * * @param patterns list of patterns */ protected void setListItemPatterns(List patterns) { listOfPatterns = patterns; } /** * returns a list of regular expression Patterns representing different common list item formats. For example * numbered items of form: *
    *
  1. some text
  2. *
  3. more text
  4. *
* or *
    *
  • some text
  • *
  • more text
  • *
* etc., all begin with some character pattern. The pattern "\\d+\." (matches "1.", "2.", ...) or "\[\\d+\]" * (matches "[1]", "[2]", ...). *

* This method returns a list of such regular expression Patterns. * * @return a list of Pattern objects. */ protected List getListItemPatterns() { if (listOfPatterns == null) { listOfPatterns = new ArrayList<>(); for (String expression : LIST_ITEM_EXPRESSIONS) { Pattern p = Pattern.compile(expression); listOfPatterns.add(p); } } return listOfPatterns; } /** * iterates over the specified list of Patterns until it finds one that matches the specified string. Then returns * the Pattern. *

* Order of the supplied list of patterns is important as most common patterns should come first. Patterns should be * strict in general, and all will be used with case sensitivity on. *

* * @param string the string to be searched * @param patterns list of patterns * @return matching pattern */ protected static Pattern matchPattern(String string, List patterns) { for (Pattern p : patterns) { if (p.matcher(string).matches()) { return p; } } return null; } /** * Write a list of string containing a whole line of a document. * * @param line a list with the words of the given line * @throws IOException if something went wrong */ private void writeLine(List line) throws IOException { int numberOfStrings = line.size(); for (int i = 0; i < numberOfStrings; i++) { WordWithTextPositions word = line.get(i); writeString(word.getText(), word.getTextPositions()); if (i < numberOfStrings - 1) { writeWordSeparator(); } } } /** * Normalize the given list of TextPositions. * * @param line list of TextPositions * @return a list of strings, one string for every word */ private List normalize(List line) { List normalized = new LinkedList<>(); StringBuilder lineBuilder = new StringBuilder(); List wordPositions = new ArrayList<>(); for (LineItem item : line) { lineBuilder = normalizeAdd(normalized, lineBuilder, wordPositions, item); } if (lineBuilder.length() > 0) { normalized.add(createWord(lineBuilder.toString(), wordPositions)); } return normalized; } /** * Used within {@link #normalize(List, boolean, boolean)} to create a single {@link WordWithTextPositions} entry. */ private WordWithTextPositions createWord(String word, List wordPositions) { return new WordWithTextPositions(normalizeWord(word), wordPositions); } /** * Normalize certain Unicode characters. For example, convert the single "fi" ligature to "f" and "i". Also * normalises Arabic and Hebrew presentation forms. * * @param word Word to normalize * @return Normalized word */ private String normalizeWord(String word) { StringBuilder builder = null; int p = 0; int q = 0; int strLength = word.length(); for (; q < strLength; q++) { // We only normalize if the codepoint is in a given range. // Otherwise, NFKC converts too many things that would cause // confusion. For example, it converts the micro symbol in // extended Latin to the value in the Greek script. We normalize // the Unicode Alphabetic and Arabic A&B Presentation forms. char c = word.charAt(q); if (0xFB00 <= c && c <= 0xFDFF || 0xFE70 <= c && c <= 0xFEFF) { if (builder == null) { builder = new StringBuilder(strLength * 2); } builder.append(word.substring(p, q)); // Some fonts map U+FDF2 differently than the Unicode spec. // They add an extra U+0627 character to compensate. // This removes the extra character for those fonts. if (c == 0xFDF2 && q > 0 && (word.charAt(q - 1) == 0x0627 || word.charAt(q - 1) == 0xFE8D)) { builder.append("\u0644\u0644\u0647"); } else { // Trim because some decompositions have an extra space, such as U+FC5E builder.append(Normalizer .normalize(word.substring(q, q + 1), Normalizer.Form.NFKC).trim()); } p = q + 1; } } if (builder == null) { return BidiUtils.visualToLogical(word); } builder.append(word.substring(p, q)); return BidiUtils.visualToLogical(builder.toString()); } /** * Used within {@link #normalize(List, boolean, boolean)} to handle a {@link TextPosition}. * * @return The StringBuilder that must be used when calling this method. */ private StringBuilder normalizeAdd(List normalized, StringBuilder lineBuilder, List wordPositions, LineItem item) { if (item.isWordSeparator()) { normalized.add( createWord(lineBuilder.toString(), new ArrayList<>(wordPositions))); lineBuilder = new StringBuilder(); wordPositions.clear(); } else { TextPosition text = item.getTextPosition(); lineBuilder.append(text.getUnicode()); wordPositions.add(text); } return lineBuilder; } /** * internal marker class. Used as a place holder in a line of TextPositions. */ private static final class LineItem { public static LineItem WORD_SEPARATOR = new LineItem(); public static LineItem getWordSeparator() { return WORD_SEPARATOR; } private final TextPosition textPosition; private LineItem() { textPosition = null; } LineItem(TextPosition textPosition) { this.textPosition = textPosition; } public TextPosition getTextPosition() { return textPosition; } public boolean isWordSeparator() { return textPosition == null; } } /** * Internal class that maps strings to lists of {@link TextPosition} arrays. Note that the number of entries in that * list may differ from the number of characters in the string due to normalization. * * @author Axel Dörfler */ private static final class WordWithTextPositions { String text; List textPositions; WordWithTextPositions(String word, List positions) { text = word; textPositions = positions; } public String getText() { return text; } public List getTextPositions() { return textPositions; } } /** * wrapper of TextPosition that adds flags to track status as linestart and paragraph start positions. *

* This is implemented as a wrapper since the TextPosition class doesn't provide complete access to its state fields * to subclasses. Also, conceptually TextPosition is immutable while these flags need to be set post-creation so it * makes sense to put these flags in this separate class. *

* * @author m.martinez@ll.mit.edu */ private static final class PositionWrapper { private boolean isLineStart = false; private boolean isParagraphStart = false; private boolean isHangingIndent = false; private boolean isArticleStart = false; private TextPosition position = null; /** * Constructs a PositionWrapper around the specified TextPosition object. * * @param position the text position. */ PositionWrapper(TextPosition position) { this.position = position; } /** * Returns the underlying TextPosition object. * * @return the text position */ public TextPosition getTextPosition() { return position; } public boolean isLineStart() { return isLineStart; } /** * Sets the isLineStart() flag to true. */ public void setLineStart() { this.isLineStart = true; } public boolean isParagraphStart() { return isParagraphStart; } /** * sets the isParagraphStart() flag to true. */ public void setParagraphStart() { this.isParagraphStart = true; } public boolean isArticleStart() { return isArticleStart; } /** * Sets the isArticleStart() flag to true. */ public void setArticleStart() { this.isArticleStart = true; } public boolean isHangingIndent() { return isHangingIndent; } /** * Sets the isHangingIndent() flag to true. */ public void setHangingIndent() { this.isHangingIndent = true; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/text/PDFTextStripperByArea.java000066400000000000000000000120561320103431700266160ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.text; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.sejda.sambox.pdmodel.PDPage; /** * This will extract text from a specified region in the PDF. * * @author Ben Litchfield */ public class PDFTextStripperByArea extends PDFTextStripper { private final List regions = new ArrayList<>(); private final Map regionArea = new HashMap<>(); private final Map>> regionCharacterList = new HashMap<>(); private final Map regionText = new HashMap<>(); /** * Constructor. * * @throws IOException If there is an error loading properties. */ public PDFTextStripperByArea() throws IOException { super.setShouldSeparateByBeads(false); } /** * This method does nothing in this derived class, because beads and regions are incompatible. Beads are ignored * when stripping by area. * * @param aShouldSeparateByBeads The new grouping of beads. */ @Override public final void setShouldSeparateByBeads(boolean aShouldSeparateByBeads) { } /** * Add a new region to group text by. * * @param regionName The name of the region. * @param rect The rectangle area to retrieve the text from. */ public void addRegion(String regionName, Rectangle2D rect) { regions.add(regionName); regionArea.put(regionName, rect); } /** * Delete a region to group text by. If the region does not exist, this method does nothing. * * @param regionName The name of the region to delete. */ public void removeRegion(String regionName) { regions.remove(regionName); regionArea.remove(regionName); } /** * Get the list of regions that have been setup. * * @return A list of java.lang.String objects to identify the region names. */ public List getRegions() { return regions; } /** * Get the text for the region, this should be called after extractRegions(). * * @param regionName The name of the region to get the text from. * @return The text that was identified in that region. */ public String getTextForRegion(String regionName) { StringWriter text = regionText.get(regionName); return text.toString(); } /** * Process the page to extract the region text. * * @param page The page to extract the regions from. * @throws IOException If there is an error while extracting text. */ public void extractRegions(PDPage page) throws IOException { for (String region : regions) { setStartPage(getCurrentPageNo()); setEndPage(getCurrentPageNo()); // reset the stored text for the region so this class // can be reused. ArrayList> regionCharactersByArticle = new ArrayList<>(); regionCharactersByArticle.add(new ArrayList()); regionCharacterList.put(region, regionCharactersByArticle); regionText.put(region, new StringWriter()); } if (page.hasContents()) { processPage(page); } } /** * {@inheritDoc} */ @Override protected void processTextPosition(TextPosition text) { for (String region : regionArea.keySet()) { Rectangle2D rect = regionArea.get(region); if (rect.contains(text.getX(), text.getY())) { charactersByArticle = regionCharacterList.get(region); super.processTextPosition(text); } } } /** * This will print the processed page text to the output stream. * * @throws IOException If there is an error writing the text. */ @Override protected void writePage() throws IOException { for (String region : regionArea.keySet()) { charactersByArticle = regionCharacterList.get(region); output = regionText.get(region); super.writePage(); } } } sambox-1.1.19/src/main/java/org/sejda/sambox/text/TextPosition.java000066400000000000000000000675041320103431700252040ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.text; import java.awt.geom.Rectangle2D; import java.text.Normalizer; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.sejda.sambox.pdmodel.font.PDFont; import org.sejda.sambox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This represents a string and a position on the screen of those characters. * * @author Ben Litchfield */ public class TextPosition { private static final Logger LOG = LoggerFactory.getLogger(TextPosition.class); private static final Map DIACRITICS = createDiacritics(); // text matrix for the start of the text object, coordinates are in display units // and have not been adjusted private final Matrix textMatrix; // ending X and Y coordinates in display units private final float endX; private final float endY; private final float maxHeight; // maximum height of text, in display units private final int rotation; // 0, 90, 180, 270 degrees of page rotation private final float x; private final float y; private final float pageHeight; private final float pageWidth; private final float widthOfSpace; // width of a space, in display units private final int[] charCodes; // internal PDF character codes private final PDFont font; private final float fontSize; private final int fontSizePt; // mutable private float[] widths; private String unicode; private float direction = -1; /** * Constructor. * * @param pageRotation rotation of the page that the text is located in * @param pageWidth width of the page that the text is located in * @param pageHeight height of the page that the text is located in * @param textMatrix text rendering matrix for start of text (in display units) * @param endX x coordinate of the end position * @param endY y coordinate of the end position * @param maxHeight Maximum height of text (in display units) * @param individualWidth The width of the given character/string. (in text units) * @param spaceWidth The width of the space character. (in display units) * @param unicode The string of Unicode characters to be displayed. * @param charCodes An array of the internal PDF character codes for the glyphs in this text. * @param font The current font for this text position. * @param fontSize The new font size. * @param fontSizeInPt The font size in pt units (see {@link #getFontSizeInPt()} for details). */ public TextPosition(int pageRotation, float pageWidth, float pageHeight, Matrix textMatrix, float endX, float endY, float maxHeight, float individualWidth, float spaceWidth, String unicode, int[] charCodes, PDFont font, float fontSize, int fontSizeInPt) { this.textMatrix = textMatrix; this.endX = endX; this.endY = endY; int rotationAngle = pageRotation; this.rotation = rotationAngle; this.maxHeight = maxHeight; this.pageHeight = pageHeight; this.pageWidth = pageWidth; this.widths = new float[] { individualWidth }; this.widthOfSpace = spaceWidth; this.unicode = unicode; this.charCodes = charCodes; this.font = font; this.fontSize = fontSize; this.fontSizePt = fontSizeInPt; x = getXRot(rotationAngle); if (rotationAngle == 0 || rotationAngle == 180) { y = this.pageHeight - getYLowerLeftRot(rotationAngle); } else { y = this.pageWidth - getYLowerLeftRot(rotationAngle); } } // Adds non-decomposing diacritics to the hash with their related combining character. // These are values that the unicode spec claims are equivalent but are not mapped in the form // NFKC normalization method. Determined by going through the Combining Diacritical Marks // section of the Unicode spec and identifying which characters are not mapped to by the // normalization. private static Map createDiacritics() { Map map = new HashMap(31); map.put(0x0060, "\u0300"); map.put(0x02CB, "\u0300"); map.put(0x0027, "\u0301"); map.put(0x02B9, "\u0301"); map.put(0x02CA, "\u0301"); map.put(0x005e, "\u0302"); map.put(0x02C6, "\u0302"); map.put(0x007E, "\u0303"); map.put(0x02C9, "\u0304"); map.put(0x00B0, "\u030A"); map.put(0x02BA, "\u030B"); map.put(0x02C7, "\u030C"); map.put(0x02C8, "\u030D"); map.put(0x0022, "\u030E"); map.put(0x02BB, "\u0312"); map.put(0x02BC, "\u0313"); map.put(0x0486, "\u0313"); map.put(0x055A, "\u0313"); map.put(0x02BD, "\u0314"); map.put(0x0485, "\u0314"); map.put(0x0559, "\u0314"); map.put(0x02D4, "\u031D"); map.put(0x02D5, "\u031E"); map.put(0x02D6, "\u031F"); map.put(0x02D7, "\u0320"); map.put(0x02B2, "\u0321"); map.put(0x02CC, "\u0329"); map.put(0x02B7, "\u032B"); map.put(0x02CD, "\u0331"); map.put(0x005F, "\u0332"); map.put(0x204E, "\u0359"); return map; } /** * Return the string of characters stored in this object. The length can be different than the CharacterCodes length * e.g. if ligatures are used ("fi", "fl", "ffl") where one glyph represents several unicode characters. * * @return The string on the screen. */ public String getUnicode() { return unicode; } /** * Return the internal PDF character codes of the glyphs in this text. * * @return an array of internal PDF character codes */ public int[] getCharacterCodes() { return charCodes; } /** * The matrix containing the starting text position and scaling. Despite the name, it is not the text matrix set by * the "Tm" operator, it is really the effective text rendering matrix (which is dependent on the current * transformation matrix (set by the "cm" operator), the text matrix (set by the "Tm" operator), the font size (set * by the "Tf" operator) and the page cropbox). * * @return The Matrix containing the starting text position */ public Matrix getTextMatrix() { return textMatrix; } /** * Return the direction/orientation of the string in this object based on its text matrix. * * @return The direction of the text (0, 90, 180, or 270) */ public float getDir() { if (direction < 0) { float a = textMatrix.getScaleY(); float b = textMatrix.getShearY(); float c = textMatrix.getShearX(); float d = textMatrix.getScaleX(); // 12 0 left to right // 0 12 if (a > 0 && Math.abs(b) < d && Math.abs(c) < a && d > 0) { direction = 0; } // -12 0 right to left (upside down) // 0 -12 else if (a < 0 && Math.abs(b) < Math.abs(d) && Math.abs(c) < Math.abs(a) && d < 0) { direction = 180; } // 0 12 up // -12 0 else if (Math.abs(a) < Math.abs(c) && b > 0 && c < 0 && Math.abs(d) < b) { direction = 90; } // 0 -12 down // 12 0 else if (Math.abs(a) < c && b < 0 && c > 0 && Math.abs(d) < Math.abs(b)) { direction = 270; } else { direction = 0; } } return direction; } /** * Return the X starting coordinate of the text, adjusted by the given rotation amount. The rotation adjusts where * the 0,0 location is relative to the text. * * @param rotation Rotation to apply (0, 90, 180, or 270). 0 will perform no adjustments. * @return X coordinate */ private float getXRot(float rotation) { if (rotation == 0) { return textMatrix.getTranslateX(); } else if (rotation == 90) { return textMatrix.getTranslateY(); } else if (rotation == 180) { return pageWidth - textMatrix.getTranslateX(); } else if (rotation == 270) { return pageHeight - textMatrix.getTranslateY(); } return 0; } /** * This will get the page rotation adjusted x position of the character. This is adjusted based on page rotation so * that the upper left is 0,0. * * @return The x coordinate of the character. */ public float getX() { return x; } /** * This will get the text direction adjusted x position of the character. This is adjusted based on text direction * so that the first character in that direction is in the upper left at 0,0. * * @return The x coordinate of the text. */ public float getXDirAdj() { return getXRot(getDir()); } /** * This will get the y position of the character with 0,0 in lower left. This will be adjusted by the given * rotation. * * @param rotation Rotation to apply to text to adjust the 0,0 location (0,90,180,270) * @return The y coordinate of the text */ private float getYLowerLeftRot(float rotation) { if (rotation == 0) { return textMatrix.getTranslateY(); } else if (rotation == 90) { return pageWidth - textMatrix.getTranslateX(); } else if (rotation == 180) { return pageHeight - textMatrix.getTranslateY(); } else if (rotation == 270) { return textMatrix.getTranslateX(); } return 0; } /** * This will get the y position of the text, adjusted so that 0,0 is upper left and it is adjusted based on the page * rotation. * * @return The adjusted y coordinate of the character. */ public float getY() { return y; } /** * This will get the y position of the text, adjusted so that 0,0 is upper left and it is adjusted based on the text * direction. * * @return The adjusted y coordinate of the character. */ public float getYDirAdj() { float dir = getDir(); // some PDFBox code assumes that the 0,0 point is in upper left, not lower left if (dir == 0 || dir == 180) { return pageHeight - getYLowerLeftRot(dir); } return pageWidth - getYLowerLeftRot(dir); } /** * Get the length or width of the text, based on a given rotation. * * @param rotation Rotation that was used to determine coordinates (0,90,180,270) * @return Width of text in display units */ private float getWidthRot(float rotation) { if (rotation == 90 || rotation == 270) { return Math.abs(endY - textMatrix.getTranslateY()); } return Math.abs(endX - textMatrix.getTranslateX()); } /** * This will get the width of the string when page rotation adjusted coordinates are used. * * @return The width of the text in display units. */ public float getWidth() { return getWidthRot(rotation); } /** * This will get the width of the string when text direction adjusted coordinates are used. * * @return The width of the text in display units. */ public float getWidthDirAdj() { return getWidthRot(getDir()); } /** * This will get the maximum height of all characters in this string. * * @return The maximum height of all characters in this string. */ public float getHeight() { return maxHeight; } /** * This will get the maximum height of all characters in this string. * * @return The maximum height of all characters in this string. */ public float getHeightDir() { // this is not really a rotation-dependent calculation, but this is defined for symmetry return maxHeight; } /** * This will get the font size that has been set with the "Tf" operator (Set text font and size). When the text is * rendered, it may appear bigger or smaller depending on the current transformation matrix (set by the "cm" * operator) and the text matrix (set by the "Tm" operator). * * @return The font size. */ public float getFontSize() { return fontSize; } /** * This will get the font size in pt. To get this size we have to multiply the font size from {@link #getFontSize() * getFontSize()} with the text matrix (set by the "Tm" operator) horizontal scaling factor and truncate the result * to integer. The actual rendering may appear bigger or smaller depending on the current transformation matrix (set * by the "cm" operator). To get the size in rendering, use {@link #getXScale() getXScale()}. * * @return The font size in pt. */ public float getFontSizeInPt() { return fontSizePt; } /** * This will get the font for the text being drawn. * * @return The font size. */ public PDFont getFont() { return font; } /** * This will get the width of a space character. This is useful for some algorithms such as the text stripper, that * need to know the width of a space character. * * @return The width of a space character. */ public float getWidthOfSpace() { return widthOfSpace; } /** * This will get the X scaling factor. This is dependent on the current transformation matrix (set by the "cm" * operator), the text matrix (set by the "Tm" operator) and the font size (set by the "Tf" operator). * * @return The X scaling factor. */ public float getXScale() { return textMatrix.getScalingFactorX(); } /** * This will get the Y scaling factor. This is dependent on the current transformation matrix (set by the "cm" * operator), the text matrix (set by the "Tm" operator) and the font size (set by the "Tf" operator). * * @return The Y scaling factor. */ public float getYScale() { return textMatrix.getScalingFactorY(); } /** * Get the widths of each individual character. * * @return An array that has the same length as the CharacterCodes array. */ public float[] getIndividualWidths() { return widths; } /** * Determine if this TextPosition logically contains another (i.e. they overlap and should be rendered on top of * each other). * * @param tp2 The other TestPosition to compare against * @return True if tp2 is contained in the bounding box of this text. */ public boolean contains(TextPosition tp2) { double thisXstart = getXDirAdj(); double thisWidth = getWidthDirAdj(); double thisXend = thisXstart + thisWidth; double tp2Xstart = tp2.getXDirAdj(); double tp2Xend = tp2Xstart + tp2.getWidthDirAdj(); // no X overlap at all so return as soon as possible if (tp2Xend <= thisXstart || tp2Xstart >= thisXend) { return false; } // no Y overlap at all so return as soon as possible. Note: 0.0 is in the upper left and // y-coordinate is top of TextPosition double thisYstart = getYDirAdj(); double tp2Ystart = tp2.getYDirAdj(); if (tp2Ystart + tp2.getHeightDir() < thisYstart || tp2Ystart > thisYstart + getHeightDir()) { return false; } // we're going to calculate the percentage of overlap, if its less than a 15% x-coordinate // overlap then we'll return false because its negligible, .15 was determined by trial and // error in the regression test files else if (tp2Xstart > thisXstart && tp2Xend > thisXend) { double overlap = thisXend - tp2Xstart; double overlapPercent = overlap / thisWidth; return overlapPercent > .15; } else if (tp2Xstart < thisXstart && tp2Xend < thisXend) { double overlap = tp2Xend - thisXstart; double overlapPercent = overlap / thisWidth; return overlapPercent > .15; } return true; } /** * Merge a single character TextPosition into the current object. This is to be used only for cases where we have a * diacritic that overlaps an existing TextPosition. In a graphical display, we could overlay them, but for text * extraction we need to merge them. Use the contains() method to test if two objects overlap. * * @param diacritic TextPosition to merge into the current TextPosition. */ public void mergeDiacritic(TextPosition diacritic) { if (diacritic.getUnicode().length() > 1) { return; } float diacXStart = diacritic.getXDirAdj(); float diacXEnd = diacXStart + diacritic.widths[0]; float currCharXStart = getXDirAdj(); int strLen = unicode.length(); boolean wasAdded = false; for (int i = 0; i < strLen && !wasAdded; i++) { if (i >= widths.length) { LOG.info("diacritic " + diacritic.getUnicode() + " on ligature " + unicode + " is not supported yet and is ignored (PDFBOX-2831)"); break; } float currCharXEnd = currCharXStart + widths[i]; // this is the case where there is an overlap of the diacritic character with the // current character and the previous character. If no previous character, just append // the diacritic after the current one if (diacXStart < currCharXStart && diacXEnd <= currCharXEnd) { if (i == 0) { insertDiacritic(i, diacritic); } else { float distanceOverlapping1 = diacXEnd - currCharXStart; float percentage1 = distanceOverlapping1 / widths[i]; float distanceOverlapping2 = currCharXStart - diacXStart; float percentage2 = distanceOverlapping2 / widths[i - 1]; if (percentage1 >= percentage2) { insertDiacritic(i, diacritic); } else { insertDiacritic(i - 1, diacritic); } } wasAdded = true; } // diacritic completely covers this character and therefore we assume that this is the // character the diacritic belongs to else if (diacXStart < currCharXStart && diacXEnd > currCharXEnd) { insertDiacritic(i, diacritic); wasAdded = true; } // otherwise, The diacritic modifies this character because its completely // contained by the character width else if (diacXStart >= currCharXStart && diacXEnd <= currCharXEnd) { insertDiacritic(i, diacritic); wasAdded = true; } // last character in the TextPosition so we add diacritic to the end else if (diacXStart >= currCharXStart && diacXEnd > currCharXEnd && i == strLen - 1) { insertDiacritic(i, diacritic); wasAdded = true; } // couldn't find anything useful so we go to the next character in the TextPosition currCharXStart += widths[i]; } } /** * Inserts the diacritic TextPosition to the str of this TextPosition and updates the widths array to include the * extra character width. * * @param i current character * @param diacritic The diacritic TextPosition */ private void insertDiacritic(int i, TextPosition diacritic) { StringBuilder sb = new StringBuilder(); sb.append(unicode.substring(0, i)); float[] widths2 = new float[widths.length + 1]; System.arraycopy(widths, 0, widths2, 0, i); // Unicode combining diacritics always go after the base character, regardless of whether // the string is in presentation order or logical order sb.append(unicode.charAt(i)); widths2[i] = widths[i]; sb.append(combineDiacritic(diacritic.getUnicode())); widths2[i + 1] = 0; // get the rest of the string sb.append(unicode.substring(i + 1, unicode.length())); System.arraycopy(widths, i + 1, widths2, i + 2, widths.length - i - 1); unicode = sb.toString(); widths = widths2; } /** * Combine the diacritic, for example, convert non-combining diacritic characters to their combining counterparts. * * @param str String to normalize * @return Normalized string */ private String combineDiacritic(String str) { // Unicode contains special combining forms of the diacritic characters which we want to use int codePoint = str.codePointAt(0); // convert the characters not defined in the Unicode spec if (DIACRITICS.containsKey(codePoint)) { return DIACRITICS.get(codePoint); } return Normalizer.normalize(str, Normalizer.Form.NFKC).trim(); } /** * @return True if the current character is a diacritic char. */ public boolean isDiacritic() { String text = this.getUnicode(); if (text.length() != 1) { return false; } if ("ー".equals(text)) { // PDFBOX-3833: ー is not a real diacritic like ¨ or ˆ, it just changes the // pronunciation of the previous sound, and is printed after the previous glyph // http://www.japanesewithanime.com/2017/04/prolonged-sound-mark.html // Ignoring it as diacritic avoids trouble if it slightly overlaps with the next glyph. return false; } int type = Character.getType(text.charAt(0)); return type == Character.NON_SPACING_MARK || type == Character.MODIFIER_SYMBOL || type == Character.MODIFIER_LETTER; } /** * @return true if the text is visible in the page cropbox and not outside its boundaries s */ public boolean isVisible() { return new Rectangle2D.Float(0, 0, pageWidth, pageHeight).contains(getX(), getY()); } /** * Show the string data for this text position. * * @return A human readable form of this object. */ @Override public String toString() { return getUnicode(); } /** * This will get the x coordinate of the end position. This is the unadjusted value passed into the constructor. * * @return The unadjusted x coordinate of the end position */ public float getEndX() { return endX; } /** * This will get the y coordinate of the end position. This is the unadjusted value passed into the constructor. * * @return The unadjusted y coordinate of the end position */ public float getEndY() { return endY; } /** * This will get the rotation of the page that the text is located in. This is the unadjusted value passed into the * constructor. * * @return The unadjusted rotation of the page that the text is located in */ public int getRotation() { return rotation; } /** * This will get the height of the page that the text is located in. This is the unadjusted value passed into the * constructor. * * @return The unadjusted height of the page that the text is located in */ public float getPageHeight() { return pageHeight; } /** * This will get the width of the page that the text is located in. This is the unadjusted value passed into the * constructor. * * @return The unadjusted width of the page that the text is located in */ public float getPageWidth() { return pageWidth; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TextPosition)) { return false; } TextPosition that = (TextPosition) o; if (Float.compare(that.endX, endX) != 0) { return false; } if (Float.compare(that.endY, endY) != 0) { return false; } if (Float.compare(that.maxHeight, maxHeight) != 0) { return false; } if (rotation != that.rotation) { return false; } if (Float.compare(that.x, x) != 0) { return false; } if (Float.compare(that.y, y) != 0) { return false; } if (Float.compare(that.pageHeight, pageHeight) != 0) { return false; } if (Float.compare(that.pageWidth, pageWidth) != 0) { return false; } if (Float.compare(that.widthOfSpace, widthOfSpace) != 0) { return false; } if (Float.compare(that.fontSize, fontSize) != 0) { return false; } if (fontSizePt != that.fontSizePt) { return false; } if (Float.compare(that.direction, direction) != 0) { return false; } if (textMatrix != null ? !textMatrix.equals(that.textMatrix) : that.textMatrix != null) { return false; } if (!Arrays.equals(charCodes, that.charCodes)) { return false; } if (font != null ? !font.equals(that.font) : that.font != null) { return false; } if (!Arrays.equals(widths, that.widths)) { return false; } return unicode != null ? unicode.equals(that.unicode) : that.unicode == null; } @Override public int hashCode() { int result = textMatrix != null ? textMatrix.hashCode() : 0; result = 31 * result + Float.floatToIntBits(endX); result = 31 * result + Float.floatToIntBits(endY); result = 31 * result + Float.floatToIntBits(maxHeight); result = 31 * result + rotation; result = 31 * result + Float.floatToIntBits(x); result = 31 * result + Float.floatToIntBits(y); result = 31 * result + Float.floatToIntBits(pageHeight); result = 31 * result + Float.floatToIntBits(pageWidth); result = 31 * result + Float.floatToIntBits(widthOfSpace); result = 31 * result + Arrays.hashCode(charCodes); result = 31 * result + (font != null ? font.hashCode() : 0); result = 31 * result + Float.floatToIntBits(fontSize); result = 31 * result + fontSizePt; return result; } } sambox-1.1.19/src/main/java/org/sejda/sambox/text/TextPositionComparator.java000066400000000000000000000052221320103431700272210ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.text; import java.util.Comparator; /** * This class is a comparator for TextPosition operators. It handles * pages with text in different directions by grouping the text based * on direction and sorting in that direction. This allows continuous text * in a given direction to be more easily grouped together. * * @author Ben Litchfield */ public class TextPositionComparator implements Comparator { @Override public int compare(TextPosition pos1, TextPosition pos2) { // only compare text that is in the same direction if (pos1.getDir() < pos2.getDir()) { return -1; } else if (pos1.getDir() > pos2.getDir()) { return 1; } // get the text direction adjusted coordinates float x1 = pos1.getXDirAdj(); float x2 = pos2.getXDirAdj(); float pos1YBottom = pos1.getYDirAdj(); float pos2YBottom = pos2.getYDirAdj(); // note that the coordinates have been adjusted so 0,0 is in upper left float pos1YTop = pos1YBottom - pos1.getHeightDir(); float pos2YTop = pos2YBottom - pos2.getHeightDir(); float yDifference = Math.abs(pos1YBottom - pos2YBottom); // we will do a simple tolerance comparison if (yDifference < .1 || pos2YBottom >= pos1YTop && pos2YBottom <= pos1YBottom || pos1YBottom >= pos2YTop && pos1YBottom <= pos2YBottom) { if (x1 < x2) { return -1; } else if (x1 > x2) { return 1; } else { return 0; } } else if (pos1YBottom < pos2YBottom) { return - 1; } else { return 1; } } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/000077500000000000000000000000001320103431700216455ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/util/BidiUtils.java000066400000000000000000000136411320103431700244050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; import static java.util.Objects.nonNull; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.text.Bidi; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * see http://translationtherapy.com/understanding-how-to-work-with-bi-directionality-bidi-text/ and * https://docs.oracle.com/javase/tutorial/2d/text/textlayoutbidirectionaltext.html for some info related to BiDi * contents * * @author Andrea Vacondio * */ public final class BidiUtils { private static final Logger LOG = LoggerFactory.getLogger(BidiUtils.class); private static Map MIRRORING_CHAR_MAP = new HashMap<>(); static { try { try (InputStream input = BidiUtils.class.getClassLoader() .getResourceAsStream("org/sejda/sambox/resources/text/BidiMirroring.txt")) { if (nonNull(input)) { parseBidiFile(input); } else { LOG.warn( "Could not find 'BidiMirroring.txt', mirroring char map will be empty"); } } } catch (IOException e) { LOG.warn("An error occurred while parsing BidiMirroring.txt.", e); } } private BidiUtils() { // hide } /** * Handles the LTR and RTL direction of the given words. The whole implementation stands and falls with the given * word. If the word is a full line, the results will be the best. If the word contains of single words or * characters, the order of the characters in a word or words in a line may wrong, due to RTL and LTR marks and * characters! * * Based on http://www.nesterovsky-bros.com/weblog/2013/07/28/VisualToLogicalConversionInJava.aspx * * @param text The word that shall be processed * @return new word with the correct direction of the containing characters */ public static String visualToLogical(String text) { if (!CharUtils.isBlank(text)) { Bidi bidi = new Bidi(text, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); if (!bidi.isLeftToRight()) { // collect individual bidi information int runCount = bidi.getRunCount(); byte[] levels = new byte[runCount]; Integer[] runs = new Integer[runCount]; for (int i = 0; i < runCount; i++) { levels[i] = (byte) bidi.getRunLevel(i); runs[i] = i; } // reorder individual parts based on their levels Bidi.reorderVisually(levels, 0, runs, 0, runCount); // collect the parts based on the direction within the run StringBuilder result = new StringBuilder(); for (int i = 0; i < runCount; i++) { int index = runs[i]; int start = bidi.getRunStart(index); int end = bidi.getRunLimit(index); int level = levels[index]; if ((level & 1) != 0) { while (--end >= start) { char character = text.charAt(end); if (Character.isMirrored(text.codePointAt(end)) && MIRRORING_CHAR_MAP.containsKey(character)) { result.append(MIRRORING_CHAR_MAP.get(character)); } else { result.append(character); } } } else { result.append(text, start, end); } } return result.toString(); } } return text; } /** * This method parses the bidi file provided as inputstream. * * @param inputStream - The bidi file as inputstream * @throws IOException if any line could not be read by the LineNumberReader */ private static void parseBidiFile(InputStream inputStream) throws IOException { IOUtils.readLines(inputStream, StandardCharsets.UTF_8).stream().map(l -> { int comment = l.indexOf('#'); // ignore comments if (comment != -1) { return l.substring(0, comment); } return l; }).filter(l -> !CharUtils.isBlank(l)).filter(l -> l.length() > 1).forEach(l -> { String[] tokens = l.split(";"); if (tokens.length == 2) { MIRRORING_CHAR_MAP.put((char) Integer.parseInt(tokens[0].trim(), 16), (char) Integer.parseInt(tokens[1].trim(), 16)); } }); } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/CharUtils.java000066400000000000000000000114231320103431700244070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; /** * Utility class providing chars related helper methods * * @author Andrea Vacondio */ public final class CharUtils { public static final byte ASCII_LINE_FEED = 10; public static final byte ASCII_FORM_FEED = 12; public static final byte ASCII_CARRIAGE_RETURN = 13; public static final byte ASCII_BACKSPACE = 8; public static final byte ASCII_HORIZONTAL_TAB = 9; private static final byte ASCII_ZERO = 48; private static final byte ASCII_SEVEN = 55; private static final byte ASCII_NINE = 57; public static final byte ASCII_NULL = 0; public static final byte ASCII_SPACE = 32; private static final byte ASCII_UPPERCASE_A = 65; private static final byte ASCII_UPPERCASE_Z = 90; private static final byte ASCII_LOWERCASE_A = 97; private static final byte ASCII_LOWERCASE_Z = 122; private CharUtils() { // no instance } /** * @param ch The character * @return true if the character terminates a PDF name, otherwise false. */ public static boolean isEndOfName(int ch) { return isWhitespace(ch) || ch == '>' || ch == '<' || ch == '[' || ch == ']' || ch == '/' || ch == ')' || ch == '(' || ch == '%'; } /** * @param c * @return true if the char is end of file */ public static boolean isEOF(int c) { return c == -1; } /** * @param c The character to check against end of line * @return true if the character is a line feed or a carriage return */ public static boolean isEOL(int c) { return isCarriageReturn(c) || isLineFeed(c); } public static boolean isLineFeed(int c) { return ASCII_LINE_FEED == c; } public static boolean isCarriageReturn(int c) { return ASCII_CARRIAGE_RETURN == c; } /** * This will tell if a character is whitespace or not. These values are specified in table 1 (page 12) of ISO * 32000-1:2008. * * @param c The character to check against whitespace * @return true if the character is a whitespace character. */ public static boolean isWhitespace(int c) { return c == ASCII_NULL || c == ASCII_HORIZONTAL_TAB || c == ASCII_FORM_FEED || isEOL(c) || isSpace(c); } public static boolean isNul(int c) { return c == ASCII_NULL; } /** * @param c The character to check against space * @return true if the character is a space character. */ public static boolean isSpace(int c) { return ASCII_SPACE == c; } /** * @param c The character to be checked * @return true if the character is a digit. */ public static boolean isDigit(int c) { return c >= ASCII_ZERO && c <= ASCII_NINE; } /** * @param c The character to be checked * @return true if the character is a letter (case unsensitive). */ public static boolean isLetter(int c) { return (c >= ASCII_UPPERCASE_A && c <= ASCII_UPPERCASE_Z) || (c >= ASCII_LOWERCASE_A && c <= ASCII_LOWERCASE_Z); } /** * @param c The character to be checked * @return true if the character is an octal digit. */ public static boolean isOctalDigit(int c) { return c >= ASCII_ZERO && c <= ASCII_SEVEN; } /** * @param c The character to be checked * @return true if the character is an hex digit. */ public static boolean isHexDigit(int c) { return isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } public static boolean isBlank(final CharSequence cs) { int strLen; if (cs == null || (strLen = cs.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if (Character.isWhitespace(cs.charAt(i)) == false) { return false; } } return true; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/DateConverter.java000066400000000000000000000700641320103431700252640ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.SimpleTimeZone; import java.util.TimeZone; import org.sejda.sambox.cos.COSString; /* * Date format is described in PDF Reference 1.7 section 3.8.2 * (www.adobe.com/devnet/acrobat/pdfs/pdf_reference_1-7.pdf) * and also in PDF 32000-1:2008 * (http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf)) * although the latter inexplicably omits the trailing apostrophe. * * The interpretation of dates without timezones is unclear. * The code below assumes that such dates are in UTC+00 (aka GMT). * This is in keeping with the PDF Reference's assertion that: * numerical fields default to zero values. * However, the Reference does go on to make the cryptic remark: * If no UT information is specified, the relationship of the specified * time to UT is considered to be unknown. Whether or not the time * zone is known, the rest of the date should be specified in local time. * I understand this to refer to _creating_ a pdf date value. That is, * code that can get the wall clock time and cannot get the timezone * should write the wall clock time with a time zone of zero. * When _parsing_ a PDF date, the statement talks about "the rest of the date" * being local time, thus explicitly excluding the use of the local time * for the time zone. */ /** * Converts dates to strings and back using the PDF date standard in section 3.8.2 of PDF Reference 1.7. * * @author Ben Litchfield * @author Fred Hansen * * TODO Move members of this class elsewhere for shared use in pdfbox and xmpbox. */ public final class DateConverter { private DateConverter() { } // milliseconds/1000 = seconds; seconds / 60 = minutes; minutes/60 = hours private static final int MINUTES_PER_HOUR = 60; private static final int SECONDS_PER_MINUTE = 60; private static final int MILLIS_PER_MINUTE = SECONDS_PER_MINUTE * 1000; private static final int MILLIS_PER_HOUR = MINUTES_PER_HOUR * MILLIS_PER_MINUTE; private static final int HALF_DAY = 12 * MINUTES_PER_HOUR * MILLIS_PER_MINUTE, DAY = 2 * HALF_DAY; /* * The Date format is supposed to be the PDF_DATE_FORMAT, but other forms appear. These lists offer alternatives to * be tried if parseBigEndianDate fails. * * The time zone offset generally trails the date string, so it is processed separately with parseTZoffset. (This * does not preclude having time zones in the elements below; one does.) * * Alas, SimpleDateFormat is badly non-reentrant -- it modifies its calendar field (PDFBox-402), so these lists are * strings to create SimpleDate format as needed. * * Some past entries have been elided because they duplicate existing entries. See the API for SimpleDateFormat, * which says "For parsing, the number of pattern letters is ignored unless it's needed to separate two adjacent * fields." * * toCalendar(String, String[]) tests to see that the entire input text has been consumed. Therefore the ordering of * formats is important. If one format begins with the entirety of another, the longer must precede the other in the * list. * * HH is for 0-23 hours and hh for 1-12 hours; an "a" field must follow "hh" Where year is yy, four digit years are * accepted and two digit years are converted to four digits in the range [thisyear-79...thisyear+20] */ private static final String[] ALPHA_START_FORMATS = { "EEEE, dd MMM yy hh:mm:ss a", "EEEE, MMM dd, yy hh:mm:ss a", "EEEE, MMM dd, yy 'at' hh:mma", // Acrobat Net Distiller 1.0 for Windows "EEEE, MMM dd, yy", // Acrobat Distiller 1.0.2 for Macintosh && PDFBOX-465 "EEEE MMM dd, yy HH:mm:ss", // ECMP5 "EEEE MMM dd HH:mm:ss z yy", // GNU Ghostscript 7.0.7 "EEEE MMM dd HH:mm:ss yy", // GNU Ghostscript 7.0.7 variant }; private static final String[] DIGIT_START_FORMATS = { "dd MMM yy HH:mm:ss", // for 26 May 2000 11:25:00 "dd MMM yy HH:mm", // for 26 May 2000 11:25 "yyyy MMM d", // ambiguity resolved only by omitting time "yyyymmddhh:mm:ss", // test case "200712172:2:3" "H:m M/d/yy", // test case "9:47 5/12/2008" "M/d/yy HH:mm:ss", "M/d/yy HH:mm", "M/d/yy", // proposed rule that is unreachable due to "dd MMM yy HH:mm:ss" // "yyyy MMM d HH:mm:ss", // rules made unreachable by "M/d/yy HH:mm:ss" "M/d/yy HH:mm" "M/d/yy", // (incoming digit strings do not mark themselves as y, m, or d!) // "d/MM/yyyy HH:mm:ss", // PDFBOX-164 and PDFBOX-170 // "M/dd/yyyy hh:mm:ss", // "MM/d/yyyy hh:mm:ss", // "M/d/yyyy HH:mm:ss", // "M/dd/yyyy", // "MM/d/yyyy", // "M/d/yyyy", // "M/d/yyyy HH:mm:ss", // "M/d/yy HH:mm:ss", // subsumed by big-endian parse // "yyyy-MM-dd'T'HH:mm:ss", // "yyyy-MM-dd'T'HH:mm:ss", // "yyyymmdd hh:mm:ss", // "yyyymmdd", // "yyyymmddX''00''", // covers 24 cases // (orignally the above ended with '+00''00'''; // the first apostrophe quoted the plus, // '' mapped to a single ', and the ''' was invalid) }; /** * Converts a Calendar to a string formatted as: D:yyyyMMddHHmmss#hh'mm' where # is Z, +, or -. * * @param cal The date to convert to a string. May be null. The DST_OFFSET is included when computing the output * time zone. * * @return The date as a String to be used in a PDF document, or null if the cal value is null */ public static String toString(Calendar cal) { if (cal == null) { return null; } String offset = formatTZoffset(cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET), "'"); return String.format(Locale.US, "D:" + "%1$4tY%1$2tm%1$2td" // yyyyMMdd + "%1$2tH%1$2tM%1$2tS" // HHmmss + "%2$s" // time zone + "'", // trailing apostrophe cal, offset); } /** * Converts the date to ISO 8601 string format: yyyy-mm-ddThh:MM:ss#hh:mm (where '#" is '+' or '-'). * * @param cal The date to convert. Must not be null. The DST_OFFSET is included in the output value. * * @return The date represented as an ISO 8601 string. */ public static String toISO8601(Calendar cal) { String offset = formatTZoffset(cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET), ":"); return String.format(Locale.US, "%1$4tY" // yyyy + "-%1$2tm" // -mm (%tm adds one to cal month value) + "-%1$2td" // -dd (%tm adds one to cal month value) + "T" // T + "%1$2tH:%1$2tM:%1$2tS" // HHmmss + "%2$s", // time zone cal, offset); } /* * Constrain a timezone offset to the range [-14:00 thru +14:00]. by adding or subtracting multiples of a full day. */ private static int restrainTZoffset(long proposedOffset) { if (proposedOffset <= 14 * MILLIS_PER_HOUR && proposedOffset >= -14 * MILLIS_PER_HOUR) { // https://www.w3.org/TR/xmlschema-2/#dateTime-timezones // Timezones between 14:00 and -14:00 are valid return (int) proposedOffset; } // Constrain a timezone offset to the range [-11:59 thru +12:00]. proposedOffset = ((proposedOffset + HALF_DAY) % DAY + DAY) % DAY; if (proposedOffset == 0) { return HALF_DAY; } // 0 <= proposedOffset < DAY proposedOffset = (proposedOffset - HALF_DAY) % HALF_DAY; // -HALF_DAY < proposedOffset < HALF_DAY return (int) proposedOffset; } /* * Formats a time zone offset as #hh^mm where # is + or -, hh is hours, ^ is a separator, and mm is minutes. Any * separator may be specified by the second argument; the usual values are ":" (ISO 8601), "" (RFC 822), and "'" * (PDF). The returned value is constrained to the range -11:59 ... 11:59. For offset of 0 millis, the String * returned is "+00^00", never "Z". To get a "general" offset in form GMT#hh:mm, write * "GMT"+DateConverter.formatTZoffset(offset, ":"); * * Take thought in choosing the source for the millis value. It can come from calendarValue.getTimeZone() or from * calendarValue.get(Calendar.ZONE_OFFSET). If a TimeZone was created from a valid time zone ID, then it may have a * daylight savings rule. (As of July 4, 2013, the data base at http://www.iana.org/time-zones recognized 629 time * zone regions. But a TimeZone created as new SimpleTimeZone(millisOffset, "ID"), will not have a daylight savings * rule. (Not even if there is a known time zone with the given ID. To get the TimeZone named "xDT" with its DST * rule, use an ID of EST5EDT, CST6CDT, MST7MDT, or PST8PDT. * * When parsing PDF dates, the incoming values DOES NOT have a TIMEZONE value. At most it has an OFFSET value like * -04'00'. It is generally impossible to determine what TIMEZONE corresponds to a given OFFSET. If the date is in * the summer when daylight savings is in effect, an offset of -0400 might correspond to any one of the 38 regions * (of 53) with standard time offset -0400 and no daylight saving. Or it might correspond to any one of the 31 * regions (out of 43) that observe daylight savings and have standard time offset of -0500. * * If a Calendar has not been assigned a TimeZone with setTimeZone(), it will have by default the local TIMEZONE, * not just the OFFSET. In the USA, this TimeZone will have a daylight savings rule. * * The offset assigned with calVal.set(Calendar.ZONE_OFFSET) differs from the offset in the TimeZone set by * Calendar.setTimeZone(). Example: Suppose my local TimeZone is America/New_York. It has an offset of -05'00'. And * suppose I set a GregorianCalendar's ZONE_OFFSET to -07'00' calVal = new GregorianCalendar(); // TimeZone is the * local default calVal.set(Calendar.ZONE_OFFSET, -7* MILLIS_PER_HOUR); Four different offsets can be computed from * calVal: calVal.get(Calendar.ZONE_OFFSET) => -07:00 calVal.get(Calendar.ZONE_OFFSET) + * calVal.get(Calendar.DST_OFFSET) => -06:00 calVal.getTimeZone().getRawOffset() => -05:00 * calVal.getTimeZone().getOffset(calVal.getTimeInMillis()) => -04:00 * * Which is correct??? I dunno, though setTimeZone() does seem to affect ZONE_OFFSET, and not vice versa. One cannot * even test whether TimeZone or ZONE_OFFSET has been set; both have been set by initialization code. TimeZone is * initialized to the local default time zone and ZONE_OFFSET is set from it. * * My choice in this DateConverter class has been to set the initial TimeZone of a GregorianCalendar to GMT. * Thereafter the TimeZone is modified with {@link #adjustTimeZoneNicely}. * * package-private for testing */ static String formatTZoffset(long millis, String sep) { SimpleDateFormat sdf = new SimpleDateFormat("Z"); // #hhmm sdf.setTimeZone(new SimpleTimeZone(restrainTZoffset(millis), "unknown")); String tz = sdf.format(new Date()); return tz.substring(0, 3) + sep + tz.substring(3); } /* * Parses an integer from a string, starting at and advancing a ParsePosition. Returns The integer that was at the * given parse position, or the remedy value if no digits were found. * * The ParsePosition will be incremented by the number of digits found, but no more than maxlen. That is, the * ParsePosition will advance across at most maxlen initial digits in text. The error index is ignored and * unchanged. * * maxlen is the maximum length of the integer to parse, usually 2, but 4 for year fields. If the field of length * maxlen begins with a digit, but contains a non-digit, no error is signaled and the integer value is returned. */ private static int parseTimeField(String text, ParsePosition where, int maxlen, int remedy) { if (text == null) { return remedy; } // it would seem that DecimalFormat.parse() would be simpler; // but that class blithely ignores setMaximumIntegerDigits int retval = 0; int index = where.getIndex(); int limit = index + Math.min(maxlen, text.length() - index); for (; index < limit; index++) { // convert digit to integer int cval = text.charAt(index) - '0'; // test to see if we got a digit if (cval < 0 || cval > 9) { // no digit at index break; } // append the digit to the return value retval = retval * 10 + cval; } if (index == where.getIndex()) { return remedy; } where.setIndex(index); return retval; } /* * Advances the ParsePosition past any and all the characters that match those in the optionals list. In particular, * a space will skip all spaces. * * The start value is incremented by the number of optionals found. The error index is ignored and unchanged. * * Returns the last non-space character passed over (even if space is not in the optionals list.) */ private static char skipOptionals(String text, ParsePosition where, String optionals) { char retval = ' ', currch; while (text != null && where.getIndex() < text.length() && optionals.indexOf((currch = text.charAt(where.getIndex()))) >= 0) { retval = (currch != ' ') ? currch : retval; where.setIndex(where.getIndex() + 1); } return retval; } /* * If the victim string is at the given position in the text, this method advances the position past that string. * * `where` is the initial position to look at. After return, this will have been incremented by the length of the * victim if it was found. The error index is ignored and unchanged. */ private static boolean skipString(String text, String victim, ParsePosition where) { if (text.startsWith(victim, where.getIndex())) { where.setIndex(where.getIndex() + victim.length()); return true; } return false; } /* * Construct a new GregorianCalendar and set defaults. Locale is ENGLISH. TimeZone is "UTC" (zero offset and no * DST). Parsing is NOT lenient. Milliseconds are zero. * * package-private for testing */ static GregorianCalendar newGreg() { GregorianCalendar retCal = new GregorianCalendar(Locale.ENGLISH); retCal.setTimeZone(new SimpleTimeZone(0, "UTC")); retCal.setLenient(false); retCal.set(Calendar.MILLISECOND, 0); return retCal; } /* * Install a TimeZone on a GregorianCalendar without changing the hours value. A plain * GregorianCalendat.setTimeZone() adjusts the Calendar.HOUR value to compensate. This is *BAD* (not to say *EVIL*) * when we have already set the time. */ private static void adjustTimeZoneNicely(GregorianCalendar cal, TimeZone tz) { cal.setTimeZone(tz); int offset = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / MILLIS_PER_MINUTE; cal.add(Calendar.MINUTE, -offset); } /* * Parses the end of a date string for a time zone and, if one is found, sets the time zone of the * GregorianCalendar. Otherwise the calendar time zone is unchanged. * * The text is parsed as (Z|GMT|UTC)? [+- ]* h [': ]? m '? where the leading String is optional, h is two digits by * default, but may be a single digit if followed by one of space, apostrophe, colon, or the end of string. * Similarly, m is one or two digits. This scheme accepts the format of PDF, RFC 822, and ISO8601. If none of these * applies (as for a time zone name), we try TimeZone.getTimeZone(). * * Scanning begins at where.index. After success, the returned index is that of the next character after the * recognized string. * * package-private for testing */ static boolean parseTZoffset(String text, GregorianCalendar cal, ParsePosition initialWhere) { ParsePosition where = new ParsePosition(initialWhere.getIndex()); TimeZone tz = new SimpleTimeZone(0, "GMT"); int tzHours, tzMin; char sign = skipOptionals(text, where, "Z+- "); boolean hadGMT = (sign == 'Z' || skipString(text, "GMT", where) || skipString(text, "UTC", where)); sign = (!hadGMT) ? sign : skipOptionals(text, where, "+- "); tzHours = parseTimeField(text, where, 2, -999); skipOptionals(text, where, "\': "); tzMin = parseTimeField(text, where, 2, 0); skipOptionals(text, where, "\' "); if (tzHours != -999) { // we parsed a time zone in default format int hrSign = (sign == '-' ? -1 : 1); tz.setRawOffset(restrainTZoffset( hrSign * (tzHours * MILLIS_PER_HOUR + tzMin * (long) MILLIS_PER_MINUTE))); updateZoneId(tz); } else if (!hadGMT) { // try to process as a name; "GMT" or "UTC" has already been processed String tzText = text.substring(initialWhere.getIndex()).trim(); tz = TimeZone.getTimeZone(tzText); // getTimeZone returns "GMT" for unknown ids if ("GMT".equals(tz.getID())) { // no timezone in text, cal amd initialWhere are unchanged return false; } else { // we got a tz by name; use it where.setIndex(text.length()); } } adjustTimeZoneNicely(cal, tz); initialWhere.setIndex(where.getIndex()); return true; } /** * Update the zone ID based on the raw offset. This is either GMT, GMT+hh:mm or GMT-hh:mm, where n is between 1 and * 14. The highest negative hour is -14, the highest positive hour is 12. Zones that don't fit in this schema are * set to zone ID "unknown". * * @param tz the time zone to update. */ private static void updateZoneId(TimeZone tz) { int offset = tz.getRawOffset(); char pm = '+'; if (offset < 0) { pm = '-'; offset = -offset; } int hh = offset / 3600000; int mm = offset % 3600000 / 60000; if (offset == 0) { tz.setID("GMT"); } else if (pm == '+' && hh <= 12) { tz.setID(String.format(Locale.US, "GMT+%02d:%02d", hh, mm)); } else if (pm == '-' && hh <= 14) { tz.setID(String.format(Locale.US, "GMT-%02d:%02d", hh, mm)); } else { tz.setID("unknown"); } } /* * Parses a big-endian date: year month day hour min sec. The year must be four digits. Other fields may be adjacent * and delimited by length or they may follow appropriate delimiters. year [ -/]* month [ -/]* dayofmonth [ T]* hour * [:] min [:] sec [.secFraction] If any numeric field is omitted, all following fields must also be omitted. No * time zone is processed. * * Ambiguous dates can produce unexpected results. For example: 1970 12 23:08 will parse as 1970 December 23 * 00:08:00 * * The parse begins at `where, on return the index is advanced to just beyond the last character processed. The * error index is ignored and unchanged. */ private static GregorianCalendar parseBigEndianDate(String text, ParsePosition initialWhere) { ParsePosition where = new ParsePosition(initialWhere.getIndex()); int year = parseTimeField(text, where, 4, 0); if (where.getIndex() != 4 + initialWhere.getIndex()) { return null; } skipOptionals(text, where, "/- "); int month = parseTimeField(text, where, 2, 1) - 1; // Calendar months are 0...11 skipOptionals(text, where, "/- "); int day = parseTimeField(text, where, 2, 1); skipOptionals(text, where, " T"); int hour = parseTimeField(text, where, 2, 0); skipOptionals(text, where, ": "); int minute = parseTimeField(text, where, 2, 0); skipOptionals(text, where, ": "); int second = parseTimeField(text, where, 2, 0); char nextC = skipOptionals(text, where, "."); if (nextC == '.') { // fractions of a second: skip upto 19 digits parseTimeField(text, where, 19, 0); } GregorianCalendar dest = newGreg(); try { dest.set(year, month, day, hour, minute, second); // trigger limit tests dest.getTimeInMillis(); } catch (IllegalArgumentException ill) { return null; } initialWhere.setIndex(where.getIndex()); skipOptionals(text, initialWhere, " "); // dest has at least a year value return dest; } /* * See if text can be parsed as a date according to any of a list of formats. The time zone may be included as part * of the format, or omitted in favor of later testing for a trailing time zone. * * The parse starts at `where`, upon return it will have been incremented to refer to the next non-space character * after the date. If no date was found, the value is unchanged. The error index is ignored and unchanged. * * If there is a failure to find a date, or the GregorianCalendar for the date that was found. Unless a time zone * was part of the format, the time zone will be GMT+0 */ private static GregorianCalendar parseSimpleDate(String text, String[] fmts, ParsePosition initialWhere) { for (String fmt : fmts) { ParsePosition where = new ParsePosition(initialWhere.getIndex()); SimpleDateFormat sdf = new SimpleDateFormat(fmt, Locale.ENGLISH); GregorianCalendar retCal = newGreg(); sdf.setCalendar(retCal); if (sdf.parse(text, where) != null) { initialWhere.setIndex(where.getIndex()); skipOptionals(text, initialWhere, " "); return retCal; } } return null; } /* * Parses a String to see if it begins with a date, and if so, returns that date. The date must be strictly * correct--no field may exceed the appropriate limit. (That is, the Calendar has setLenient(false).) Skips initial * spaces, but does NOT check for "D:" * * The scan first tries parseBigEndianDate and parseTZoffset and then tries parseSimpleDate with appropriate * formats, again followed by parseTZoffset. If at any stage the entire text is consumed, that date value is * returned immediately. Otherwise the date that consumes the longest initial part of the text is returned. * * - PDF format dates are among those recognized by parseBigEndianDate. - The formats tried are alphaStartFormats or * digitStartFormat and any listed in the value of moreFmts. */ private static Calendar parseDate(String text, ParsePosition initialWhere) { if (text == null || text.isEmpty()) { return null; } // remember longestr date string int longestLen = -999999; // theorem: the above value will never be used // proof: longestLen is only used if longestDate is not null GregorianCalendar longestDate = null; // null says no date found yet int whereLen; // tempcopy of where.getIndex() ParsePosition where = new ParsePosition(initialWhere.getIndex()); // check for null (throws exception) and trim off surrounding spaces skipOptionals(text, where, " "); int startPosition = where.getIndex(); // try big-endian parse GregorianCalendar retCal = parseBigEndianDate(text, where); // check for success and a timezone if (retCal != null && (where.getIndex() == text.length() || parseTZoffset(text, retCal, where))) { // if text is fully consumed, return the date else remember it and its length whereLen = where.getIndex(); if (whereLen == text.length()) { initialWhere.setIndex(whereLen); return retCal; } longestLen = whereLen; longestDate = retCal; } // try one of the sets of standard formats where.setIndex(startPosition); String[] formats = Character.isDigit(text.charAt(startPosition)) ? DIGIT_START_FORMATS : ALPHA_START_FORMATS; retCal = parseSimpleDate(text, formats, where); // check for success and a timezone if (retCal != null && (where.getIndex() == text.length() || parseTZoffset(text, retCal, where))) { // if text is fully consumed, return the date else remember it and its length whereLen = where.getIndex(); if (whereLen == text.length()) { initialWhere.setIndex(whereLen); return retCal; } if (whereLen > longestLen) { longestLen = whereLen; longestDate = retCal; } } if (longestDate != null) { initialWhere.setIndex(longestLen); return longestDate; } return retCal; } /** * Returns the Calendar for a given COS string containing a date, or {@code null} if it cannot be parsed. * * The returned value will have 0 for DST_OFFSET. * * @param text A COS string containing a date. * @return The Calendar that the text string represents, or {@code null} if it cannot be parsed. */ public static Calendar toCalendar(COSString text) { if (text == null) { return null; } return toCalendar(text.getString()); } /** * Returns the Calendar for a given string containing a date, or {@code null} if it cannot be parsed. * * The returned value will have 0 for DST_OFFSET. * * @param text A COS string containing a date. * @return The Calendar that the text string represents, or {@code null} if it cannot be parsed. */ public static Calendar toCalendar(String text) { if (text == null || text.trim().isEmpty()) { return null; } ParsePosition where = new ParsePosition(0); skipOptionals(text, where, " "); skipString(text, "D:", where); Calendar calendar = parseDate(text, where); if (calendar == null || where.getIndex() != text.length()) { // the date string is invalid return null; } return calendar; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/Hex.java000066400000000000000000000130011320103431700232270ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; import java.io.IOException; import java.io.OutputStream; /** * Utility functions for hex encoding. * * @author John Hewson */ public final class Hex { /** * for hex conversion. * * https://stackoverflow.com/questions/2817752/java-code-to-convert-byte-to-hexadecimal * */ private static final byte[] HEX_BYTES = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; private Hex() {} /** * Returns a hex string of the given byte. */ public static String getString(byte b) { char[] chars = new char[] { HEX_CHARS[getHighNibble(b)], HEX_CHARS[getLowNibble(b)] }; return new String(chars); } /** * Returns a hex string of the given byte array. */ public static String getString(byte[] bytes) { StringBuilder string = new StringBuilder(bytes.length * 2); for (byte b : bytes) { string.append(HEX_CHARS[getHighNibble(b)]).append(HEX_CHARS[getLowNibble(b)]); } return string.toString(); } /** * Returns the bytes corresponding to the ASCII hex encoding of the given byte. */ public static byte[] getBytes(byte b) { return new byte[] { HEX_BYTES[getHighNibble(b)], HEX_BYTES[getLowNibble(b)] }; } /** * Returns the bytes corresponding to the ASCII hex encoding of the given bytes. */ public static byte[] getBytes(byte[] bytes) { byte[] asciiBytes = new byte[bytes.length * 2]; for (int i = 0; i < bytes.length; i++) { asciiBytes[i * 2] = HEX_BYTES[getHighNibble(bytes[i])]; asciiBytes[i * 2 + 1] = HEX_BYTES[getLowNibble(bytes[i])]; } return asciiBytes; } /** * Returns the characters corresponding to the ASCII hex encoding of the given short. */ public static char[] getChars(short num) { char[] hex = new char[4]; hex[0] = HEX_CHARS[(num >> 12) & 0x0F]; hex[1] = HEX_CHARS[(num >> 8) & 0x0F]; hex[2] = HEX_CHARS[(num >> 4) & 0x0F]; hex[3] = HEX_CHARS[num & 0x0F]; return hex; } /** * Takes the characters in the given string, convert it to bytes in UTF16-BE format and build a char array that * corresponds to the ASCII hex encoding of the resulting bytes. * * Example: * *
     * getCharsUTF16BE("ab") == new char[] { '0', '0', '6', '1', '0', '0', '6', '2' }
     * 
* * @param text The string to convert * @return The string converted to hex */ public static char[] getCharsUTF16BE(String text) { // Note that the internal representation of string in Java is already UTF-16. Therefore // we do not need to use an encoder to convert the string to its byte representation. char[] hex = new char[text.length() * 4]; for (int stringIdx = 0, charIdx = 0; stringIdx < text.length(); stringIdx++) { char c = text.charAt(stringIdx); hex[charIdx++] = HEX_CHARS[(c >> 12) & 0x0F]; hex[charIdx++] = HEX_CHARS[(c >> 8) & 0x0F]; hex[charIdx++] = HEX_CHARS[(c >> 4) & 0x0F]; hex[charIdx++] = HEX_CHARS[c & 0x0F]; } return hex; } /** * Writes the given byte as hex value to the given output stream. * * @param b the byte to be written * @param output the output stream to be written to * @throws IOException exception if anything went wrong */ public static void writeHexByte(byte b, OutputStream output) throws IOException { output.write(HEX_BYTES[getHighNibble(b)]); output.write(HEX_BYTES[getLowNibble(b)]); } /** * Writes the given byte array as hex value to the given output stream. * * @param bytes the byte array to be written * @param output the output stream to be written to * @throws IOException exception if anything went wrong */ public static void writeHexBytes(byte[] bytes, OutputStream output) throws IOException { for (byte b : bytes) { writeHexByte(b, output); } } /** * Get the high nibble of the given byte. * * @param b the given byte * @return the high nibble */ private static int getHighNibble(byte b) { return (b & 0xF0) >> 4; } /** * Get the low nibble of the given byte. * * @param b the given byte * @return the low nibble */ private static int getLowNibble(byte b) { return b & 0x0F; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/Matrix.java000066400000000000000000000512461320103431700237640ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.util.Arrays; import org.sejda.sambox.cos.COSArray; import org.sejda.sambox.cos.COSFloat; import org.sejda.sambox.cos.COSNumber; /** * This class will be used for matrix manipulation. * * @author Ben Litchfield */ public final class Matrix implements Cloneable { static final float[] DEFAULT_SINGLE = { 1,0,0, // a b 0 sx hy 0 note: hx and hy are reversed vs. the PDF spec as we use 0,1,0, // c d 0 = hx sy 0 AffineTransform's definition x and y shear 0,0,1 // tx ty 1 tx ty 1 }; private final float[] single; /** * Constructor. */ public Matrix() { single = new float[DEFAULT_SINGLE.length]; System.arraycopy(DEFAULT_SINGLE, 0, single, 0, DEFAULT_SINGLE.length); } /** * Creates a matrix from a 6-element COS array. */ public Matrix(COSArray array) { single = new float[DEFAULT_SINGLE.length]; single[0] = ((COSNumber) array.getObject(0)).floatValue(); single[1] = ((COSNumber) array.getObject(1)).floatValue(); single[3] = ((COSNumber) array.getObject(2)).floatValue(); single[4] = ((COSNumber) array.getObject(3)).floatValue(); single[6] = ((COSNumber) array.getObject(4)).floatValue(); single[7] = ((COSNumber) array.getObject(5)).floatValue(); single[8] = 1; } /** * Creates a matrix with the given 6 elements. */ public Matrix(float a, float b, float c, float d, float e, float f) { single = new float[DEFAULT_SINGLE.length]; single[0] = a; single[1] = b; single[3] = c; single[4] = d; single[6] = e; single[7] = f; single[8] = 1; } /** * Creates a matrix with the same elements as the given AffineTransform. */ public Matrix(AffineTransform at) { single = new float[DEFAULT_SINGLE.length]; System.arraycopy(DEFAULT_SINGLE, 0, single, 0, DEFAULT_SINGLE.length); single[0] = (float)at.getScaleX(); single[1] = (float)at.getShearY(); single[3] = (float)at.getShearX(); single[4] = (float)at.getScaleY(); single[6] = (float)at.getTranslateX(); single[7] = (float)at.getTranslateY(); } /** * This method resets the numbers in this Matrix to the original values, which are * the values that a newly constructed Matrix would have. * * @deprecated This method will be removed. */ @Deprecated public void reset() { System.arraycopy(DEFAULT_SINGLE, 0, single, 0, DEFAULT_SINGLE.length); } /** * Create an affine transform from this matrix's values. * * @return An affine transform with this matrix's values. */ public AffineTransform createAffineTransform() { return new AffineTransform( single[0], single[1], // m00 m10 = scaleX shearY single[3], single[4], // m01 m11 = shearX scaleY single[6], single[7] ); // m02 m12 = tx ty } /** * Set the values of the matrix from the AffineTransform. * * @param af The transform to get the values from. * @deprecated Use the {@link #Matrix(AffineTransform)} constructor instead. */ @Deprecated public void setFromAffineTransform( AffineTransform af ) { single[0] = (float)af.getScaleX(); single[1] = (float)af.getShearY(); single[3] = (float)af.getShearX(); single[4] = (float)af.getScaleY(); single[6] = (float)af.getTranslateX(); single[7] = (float)af.getTranslateY(); } /** * This will get a matrix value at some point. * * @param row The row to get the value from. * @param column The column to get the value from. * * @return The value at the row/column position. */ public float getValue( int row, int column ) { return single[row*3+column]; } /** * This will set a value at a position. * * @param row The row to set the value at. * @param column the column to set the value at. * @param value The value to set at the position. */ public void setValue( int row, int column, float value ) { single[row*3+column] = value; } /** * Return a single dimension array of all values in the matrix. * * @return The values of this matrix. */ public float[][] getValues() { float[][] retval = new float[3][3]; retval[0][0] = single[0]; retval[0][1] = single[1]; retval[0][2] = single[2]; retval[1][0] = single[3]; retval[1][1] = single[4]; retval[1][2] = single[5]; retval[2][0] = single[6]; retval[2][1] = single[7]; retval[2][2] = single[8]; return retval; } /** * Return a single dimension array of all values in the matrix. * * @return The values ot this matrix. * @deprecated Use {@link #getValues()} instead. */ @Deprecated public double[][] getValuesAsDouble() { double[][] retval = new double[3][3]; retval[0][0] = single[0]; retval[0][1] = single[1]; retval[0][2] = single[2]; retval[1][0] = single[3]; retval[1][1] = single[4]; retval[1][2] = single[5]; retval[2][0] = single[6]; retval[2][1] = single[7]; retval[2][2] = single[8]; return retval; } /** * Concatenates (premultiplies) the given matrix to this matrix. * * @param matrix The matrix to concatenate. */ public void concatenate(Matrix matrix) { matrix.multiply(this, this); } /** * Translates this matrix by the given vector. * * @param vector 2D vector */ public void translate(Vector vector) { Matrix m = Matrix.getTranslateInstance(vector.getX(), vector.getY()); concatenate(m); } /** * Translates this matrix by the given ammount. * * @param tx x-translation * @param ty y-translation */ public void translate(float tx, float ty) { Matrix m = Matrix.getTranslateInstance(tx, ty); concatenate(m); } /** * Scales this matrix by the given factors. * * @param sx x-scale * @param sy y-scale */ public void scale(float sx, float sy) { Matrix m = Matrix.getScaleInstance(sx, sy); concatenate(m); } /** * Rotares this matrix by the given factors. * * @param theta The angle of rotation measured in radians */ public void rotate(double theta) { Matrix m = Matrix.getRotateInstance(theta, 0, 0); concatenate(m); } /** * This will take the current matrix and multiply it with a matrix that is passed in. * * @param b The matrix to multiply by. * * @return The result of the two multiplied matrices. */ public Matrix multiply( Matrix b ) { return this.multiply(b, new Matrix()); } /** * This method multiplies this Matrix with the specified other Matrix, storing the product in the specified * result Matrix. By reusing Matrix instances like this, multiplication chains can be executed without having * to create many temporary Matrix objects. *

* It is allowed to have (other == this) or (result == this) or indeed (other == result) but if this is done, * the backing float[] matrix values may be copied in order to ensure a correct product. * * @param other the second operand Matrix in the multiplication * @param result the Matrix instance into which the result should be stored. If result is null, a new Matrix * instance is created. * @return the product of the two matrices. */ public Matrix multiply( Matrix other, Matrix result ) { if (result == null) { result = new Matrix(); } if (other != null && other.single != null) { // the operands float[] thisOperand = this.single; float[] otherOperand = other.single; // We're multiplying 2 sets of floats together to produce a third, but we allow // any of these float[] instances to be the same objects. // There is the possibility then to overwrite one of the operands with result values // and therefore corrupt the result. // If either of these operands are the same float[] instance as the result, then // they need to be copied. if (this == result) { final float[] thisOrigVals = new float[this.single.length]; System.arraycopy(this.single, 0, thisOrigVals, 0, this.single.length); thisOperand = thisOrigVals; } if (other == result) { final float[] otherOrigVals = new float[other.single.length]; System.arraycopy(other.single, 0, otherOrigVals, 0, other.single.length); otherOperand = otherOrigVals; } result.single[0] = thisOperand[0] * otherOperand[0] + thisOperand[1] * otherOperand[3] + thisOperand[2] * otherOperand[6]; result.single[1] = thisOperand[0] * otherOperand[1] + thisOperand[1] * otherOperand[4] + thisOperand[2] * otherOperand[7]; result.single[2] = thisOperand[0] * otherOperand[2] + thisOperand[1] * otherOperand[5] + thisOperand[2] * otherOperand[8]; result.single[3] = thisOperand[3] * otherOperand[0] + thisOperand[4] * otherOperand[3] + thisOperand[5] * otherOperand[6]; result.single[4] = thisOperand[3] * otherOperand[1] + thisOperand[4] * otherOperand[4] + thisOperand[5] * otherOperand[7]; result.single[5] = thisOperand[3] * otherOperand[2] + thisOperand[4] * otherOperand[5] + thisOperand[5] * otherOperand[8]; result.single[6] = thisOperand[6] * otherOperand[0] + thisOperand[7] * otherOperand[3] + thisOperand[8] * otherOperand[6]; result.single[7] = thisOperand[6] * otherOperand[1] + thisOperand[7] * otherOperand[4] + thisOperand[8] * otherOperand[7]; result.single[8] = thisOperand[6] * otherOperand[2] + thisOperand[7] * otherOperand[5] + thisOperand[8] * otherOperand[8]; } return result; } /** * Transforms the given point by this matrix. * * @param point point to transform */ public void transform(Point2D point) { float x = (float)point.getX(); float y = (float)point.getY(); float a = single[0]; float b = single[1]; float c = single[3]; float d = single[4]; float e = single[6]; float f = single[7]; point.setLocation(x * a + y * c + e, x * b + y * d + f); } /** * Transforms the given point by this matrix. * * @param x x-coordinate * @param y y-coordinate */ public Point2D.Float transformPoint(float x, float y) { float a = single[0]; float b = single[1]; float c = single[3]; float d = single[4]; float e = single[6]; float f = single[7]; return new Point2D.Float(x * a + y * c + e, x * b + y * d + f); } /** * Transforms the given point by this matrix. * * @param vector @2D vector */ public Vector transform(Vector vector) { float a = single[0]; float b = single[1]; float c = single[3]; float d = single[4]; float e = single[6]; float f = single[7]; float x = vector.getX(); float y = vector.getY(); return new Vector(x * a + y * c + e, x * b + y * d + f); } /** * Create a new matrix with just the scaling operators. * * @return A new matrix with just the scaling operators. * @deprecated This method is due to be removed, please contact us if you make use of it. */ @Deprecated public Matrix extractScaling() { Matrix matrix = new Matrix(); matrix.single[0] = this.single[0]; matrix.single[4] = this.single[4]; return matrix; } /** * Convenience method to create a scaled instance. * * @param sx The xscale operator. * @param sy The yscale operator. * @return A new matrix with just the x/y scaling */ public static Matrix getScaleInstance(float sx, float sy) { Matrix matrix = new Matrix(); matrix.single[0] = sx; matrix.single[4] = sy; return matrix; } /** * Create a new matrix with just the translating operators. * * @return A new matrix with just the translating operators. * @deprecated This method is due to be removed, please contact us if you make use of it. */ @Deprecated public Matrix extractTranslating() { Matrix matrix = new Matrix(); matrix.single[6] = this.single[6]; matrix.single[7] = this.single[7]; return matrix; } /** * Convenience method to create a translating instance. * * @param tx The x translating operator. * @param ty The y translating operator. * @return A new matrix with just the x/y translating. * @deprecated Use {@link #getTranslateInstance} instead. */ @Deprecated public static Matrix getTranslatingInstance(float tx, float ty) { return getTranslateInstance(tx, ty); } /** * Convenience method to create a translating instance. * * @param tx The x translating operator. * @param ty The y translating operator. * @return A new matrix with just the x/y translating. */ public static Matrix getTranslateInstance(float tx, float ty) { Matrix matrix = new Matrix(); matrix.single[6] = tx; matrix.single[7] = ty; return matrix; } /** * Convenience method to create a rotated instance. * * @param theta The angle of rotation measured in radians * @param tx The x translation. * @param ty The y translation. * @return A new matrix with the rotation and the x/y translating. */ public static Matrix getRotateInstance(double theta, float tx, float ty) { float cosTheta = (float)Math.cos(theta); float sinTheta = (float)Math.sin(theta); Matrix matrix = new Matrix(); matrix.single[0] = cosTheta; matrix.single[1] = sinTheta; matrix.single[3] = -sinTheta; matrix.single[4] = cosTheta; matrix.single[6] = tx; matrix.single[7] = ty; return matrix; } /** * Produces a copy of the first matrix, with the second matrix concatenated. * * @param a The matrix to copy. * @param b The matrix to concatenate. */ public static Matrix concatenate(Matrix a, Matrix b) { Matrix copy = a.clone(); copy.concatenate(b); return copy; } /** * Clones this object. * @return cloned matrix as an object. */ @Override public Matrix clone() { Matrix clone = new Matrix(); System.arraycopy( single, 0, clone.single, 0, 9 ); return clone; } /** * Returns the x-scaling factor of this matrix. This is calculated from the scale and shear. * * @return The x-scaling factor. */ public float getScalingFactorX() { float xScale = single[0]; /** * BM: if the trm is rotated, the calculation is a little more complicated * * The rotation matrix multiplied with the scaling matrix is: * ( x 0 0) ( cos sin 0) ( x*cos x*sin 0) * ( 0 y 0) * (-sin cos 0) = (-y*sin y*cos 0) * ( 0 0 1) ( 0 0 1) ( 0 0 1) * * So, if you want to deduce x from the matrix you take * M(0,0) = x*cos and M(0,1) = x*sin and use the theorem of Pythagoras * * sqrt(M(0,0)^2+M(0,1)^2) = * sqrt(x2*cos2+x2*sin2) = * sqrt(x2*(cos2+sin2)) = <- here is the trick cos2+sin2 is one * sqrt(x2) = * abs(x) */ if( !(single[1]==0.0f && single[3]==0.0f) ) { xScale = (float)Math.sqrt(Math.pow(single[0], 2)+ Math.pow(single[1], 2)); } return xScale; } /** * Returns the y-scaling factor of this matrix. This is calculated from the scale and shear. * * @return The y-scaling factor. */ public float getScalingFactorY() { float yScale = single[4]; if( !(single[1]==0.0f && single[3]==0.0f) ) { yScale = (float)Math.sqrt(Math.pow(single[3], 2)+ Math.pow(single[4], 2)); } return yScale; } /** * Returns the x-scaling element of this matrix. */ public float getScaleX() { return single[0]; } /** * Returns the y-shear element of this matrix. */ public float getShearY() { return single[1]; } /** * Returns the x-shear element of this matrix. */ public float getShearX() { return single[3]; } /** * Returns the y-scaling element of this matrix. */ public float getScaleY() { return single[4]; } /** * Returns the x-translation element of this matrix. */ public float getTranslateX() { return single[6]; } /** * Returns the y-translation element of this matrix. */ public float getTranslateY() { return single[7]; } /** * Get the x position in the matrix. This method is deprecated as it is incorrectly named. * * @return The x-position. * @deprecated Use {@link #getTranslateX} instead */ @Deprecated public float getXPosition() { return single[6]; } /** * Get the y position. This method is deprecated as it is incorrectly named. * * @return The y position. * @deprecated Use {@link #getTranslateY} instead */ @Deprecated public float getYPosition() { return single[7]; } /** * Returns a COS array which represents this matrix. */ public COSArray toCOSArray() { COSArray array = new COSArray(); array.add(new COSFloat(single[0])); array.add(new COSFloat(single[1])); array.add(new COSFloat(single[3])); array.add(new COSFloat(single[4])); array.add(new COSFloat(single[6])); array.add(new COSFloat(single[7])); return array; } @Override public String toString() { StringBuffer sb = new StringBuffer( "" ); sb.append("["); sb.append(single[0] + ","); sb.append(single[1] + ","); sb.append(single[3] + ","); sb.append(single[4] + ","); sb.append(single[6] + ","); sb.append(single[7] + "]"); return sb.toString(); } @Override public int hashCode() { return Arrays.hashCode(single); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } return Arrays.equals(this.single, ((Matrix) obj).single); } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/NumberFormatUtil.java000066400000000000000000000132771320103431700257610ustar00rootroot00000000000000/* * Copyright 2016 The Apache Software Foundation. * * 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. */ package org.sejda.sambox.util; /** * This class contains methods to format numbers. * * @author Michael Doswald */ public final class NumberFormatUtil { private NumberFormatUtil() { // } /** * Maximum number of fraction digits supported by the format methods */ private static final int MAX_FRACTION_DIGITS = 5; /** * Contains the power of ten values for fast lookup in the format methods */ private static final long[] POWER_OF_TENS; private static final int[] POWER_OF_TENS_INT; static { POWER_OF_TENS = new long[19]; POWER_OF_TENS[0] = 1; for (int exp = 1; exp < POWER_OF_TENS.length; exp++) { POWER_OF_TENS[exp] = POWER_OF_TENS[exp - 1] * 10; } POWER_OF_TENS_INT = new int[10]; POWER_OF_TENS_INT[0] = 1; for (int exp = 1; exp < POWER_OF_TENS_INT.length; exp++) { POWER_OF_TENS_INT[exp] = POWER_OF_TENS_INT[exp - 1] * 10; } } /** * Fast variant to format a floating point value to a ASCII-string. The format will fail if the value is greater * than {@link Long#MAX_VALUE}, smaller or equal to {@link Long#MIN_VALUE}, is {@link Float#NaN}, infinite or the * number of requested fraction digits is greater than {@link #MAX_FRACTION_DIGITS}. * * When the number contains more fractional digits than {@code maxFractionDigits} the value will be rounded. * Rounding is done to the nearest possible value, with the tie breaking rule of rounding away from zero. * * @param value The float value to format * @param maxFractionDigits The maximum number of fraction digits used * @param asciiBuffer The output buffer to write the formatted value to * * @return The number of bytes used in the buffer or {@code -1} if formatting failed */ public static int formatFloatFast(float value, int maxFractionDigits, byte[] asciiBuffer) { if (Float.isNaN(value) || Float.isInfinite(value) || value > Long.MAX_VALUE || value <= Long.MIN_VALUE || maxFractionDigits > MAX_FRACTION_DIGITS) { return -1; } int offset = 0; long integerPart = (long) value; // handle sign if (value < 0) { asciiBuffer[offset++] = '-'; integerPart = -integerPart; } // extract fraction part long fractionPart = (long) ((Math.abs((double) value) - integerPart) * POWER_OF_TENS[maxFractionDigits] + 0.5d); // Check for rounding to next integer if (fractionPart >= POWER_OF_TENS[maxFractionDigits]) { integerPart++; fractionPart -= POWER_OF_TENS[maxFractionDigits]; } // format integer part offset = formatPositiveNumber(integerPart, getExponent(integerPart), false, asciiBuffer, offset); if (fractionPart > 0 && maxFractionDigits > 0) { asciiBuffer[offset++] = '.'; offset = formatPositiveNumber(fractionPart, maxFractionDigits - 1, true, asciiBuffer, offset); } return offset; } /** * Formats a positive integer number starting with the digit at {@code 10^exp}. * * @param number The number to format * @param exp The start digit * @param omitTrailingZeros Whether the formatting should stop if only trailing zeros are left. This is needed e.g. * when formatting fractions of a number. * @param asciiBuffer The buffer to write the ASCII digits to * @param startOffset The start offset into the buffer to start writing * * @return The offset into the buffer which contains the first byte that was not filled by the method */ private static int formatPositiveNumber(long number, int exp, boolean omitTrailingZeros, byte[] asciiBuffer, int startOffset) { int offset = startOffset; long remaining = number; while (remaining > Integer.MAX_VALUE && (!omitTrailingZeros || remaining > 0)) { long digit = remaining / POWER_OF_TENS[exp]; remaining -= (digit * POWER_OF_TENS[exp]); asciiBuffer[offset++] = (byte) ('0' + digit); exp--; } // If the remaining fits into an integer, use int arithmetic as it is faster int remainingInt = (int) remaining; while (exp >= 0 && (!omitTrailingZeros || remainingInt > 0)) { int digit = remainingInt / POWER_OF_TENS_INT[exp]; remainingInt -= (digit * POWER_OF_TENS_INT[exp]); asciiBuffer[offset++] = (byte) ('0' + digit); exp--; } return offset; } /** * Returns the highest exponent of 10 where {@code 10^exp < number} for numbers > 0 */ private static int getExponent(long number) { for (int exp = 0; exp < (POWER_OF_TENS.length - 1); exp++) { if (number < POWER_OF_TENS[exp + 1]) { return exp; } } return POWER_OF_TENS.length - 1; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/Pool.java000066400000000000000000000051421320103431700234230ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.util.Optional; import java.util.concurrent.ArrayBlockingQueue; import java.util.function.Consumer; import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A simple object pool implementation that requires a {@link Supplier} which is used to create instances to pool and * when an object is requested but the pool is exhausted. * * @author Andrea Vacondio */ public class Pool { private static final Logger LOG = LoggerFactory.getLogger(Pool.class); private final ArrayBlockingQueue pool; private Supplier supplier; private Optional> applyOnGive = Optional.empty(); public Pool(Supplier creator, int poolsize) { requireNotNullArg(creator, "Pool objects creator cannot be null"); this.pool = new ArrayBlockingQueue<>(poolsize); this.supplier = creator; } /** * @return the instance borrowed from the pool */ public T borrow() { return Optional.ofNullable(this.pool.poll()).orElseGet(supplier); } /** * Returns the object to the pool * * @param object */ public void give(T object) { applyOnGive.ifPresent(c -> c.accept(object)); if (!this.pool.offer(object)) { LOG.info("Poll is already full, cannot return borrowed instance"); } } /** * Configure the pool to apply the given consumer to returned objects. This might be useful to restore initial * status on returned objects. * * @param applyOnGive * @return the pool */ public Pool onGive(Consumer applyOnGive) { this.applyOnGive = Optional.ofNullable(applyOnGive); return this; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/QuickSort.java000066400000000000000000000066361320103431700244470ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; import java.util.ArrayDeque; import java.util.Comparator; import java.util.Deque; import java.util.List; /** * see http://de.wikipedia.org/wiki/Quicksort. * * @author Uwe Pachler */ public class QuickSort { private QuickSort() { } private static final Comparator OBJCOMP = new Comparator() { @Override public int compare(Comparable object1, Comparable object2) { return object1.compareTo(object2); } }; /** * Sorts the given list using the given comparator. * * @param list list to be sorted * @param cmp comparator used to compare the object swithin the list */ public static void sort(List list, Comparator cmp) { int size = list.size(); if (size < 2) { return; } quicksort(list, cmp); } /** * Sorts the given list using compareTo as comparator. * * @param list list to be sorted */ public static void sort(List list) { sort(list, (Comparator) OBJCOMP); } private static void quicksort(List list, Comparator cmp) { Deque stack = new ArrayDeque<>(); stack.push(0); stack.push(list.size()); while (!stack.isEmpty()) { int right = stack.pop(); int left = stack.pop(); if (right - left < 2) { continue; } int p = left + ((right - left) / 2); p = partition(list, cmp, p, left, right); stack.push(p + 1); stack.push(right); stack.push(left); stack.push(p); } } private static int partition(List list, Comparator cmp, int p, int start, int end) { int l = start; int h = end - 2; T piv = list.get(p); swap(list, p, end - 1); while (l < h) { if (cmp.compare(list.get(l), piv) <= 0) { l++; } else if (cmp.compare(piv, list.get(h)) <= 0) { h--; } else { swap(list, l, h); } } int idx = h; if (cmp.compare(list.get(h), piv) < 0) { idx++; } swap(list, end - 1, idx); return idx; } private static void swap(List list, int i, int j) { T tmp = list.get(i); list.set(i, list.get(j)); list.set(j, tmp); } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/ReflectionUtils.java000066400000000000000000000145271320103431700256340ustar00rootroot00000000000000package org.sejda.sambox.util; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; /** * Simple utility class for working with the reflection API and handling * reflection exceptions. * * From Springframework * */ public abstract class ReflectionUtils { /** * Attempt to find a {@link Field field} on the supplied {@link Class} with the * supplied {@code name}. Searches all superclasses up to {@link Object}. * @param clazz the class to introspect * @param name the name of the field * @return the corresponding Field object, or {@code null} if not found */ public static Field findField(Class clazz, String name) { return findField(clazz, name, null); } /** * Attempt to find a {@link Field field} on the supplied {@link Class} with the * supplied {@code name} and/or {@link Class type}. Searches all superclasses * up to {@link Object}. * @param clazz the class to introspect * @param name the name of the field (may be {@code null} if type is specified) * @param type the type of the field (may be {@code null} if name is specified) * @return the corresponding Field object, or {@code null} if not found */ public static Field findField(Class clazz, String name, Class type) { Class searchType = clazz; while (Object.class != searchType && searchType != null) { Field[] fields = getDeclaredFields(searchType); for (Field field : fields) { if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) { return field; } } searchType = searchType.getSuperclass(); } return null; } /** * Get the field represented by the supplied {@link Field field object} on the * specified {@link Object target object}. In accordance with {@link Field#get(Object)} * semantics, the returned value is automatically wrapped if the underlying field * has a primitive type. *

Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. * @param field the field to get * @param target the target object from which to get the field * @return the field's current value */ public static Object getField(Field field, Object target) { try { return field.get(target); } catch (IllegalAccessException ex) { handleReflectionException(ex); throw new IllegalStateException( "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); } } /** * Handle the given reflection exception. Should only be called if no * checked exception is expected to be thrown by the target method. *

Throws the underlying RuntimeException or Error in case of an * InvocationTargetException with such a root cause. Throws an * IllegalStateException with an appropriate message or * UndeclaredThrowableException otherwise. * @param ex the reflection exception to handle */ public static void handleReflectionException(Exception ex) { if (ex instanceof NoSuchMethodException) { throw new IllegalStateException("Method not found: " + ex.getMessage()); } if (ex instanceof IllegalAccessException) { throw new IllegalStateException("Could not access method: " + ex.getMessage()); } if (ex instanceof InvocationTargetException) { handleInvocationTargetException((InvocationTargetException) ex); } if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } throw new UndeclaredThrowableException(ex); } /** * Handle the given invocation target exception. Should only be called if no * checked exception is expected to be thrown by the target method. *

Throws the underlying RuntimeException or Error in case of such a root * cause. Throws an UndeclaredThrowableException otherwise. * @param ex the invocation target exception to handle */ public static void handleInvocationTargetException(InvocationTargetException ex) { rethrowRuntimeException(ex.getTargetException()); } /** * Rethrow the given {@link Throwable exception}, which is presumably the * target exception of an {@link InvocationTargetException}. * Should only be called if no checked exception is expected to be thrown * by the target method. *

Rethrows the underlying exception cast to a {@link RuntimeException} or * {@link Error} if appropriate; otherwise, throws an * {@link UndeclaredThrowableException}. * @param ex the exception to rethrow * @throws RuntimeException the rethrown exception */ public static void rethrowRuntimeException(Throwable ex) { if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } if (ex instanceof Error) { throw (Error) ex; } throw new UndeclaredThrowableException(ex); } /** * Make the given field accessible, explicitly setting it accessible if * necessary. The {@code setAccessible(true)} method is only called * when actually necessary, to avoid unnecessary conflicts with a JVM * SecurityManager (if active). * @param field the field to make accessible * @see java.lang.reflect.Field#setAccessible */ public static void makeAccessible(Field field) { if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { field.setAccessible(true); } } /** * This variant retrieves {@link Class#getDeclaredFields()} from a local cache * in order to avoid the JVM's SecurityManager check and defensive array copying. * @param clazz the class to introspect * @return the cached array of fields * @see Class#getDeclaredFields() */ private static Field[] getDeclaredFields(Class clazz) { return clazz.getDeclaredFields(); } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/SpecVersionUtils.java000066400000000000000000000063121320103431700257730ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; import static org.sejda.util.RequireUtils.requireNotNullArg; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class with pdf spec versions related methods and constants * * @author Andrea Vacondio */ public class SpecVersionUtils { private static final Logger LOG = LoggerFactory.getLogger(SpecVersionUtils.class); public static final int EXPECTED_HEADER_LENGTH = 8; public static final String PDF_HEADER = "%PDF-"; public static final String V1_0 = "1.0"; public static final String V1_1 = "1.1"; public static final String V1_2 = "1.2"; public static final String V1_3 = "1.3"; public static final String V1_4 = "1.4"; public static final String V1_5 = "1.5"; public static final String V1_6 = "1.6"; public static final String V1_7 = "1.7"; public static final String V2_0 = "2.0"; private static final String VERSION_PATTERN = "^(\\d)\\.(\\d)$"; private SpecVersionUtils() { // utility } /** * Parses a conforming file header string or returning the version (ex. "1.4") found. File header is defined in Chap * 7.5.2 of PDF 32000-1:2008 * * @param header * @return the parsed version (ex. "1.4") * @throws IOException if the string is not a valid header */ public static String parseHeaderString(String header) { String version = sanitizeVersion( header.substring(EXPECTED_HEADER_LENGTH - 3, EXPECTED_HEADER_LENGTH)); if (!version.matches(VERSION_PATTERN)) { // it seems Acrobat doesn't choke on invalid version but it does sometime warn the user LOG.warn("Invalid header version {}, falling back to {}", version, V1_6); return V1_6; } return version; } private static String sanitizeVersion(String version) { return version.replace(',', '.'); } /** * @param version the version to compare * @param atLeast min version to return true (ex. "1.5") * @return true if the given pdf spec version is at least as high as the given atLeast version */ public static boolean isAtLeast(String version, String atLeast) { requireNotNullArg(version, "Cannot compare a null version"); requireNotNullArg(atLeast, "Cannot compare a null version"); return version.compareTo(atLeast) >= 0; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/Vector.java000066400000000000000000000030011320103431700237440ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; /** * A 2D vector. * * @author John Hewson */ public final class Vector { private final float x, y; public Vector(float x, float y) { this.x = x; this.y = y; } /** * Returns the x magnitude. */ public float getX() { return x; } /** * Returns the y magnitude. */ public float getY() { return y; } /** * Returns a new vector scaled by both x and y. * * @param sxy x and y scale */ public Vector scale(float sxy) { return new Vector(x * sxy, y * sxy); } @Override public String toString() { return "(" + x + ", " + y + ")"; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/Version.java000066400000000000000000000031721320103431700241400ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import org.sejda.sambox.SAMBox; /** * Exposes PDFBox version. */ public final class Version { private Version() { // static helper } /** * @return the version of PDFBox. */ public static String getVersion() { try (InputStream stream = Version.class.getClassLoader().getResourceAsStream( SAMBox.SAMBOX_PROPERTIES)) { if (stream != null) { Properties properties = new Properties(); properties.load(stream); return properties.getProperty("sambox.version", ""); } } catch (IOException io) { // nothing } return null; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/filetypedetector/000077500000000000000000000000001320103431700252205ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/util/filetypedetector/ByteTrie.java000066400000000000000000000071401320103431700276140ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util.filetypedetector; import java.util.HashMap; import java.util.Map; /** * @author Drew Noakes * * code taken from https://github.com/drewnoakes/metadata-extractor * * 2016-01-04 * * latest commit number 73f1a48 * * Stores values using a prefix tree (aka 'trie', i.e. reTRIEval data structure). * * @param the type of value to store for byte sequences */ class ByteTrie { /** * A node in the trie. Has children and may have an associated value. */ static class ByteTrieNode { private final Map> children = new HashMap<>(); private T value = null; public void setValue(T value) { if (this.value != null) { throw new IllegalStateException("Value already set for this trie node"); } this.value = value; } public T getValue() { return value; } } private final ByteTrieNode root = new ByteTrieNode<>(); private int maxDepth; /** * Return the most specific value stored for this byte sequence. If not found, returns * null or a default values as specified by calling * {@link ByteTrie#setDefaultValue}. * @param bytes * @return */ public T find(byte[] bytes) { ByteTrieNode node = root; T val = node.getValue(); for (byte b : bytes) { ByteTrieNode child = node.children.get(b); if (child == null) { break; } node = child; if (node.getValue() != null) { val = node.getValue(); } } return val; } /** * Store the given value at the specified path. * @param value * @param parts */ public void addPath(T value, byte[]... parts) { int depth = 0; ByteTrieNode node = root; for (byte[] part : parts) { for (byte b : part) { ByteTrieNode child = node.children.get(b); if (child == null) { child = new ByteTrieNode<>(); node.children.put(b, child); } node = child; depth++; } } node.setValue(value); maxDepth = Math.max(maxDepth, depth); } /** * Sets the default value to use in {@link ByteTrie#find(byte[])} when no path matches. * @param defaultValue */ public void setDefaultValue(T defaultValue) { root.setValue(defaultValue); } /** * Gets the maximum depth stored in this trie. * @return */ public int getMaxDepth() { return maxDepth; } } sambox-1.1.19/src/main/java/org/sejda/sambox/util/filetypedetector/FileType.java000066400000000000000000000027021320103431700276050ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util.filetypedetector; /** * @author Drew Noakes * * code taken from https://github.com/drewnoakes/metadata-extractor * * 2016-01-04 * * latest commit number 73f1a48 * * Enumeration of supported image file formats. */ public enum FileType { UNKNOWN, JPEG, TIFF, PSD, PNG, BMP, GIF, ICO, PCX, RIFF, /** Sony camera raw. */ ARW, /** Canon camera raw, version 1. */ CRW, /** Canon camera raw, version 2. */ CR2, /** Nikon camera raw. */ NEF, /** Olympus camera raw. */ ORF, /** FujiFilm camera raw. */ RAF, /** Panasonic camera raw. */ RW2 }sambox-1.1.19/src/main/java/org/sejda/sambox/util/filetypedetector/FileTypeDetector.java000066400000000000000000000110131320103431700312720ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.util.filetypedetector; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; /** * @author Drew Noakes * * code taken from https://github.com/drewnoakes/metadata-extractor * * 2016-01-04 * * latest commit number 73f1a48 * * Examines the a file's first bytes and estimates the file's type. */ public final class FileTypeDetector { private static final ByteTrie ROOT; static { ROOT = new ByteTrie<>(); ROOT.setDefaultValue(FileType.UNKNOWN); // https://en.wikipedia.org/wiki/List_of_file_signatures ROOT.addPath(FileType.JPEG, new byte[] { (byte) 0xff, (byte) 0xd8 }); ROOT.addPath(FileType.TIFF, "II".getBytes(StandardCharsets.ISO_8859_1), new byte[] { 0x2a, 0x00 }); ROOT.addPath(FileType.TIFF, "MM".getBytes(StandardCharsets.ISO_8859_1), new byte[] { 0x00, 0x2a }); ROOT.addPath(FileType.PSD, "8BPS".getBytes(StandardCharsets.ISO_8859_1)); ROOT.addPath(FileType.PNG, new byte[] { (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52 }); // TODO technically there are other very rare magic numbers for OS/2 BMP files... ROOT.addPath(FileType.BMP, "BM".getBytes(StandardCharsets.ISO_8859_1)); ROOT.addPath(FileType.GIF, "GIF87a".getBytes(StandardCharsets.ISO_8859_1)); ROOT.addPath(FileType.GIF, "GIF89a".getBytes(StandardCharsets.ISO_8859_1)); ROOT.addPath(FileType.ICO, new byte[] { 0x00, 0x00, 0x01, 0x00 }); // multiple PCX versions, explicitly listed ROOT.addPath(FileType.PCX, new byte[] { 0x0A, 0x00, 0x01 }); ROOT.addPath(FileType.PCX, new byte[] { 0x0A, 0x02, 0x01 }); ROOT.addPath(FileType.PCX, new byte[] { 0x0A, 0x03, 0x01 }); ROOT.addPath(FileType.PCX, new byte[] { 0x0A, 0x05, 0x01 }); ROOT.addPath(FileType.RIFF, "RIFF".getBytes(StandardCharsets.ISO_8859_1)); ROOT.addPath(FileType.ARW, "II".getBytes(StandardCharsets.ISO_8859_1), new byte[] { 0x2a, 0x00, 0x08, 0x00 }); ROOT.addPath(FileType.CRW, "II".getBytes(StandardCharsets.ISO_8859_1), new byte[] { 0x1a, 0x00, 0x00, 0x00 }, "HEAPCCDR".getBytes(StandardCharsets.ISO_8859_1)); ROOT.addPath(FileType.CR2, "II".getBytes(StandardCharsets.ISO_8859_1), new byte[] { 0x2a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x52 }); ROOT.addPath(FileType.NEF, "MM".getBytes(StandardCharsets.ISO_8859_1), new byte[] { 0x00, 0x2a, 0x00, 0x00, 0x00, (byte) 0x80, 0x00 }); ROOT.addPath(FileType.ORF, "IIRO".getBytes(StandardCharsets.ISO_8859_1), new byte[] { (byte) 0x08, 0x00 }); ROOT.addPath(FileType.ORF, "IIRS".getBytes(StandardCharsets.ISO_8859_1), new byte[] { (byte) 0x08, 0x00 }); ROOT.addPath(FileType.RAF, "FUJIFILMCCD-RAW".getBytes(StandardCharsets.ISO_8859_1)); ROOT.addPath(FileType.RW2, "II".getBytes(StandardCharsets.ISO_8859_1), new byte[] { 0x55, 0x00 }); } private FileTypeDetector() { } /** * Examines the a file's first bytes and estimates the file's type. * * @param file to examine. * @return the file type or null if it wasn't possible to determine by reading the first bytes * @throws IOException if an IO error occurs. */ public static FileType detectFileType(File file) throws IOException { byte[] firstBytes = new byte[ROOT.getMaxDepth()]; try (FileInputStream fin = new FileInputStream(file)) { fin.read(firstBytes); } return ROOT.find(firstBytes); } } sambox-1.1.19/src/main/java/org/sejda/sambox/xref/000077500000000000000000000000001320103431700216345ustar00rootroot00000000000000sambox-1.1.19/src/main/java/org/sejda/sambox/xref/CompressedXrefEntry.java000066400000000000000000000060031320103431700264510ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.xref; import static org.sejda.util.RequireUtils.requireArg; /** * An xref entry for a cross reference stream which represent a compressed objects (i.e. an object part of an object * stream). See table 18 PDF32000:2008-1 * * @author Andrea Vacondio * */ public final class CompressedXrefEntry extends XrefEntry { private long objectStreamNumber; private long index; private CompressedXrefEntry(XrefType type, long objectNumber, long byteOffset, int generationNumber, long objectStreamNumber, long index) { super(type, objectNumber, byteOffset, generationNumber); requireArg(objectStreamNumber >= 0, "Containing object stream number cannot be negative"); this.objectStreamNumber = objectStreamNumber; this.index = index; } /** * @return The object number of the object stream in which this object is stored. */ public long getObjectStreamNumber() { return objectStreamNumber; } @Override public byte[] toXrefStreamEntry(int secondFieldLength, int thirdFieldLength) { byte[] retVal = new byte[1 + secondFieldLength + thirdFieldLength]; retVal[0] = 0b00000010; copyBytesTo(getObjectStreamNumber(), secondFieldLength, retVal, 1); copyBytesTo(index, thirdFieldLength, retVal, 1 + secondFieldLength); return retVal; } @Override public String toString() { return String.format("%s offset=%d objectStreamNumber=%d, %s", getType().toString(), getByteOffset(), objectStreamNumber, key().toString()); } /** * Factory method for an entry in the xref stream representing a compressed object in an object stream * * @param objectNumber * @param objectStreamNumber The object number of the object stream in which this object is stored. * @param index The index of this object within the object stream. * @return the newly created instance */ public static CompressedXrefEntry compressedEntry(long objectNumber, long objectStreamNumber, long index) { return new CompressedXrefEntry(XrefType.COMPRESSED, objectNumber, UNKNOWN_OFFSET, 0, objectStreamNumber, index); } } sambox-1.1.19/src/main/java/org/sejda/sambox/xref/FileTrailer.java000066400000000000000000000032411320103431700247010ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.xref; import org.sejda.sambox.cos.COSDictionary; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.common.PDDictionaryWrapper; /** * File trailer with information regarding the xref offset * * @author Andrea Vacondio * */ public class FileTrailer extends PDDictionaryWrapper { private long xrefOffset = -1; public FileTrailer() { super(); } public FileTrailer(COSDictionary trailerDictionary) { super(trailerDictionary); } public long xrefOffset() { return xrefOffset; } public void xrefOffset(long offset) { this.xrefOffset = offset; } /** * @return true if the type of this dictionary is {@link COSName#XREF} */ public boolean isXrefStream() { return COSName.XREF.equals(getCOSObject().getCOSName(COSName.TYPE)); } } sambox-1.1.19/src/main/java/org/sejda/sambox/xref/Xref.java000066400000000000000000000054501320103431700234070ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.xref; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.TreeSet; import org.sejda.sambox.cos.COSObjectKey; /** * Xref table/stream entries. * * @author Andrea Vacondio * */ public class Xref { private HashMap data = new HashMap<>(); /** * Adds the given entry to the {@link Xref} if an entry with the given object number and generation is not already * present. * * @param entry * @return null if the entry was added. The current entry with the given object number and generation if the entry * was already present. */ public XrefEntry addIfAbsent(XrefEntry entry) { return data.putIfAbsent(entry.key(), entry); } /** * Adds the given entry to the {@link Xref} replacing any entry previously associated to the given object number and * generation. * * @param entry * @return the previous value or null if no entry was previously associated to the given object number and * generation. */ public XrefEntry add(XrefEntry entry) { return data.put(entry.key(), entry); } /** * @param objectKey * @return the {@link XrefEntry} for with the given object Number and generation number or null if nothing is found */ public XrefEntry get(COSObjectKey objectKey) { return data.get(objectKey); } /** * @return an unmodifiable view of the entries in this xref */ public Collection values() { return Collections.unmodifiableCollection(data.values()); } /** * @return the highest key in this xref */ public COSObjectKey highestKey() { return new TreeSet<>(data.keySet()).last(); } /** * @param objectKey * @return true if a value for the given key is registered to this xref */ public boolean contains(COSObjectKey objectKey) { return data.containsKey(objectKey); } } sambox-1.1.19/src/main/java/org/sejda/sambox/xref/XrefEntry.java000066400000000000000000000136151320103431700244330ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.xref; import static org.sejda.util.RequireUtils.requireArg; import java.util.Locale; import org.sejda.sambox.cos.COSObjectKey; /** * Entry of the xref table or stream * * @author Andrea Vacondio */ public class XrefEntry { private static final String XREFTABLE_ENTRY_FORMAT = "%010d %05d %c\r\n"; public static final XrefEntry DEFAULT_FREE_ENTRY = freeEntry(0, 65535); public static final long UNKNOWN_OFFSET = -1; private XrefType type; private COSObjectKey key; private long byteOffset; XrefEntry(XrefType type, long objectNumber, long byteOffset, int generationNumber) { requireArg(objectNumber >= 0 && generationNumber >= 0, "Object number and generation number cannot be negative"); this.type = type; this.key = new COSObjectKey(objectNumber, generationNumber); this.byteOffset = byteOffset; } public XrefType getType() { return type; } public long getByteOffset() { return byteOffset; } public void setByteOffset(long byteOffset) { this.byteOffset = byteOffset; } public long getObjectNumber() { return key.objectNumber(); } public int getGenerationNumber() { return key.generation(); } public boolean isUnknownOffset() { return this.byteOffset <= UNKNOWN_OFFSET; } public COSObjectKey key() { return key; } /** * @param entry * @return true if the given input entry is part of an object stream and this is the entry representing that object * stream. */ public boolean owns(XrefEntry entry) { return entry != null && entry.getType() == XrefType.COMPRESSED && key.objectNumber() == ((CompressedXrefEntry) entry).getObjectStreamNumber(); } @Override public String toString() { return String.format("%s offset=%d, %s", type.toString(), byteOffset, key.toString()); } /** * @return a xref table line corresponding to this entry * @throws IllegalArgumentException if the entry is a compressed one */ public String toXrefTableEntry() { switch (type) { case IN_USE: return String.format(Locale.US, XREFTABLE_ENTRY_FORMAT, getByteOffset(), getGenerationNumber(), 'n'); case FREE: return String.format(Locale.US, XREFTABLE_ENTRY_FORMAT, getObjectNumber(), getGenerationNumber(), 'f'); default: throw new IllegalArgumentException( "Only in_use and free entries can be written to an xref table"); } } /** * Creates Cross-reference stream data for this entry as defined in Chap 7.5.8.3 of PDF32000-1:2008, table 18. * * @param secondFieldLength length of the second field * @param thirdFieldLength length of the second field * @return an entry corresponding to this xref entry to be used in the xref stream. */ public byte[] toXrefStreamEntry(int secondFieldLength, int thirdFieldLength) { byte[] retVal = new byte[1 + secondFieldLength + thirdFieldLength]; if (type == XrefType.FREE) { retVal[0] = 0b00000000; copyBytesTo(key.objectNumber(), secondFieldLength, retVal, 1); copyBytesTo(key.generation(), thirdFieldLength, retVal, 1 + secondFieldLength); return retVal; } retVal[0] = 0b00000001; copyBytesTo(byteOffset, secondFieldLength, retVal, 1); copyBytesTo(key.generation(), thirdFieldLength, retVal, 1 + secondFieldLength); return retVal; } protected void copyBytesTo(long data, int length, byte[] destination, int destinationIndex) { for (int i = 0; i < length; i++) { destination[length + destinationIndex - i - 1] = (byte) (data & 0xFF); data >>= 8; } } /** * Factory method for an in use xref table/stream entry * * @param objectNumber * @param byteOffset * @param generationNumber * @return the newly created instance */ public static XrefEntry inUseEntry(long objectNumber, long byteOffset, int generationNumber) { return new XrefEntry(XrefType.IN_USE, objectNumber, byteOffset, generationNumber); } /** * Factory method for an in use xref table/stream entry with unknown offset * * @param objectNumber * @param generationNumber * @return the newly created instance */ public static XrefEntry unknownOffsetEntry(long objectNumber, int generationNumber) { return new XrefEntry(XrefType.IN_USE, objectNumber, UNKNOWN_OFFSET, generationNumber); } /** * Factory method for a free xref tabe/stream entry * * @param objectNumber * @param generationNumber * @return the newly created instance */ public static XrefEntry freeEntry(long objectNumber, int generationNumber) { return new XrefEntry(XrefType.FREE, objectNumber, UNKNOWN_OFFSET, generationNumber); } } sambox-1.1.19/src/main/java/org/sejda/sambox/xref/XrefType.java000066400000000000000000000017041320103431700242470ustar00rootroot00000000000000/* * 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. */ package org.sejda.sambox.xref; /** * Possible types of xref entry * * @author Andrea Vacondio * */ public enum XrefType { FREE, IN_USE, COMPRESSED; } sambox-1.1.19/src/main/resources/000077500000000000000000000000001320103431700166135ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/META-INF/000077500000000000000000000000001320103431700177535ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/META-INF/LICENSE000066400000000000000000000243641320103431700207710ustar00rootroot00000000000000 EXTERNAL COMPONENTS Apache PDFBox includes a number of components with separate copyright notices and license terms. Your use of these components is subject to the terms and conditions of the following licenses. Contributions made to the original PDFBox project: Copyright (c) 2002-2007, www.pdfbox.org All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of pdfbox; nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Adobe Font Metrics (AFM) for PDF Core 14 Fonts This file and the 14 PostScript(R) AFM files it accompanies may be used, copied, and distributed for any purpose and without charge, with or without modification, provided that all copyright notices are retained; that the AFM files are not distributed without this file; that all modifications to this file or any of the AFM files are prominently noted in the modified file(s); and that this paragraph is not modified. Adobe Systems has no responsibility or obligation to support the use of the AFM files. CMaps for PDF Fonts (http://opensource.adobe.com/wiki/display/cmap/Downloads) Copyright 1990-2009 Adobe Systems Incorporated. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Adobe Systems Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Glyphlist (http://www.adobe.com/devnet/opentype/archives/glyph.html) Copyright (c) 1997,1998,2002,2007 Adobe Systems Incorporated Permission is hereby granted, free of charge, to any person obtaining a copy of this documentation file to use, copy, publish, distribute, sublicense, and/or sell copies of the documentation, and to permit others to do the same, provided that: - No modification, editing or other alteration of this document is allowed; and - The above copyright notice and this permission notice shall be included in all copies of the documentation. Permission is hereby granted, free of charge, to any person obtaining a copy of this documentation file, to create their own derivative works from the content of this document to use, copy, publish, distribute, sublicense, and/or sell the derivative works, and to permit others to do the same, provided that the derived work is not represented as being a copy or version of this document. Adobe shall not be liable to any party for any loss of revenue or profit or for indirect, incidental, special, consequential, or other similar damages, whether based on tort (including without limitation negligence or strict liability), contract or other legal or equitable grounds even if Adobe has been advised or had reason to know of the possibility of such damages. The Adobe materials are provided on an "AS IS" basis. Adobe specifically disclaims all express, statutory, or implied warranties relating to the Adobe materials, including but not limited to those concerning merchantability or fitness for a particular purpose or non-infringement of any third party rights regarding the Adobe materials. Liberation Fonts (https://fedorahosted.org/liberation-fonts) Digitized data copyright (c) 2010 Google Corporation with Reserved Font Arimo, Tinos and Cousine. Copyright (c) 2012 Red Hat, Inc. with Reserved Font Name Liberation. This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting ? in part or in whole ? any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components,in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. sambox-1.1.19/src/main/resources/META-INF/NOTICE000066400000000000000000000007231320103431700206610ustar00rootroot00000000000000Based on source code contributed to the original PDFBox project. Copyright (c) 2002-2007, www.pdfbox.org Includes the Adobe Glyph List Copyright 1997, 1998, 2002, 2007, 2010 Adobe Systems Incorporated. Includes the Zapf Dingbats Glyph List Copyright 2002, 2010 Adobe Systems Incorporated. Includes the Bidi Mirroring Glyph Property (BidiMirroring-8.0.0.txt) Copyright 1991-2015 Unicode, Inc. Includes parts of TwelveMonkeys ImageIO Copyright 2008-2016 Harald Kuhrsambox-1.1.19/src/main/resources/org/000077500000000000000000000000001320103431700174025ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/000077500000000000000000000000001320103431700204705ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/000077500000000000000000000000001320103431700217615ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/000077500000000000000000000000001320103431700237735ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/000077500000000000000000000000001320103431700245365ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Courier-Bold.afm000066400000000000000000000364731320103431700275260ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Mon Jun 23 16:28:00 1997 Comment UniqueID 43048 Comment VMusage 41139 52164 FontName Courier-Bold FullName Courier Bold FamilyName Courier Weight Bold ItalicAngle 0 IsFixedPitch true CharacterSet ExtendedRoman FontBBox -113 -250 749 801 UnderlinePosition -100 UnderlineThickness 50 Version 003.000 Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. EncodingScheme AdobeStandardEncoding CapHeight 562 XHeight 439 Ascender 629 Descender -157 StdHW 84 StdVW 106 StartCharMetrics 315 C 32 ; WX 600 ; N space ; B 0 0 0 0 ; C 33 ; WX 600 ; N exclam ; B 202 -15 398 572 ; C 34 ; WX 600 ; N quotedbl ; B 135 277 465 562 ; C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ; C 36 ; WX 600 ; N dollar ; B 82 -126 519 666 ; C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ; C 38 ; WX 600 ; N ampersand ; B 36 -15 546 543 ; C 39 ; WX 600 ; N quoteright ; B 171 277 423 562 ; C 40 ; WX 600 ; N parenleft ; B 219 -102 461 616 ; C 41 ; WX 600 ; N parenright ; B 139 -102 381 616 ; C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ; C 43 ; WX 600 ; N plus ; B 71 39 529 478 ; C 44 ; WX 600 ; N comma ; B 123 -111 393 174 ; C 45 ; WX 600 ; N hyphen ; B 100 203 500 313 ; C 46 ; WX 600 ; N period ; B 192 -15 408 171 ; C 47 ; WX 600 ; N slash ; B 98 -77 502 626 ; C 48 ; WX 600 ; N zero ; B 87 -15 513 616 ; C 49 ; WX 600 ; N one ; B 81 0 539 616 ; C 50 ; WX 600 ; N two ; B 61 0 499 616 ; C 51 ; WX 600 ; N three ; B 63 -15 501 616 ; C 52 ; WX 600 ; N four ; B 53 0 507 616 ; C 53 ; WX 600 ; N five ; B 70 -15 521 601 ; C 54 ; WX 600 ; N six ; B 90 -15 521 616 ; C 55 ; WX 600 ; N seven ; B 55 0 494 601 ; C 56 ; WX 600 ; N eight ; B 83 -15 517 616 ; C 57 ; WX 600 ; N nine ; B 79 -15 510 616 ; C 58 ; WX 600 ; N colon ; B 191 -15 407 425 ; C 59 ; WX 600 ; N semicolon ; B 123 -111 408 425 ; C 60 ; WX 600 ; N less ; B 66 15 523 501 ; C 61 ; WX 600 ; N equal ; B 71 118 529 398 ; C 62 ; WX 600 ; N greater ; B 77 15 534 501 ; C 63 ; WX 600 ; N question ; B 98 -14 501 580 ; C 64 ; WX 600 ; N at ; B 16 -15 584 616 ; C 65 ; WX 600 ; N A ; B -9 0 609 562 ; C 66 ; WX 600 ; N B ; B 30 0 573 562 ; C 67 ; WX 600 ; N C ; B 22 -18 560 580 ; C 68 ; WX 600 ; N D ; B 30 0 594 562 ; C 69 ; WX 600 ; N E ; B 25 0 560 562 ; C 70 ; WX 600 ; N F ; B 39 0 570 562 ; C 71 ; WX 600 ; N G ; B 22 -18 594 580 ; C 72 ; WX 600 ; N H ; B 20 0 580 562 ; C 73 ; WX 600 ; N I ; B 77 0 523 562 ; C 74 ; WX 600 ; N J ; B 37 -18 601 562 ; C 75 ; WX 600 ; N K ; B 21 0 599 562 ; C 76 ; WX 600 ; N L ; B 39 0 578 562 ; C 77 ; WX 600 ; N M ; B -2 0 602 562 ; C 78 ; WX 600 ; N N ; B 8 -12 610 562 ; C 79 ; WX 600 ; N O ; B 22 -18 578 580 ; C 80 ; WX 600 ; N P ; B 48 0 559 562 ; C 81 ; WX 600 ; N Q ; B 32 -138 578 580 ; C 82 ; WX 600 ; N R ; B 24 0 599 562 ; C 83 ; WX 600 ; N S ; B 47 -22 553 582 ; C 84 ; WX 600 ; N T ; B 21 0 579 562 ; C 85 ; WX 600 ; N U ; B 4 -18 596 562 ; C 86 ; WX 600 ; N V ; B -13 0 613 562 ; C 87 ; WX 600 ; N W ; B -18 0 618 562 ; C 88 ; WX 600 ; N X ; B 12 0 588 562 ; C 89 ; WX 600 ; N Y ; B 12 0 589 562 ; C 90 ; WX 600 ; N Z ; B 62 0 539 562 ; C 91 ; WX 600 ; N bracketleft ; B 245 -102 475 616 ; C 92 ; WX 600 ; N backslash ; B 99 -77 503 626 ; C 93 ; WX 600 ; N bracketright ; B 125 -102 355 616 ; C 94 ; WX 600 ; N asciicircum ; B 108 250 492 616 ; C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ; C 96 ; WX 600 ; N quoteleft ; B 178 277 428 562 ; C 97 ; WX 600 ; N a ; B 35 -15 570 454 ; C 98 ; WX 600 ; N b ; B 0 -15 584 626 ; C 99 ; WX 600 ; N c ; B 40 -15 545 459 ; C 100 ; WX 600 ; N d ; B 20 -15 591 626 ; C 101 ; WX 600 ; N e ; B 40 -15 563 454 ; C 102 ; WX 600 ; N f ; B 83 0 547 626 ; L i fi ; L l fl ; C 103 ; WX 600 ; N g ; B 30 -146 580 454 ; C 104 ; WX 600 ; N h ; B 5 0 592 626 ; C 105 ; WX 600 ; N i ; B 77 0 523 658 ; C 106 ; WX 600 ; N j ; B 63 -146 440 658 ; C 107 ; WX 600 ; N k ; B 20 0 585 626 ; C 108 ; WX 600 ; N l ; B 77 0 523 626 ; C 109 ; WX 600 ; N m ; B -22 0 626 454 ; C 110 ; WX 600 ; N n ; B 18 0 592 454 ; C 111 ; WX 600 ; N o ; B 30 -15 570 454 ; C 112 ; WX 600 ; N p ; B -1 -142 570 454 ; C 113 ; WX 600 ; N q ; B 20 -142 591 454 ; C 114 ; WX 600 ; N r ; B 47 0 580 454 ; C 115 ; WX 600 ; N s ; B 68 -17 535 459 ; C 116 ; WX 600 ; N t ; B 47 -15 532 562 ; C 117 ; WX 600 ; N u ; B -1 -15 569 439 ; C 118 ; WX 600 ; N v ; B -1 0 601 439 ; C 119 ; WX 600 ; N w ; B -18 0 618 439 ; C 120 ; WX 600 ; N x ; B 6 0 594 439 ; C 121 ; WX 600 ; N y ; B -4 -142 601 439 ; C 122 ; WX 600 ; N z ; B 81 0 520 439 ; C 123 ; WX 600 ; N braceleft ; B 160 -102 464 616 ; C 124 ; WX 600 ; N bar ; B 255 -250 345 750 ; C 125 ; WX 600 ; N braceright ; B 136 -102 440 616 ; C 126 ; WX 600 ; N asciitilde ; B 71 153 530 356 ; C 161 ; WX 600 ; N exclamdown ; B 202 -146 398 449 ; C 162 ; WX 600 ; N cent ; B 66 -49 518 614 ; C 163 ; WX 600 ; N sterling ; B 72 -28 558 611 ; C 164 ; WX 600 ; N fraction ; B 25 -60 576 661 ; C 165 ; WX 600 ; N yen ; B 10 0 590 562 ; C 166 ; WX 600 ; N florin ; B -30 -131 572 616 ; C 167 ; WX 600 ; N section ; B 83 -70 517 580 ; C 168 ; WX 600 ; N currency ; B 54 49 546 517 ; C 169 ; WX 600 ; N quotesingle ; B 227 277 373 562 ; C 170 ; WX 600 ; N quotedblleft ; B 71 277 535 562 ; C 171 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ; C 172 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ; C 173 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ; C 174 ; WX 600 ; N fi ; B 12 0 593 626 ; C 175 ; WX 600 ; N fl ; B 12 0 593 626 ; C 177 ; WX 600 ; N endash ; B 65 203 535 313 ; C 178 ; WX 600 ; N dagger ; B 106 -70 494 580 ; C 179 ; WX 600 ; N daggerdbl ; B 106 -70 494 580 ; C 180 ; WX 600 ; N periodcentered ; B 196 165 404 351 ; C 182 ; WX 600 ; N paragraph ; B 6 -70 576 580 ; C 183 ; WX 600 ; N bullet ; B 140 132 460 430 ; C 184 ; WX 600 ; N quotesinglbase ; B 175 -142 427 143 ; C 185 ; WX 600 ; N quotedblbase ; B 65 -142 529 143 ; C 186 ; WX 600 ; N quotedblright ; B 61 277 525 562 ; C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ; C 188 ; WX 600 ; N ellipsis ; B 26 -15 574 116 ; C 189 ; WX 600 ; N perthousand ; B -113 -15 713 616 ; C 191 ; WX 600 ; N questiondown ; B 99 -146 502 449 ; C 193 ; WX 600 ; N grave ; B 132 508 395 661 ; C 194 ; WX 600 ; N acute ; B 205 508 468 661 ; C 195 ; WX 600 ; N circumflex ; B 103 483 497 657 ; C 196 ; WX 600 ; N tilde ; B 89 493 512 636 ; C 197 ; WX 600 ; N macron ; B 88 505 512 585 ; C 198 ; WX 600 ; N breve ; B 83 468 517 631 ; C 199 ; WX 600 ; N dotaccent ; B 230 498 370 638 ; C 200 ; WX 600 ; N dieresis ; B 128 498 472 638 ; C 202 ; WX 600 ; N ring ; B 198 481 402 678 ; C 203 ; WX 600 ; N cedilla ; B 205 -206 387 0 ; C 205 ; WX 600 ; N hungarumlaut ; B 68 488 588 661 ; C 206 ; WX 600 ; N ogonek ; B 169 -199 400 0 ; C 207 ; WX 600 ; N caron ; B 103 493 497 667 ; C 208 ; WX 600 ; N emdash ; B -10 203 610 313 ; C 225 ; WX 600 ; N AE ; B -29 0 602 562 ; C 227 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ; C 232 ; WX 600 ; N Lslash ; B 39 0 578 562 ; C 233 ; WX 600 ; N Oslash ; B 22 -22 578 584 ; C 234 ; WX 600 ; N OE ; B -25 0 595 562 ; C 235 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ; C 241 ; WX 600 ; N ae ; B -4 -15 601 454 ; C 245 ; WX 600 ; N dotlessi ; B 77 0 523 439 ; C 248 ; WX 600 ; N lslash ; B 77 0 523 626 ; C 249 ; WX 600 ; N oslash ; B 30 -24 570 463 ; C 250 ; WX 600 ; N oe ; B -18 -15 611 454 ; C 251 ; WX 600 ; N germandbls ; B 22 -15 596 626 ; C -1 ; WX 600 ; N Idieresis ; B 77 0 523 761 ; C -1 ; WX 600 ; N eacute ; B 40 -15 563 661 ; C -1 ; WX 600 ; N abreve ; B 35 -15 570 661 ; C -1 ; WX 600 ; N uhungarumlaut ; B -1 -15 628 661 ; C -1 ; WX 600 ; N ecaron ; B 40 -15 563 667 ; C -1 ; WX 600 ; N Ydieresis ; B 12 0 589 761 ; C -1 ; WX 600 ; N divide ; B 71 16 529 500 ; C -1 ; WX 600 ; N Yacute ; B 12 0 589 784 ; C -1 ; WX 600 ; N Acircumflex ; B -9 0 609 780 ; C -1 ; WX 600 ; N aacute ; B 35 -15 570 661 ; C -1 ; WX 600 ; N Ucircumflex ; B 4 -18 596 780 ; C -1 ; WX 600 ; N yacute ; B -4 -142 601 661 ; C -1 ; WX 600 ; N scommaaccent ; B 68 -250 535 459 ; C -1 ; WX 600 ; N ecircumflex ; B 40 -15 563 657 ; C -1 ; WX 600 ; N Uring ; B 4 -18 596 801 ; C -1 ; WX 600 ; N Udieresis ; B 4 -18 596 761 ; C -1 ; WX 600 ; N aogonek ; B 35 -199 586 454 ; C -1 ; WX 600 ; N Uacute ; B 4 -18 596 784 ; C -1 ; WX 600 ; N uogonek ; B -1 -199 585 439 ; C -1 ; WX 600 ; N Edieresis ; B 25 0 560 761 ; C -1 ; WX 600 ; N Dcroat ; B 30 0 594 562 ; C -1 ; WX 600 ; N commaaccent ; B 205 -250 397 -57 ; C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ; C -1 ; WX 600 ; N Emacron ; B 25 0 560 708 ; C -1 ; WX 600 ; N ccaron ; B 40 -15 545 667 ; C -1 ; WX 600 ; N aring ; B 35 -15 570 678 ; C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 610 562 ; C -1 ; WX 600 ; N lacute ; B 77 0 523 801 ; C -1 ; WX 600 ; N agrave ; B 35 -15 570 661 ; C -1 ; WX 600 ; N Tcommaaccent ; B 21 -250 579 562 ; C -1 ; WX 600 ; N Cacute ; B 22 -18 560 784 ; C -1 ; WX 600 ; N atilde ; B 35 -15 570 636 ; C -1 ; WX 600 ; N Edotaccent ; B 25 0 560 761 ; C -1 ; WX 600 ; N scaron ; B 68 -17 535 667 ; C -1 ; WX 600 ; N scedilla ; B 68 -206 535 459 ; C -1 ; WX 600 ; N iacute ; B 77 0 523 661 ; C -1 ; WX 600 ; N lozenge ; B 66 0 534 740 ; C -1 ; WX 600 ; N Rcaron ; B 24 0 599 790 ; C -1 ; WX 600 ; N Gcommaaccent ; B 22 -250 594 580 ; C -1 ; WX 600 ; N ucircumflex ; B -1 -15 569 657 ; C -1 ; WX 600 ; N acircumflex ; B 35 -15 570 657 ; C -1 ; WX 600 ; N Amacron ; B -9 0 609 708 ; C -1 ; WX 600 ; N rcaron ; B 47 0 580 667 ; C -1 ; WX 600 ; N ccedilla ; B 40 -206 545 459 ; C -1 ; WX 600 ; N Zdotaccent ; B 62 0 539 761 ; C -1 ; WX 600 ; N Thorn ; B 48 0 557 562 ; C -1 ; WX 600 ; N Omacron ; B 22 -18 578 708 ; C -1 ; WX 600 ; N Racute ; B 24 0 599 784 ; C -1 ; WX 600 ; N Sacute ; B 47 -22 553 784 ; C -1 ; WX 600 ; N dcaron ; B 20 -15 727 626 ; C -1 ; WX 600 ; N Umacron ; B 4 -18 596 708 ; C -1 ; WX 600 ; N uring ; B -1 -15 569 678 ; C -1 ; WX 600 ; N threesuperior ; B 138 222 433 616 ; C -1 ; WX 600 ; N Ograve ; B 22 -18 578 784 ; C -1 ; WX 600 ; N Agrave ; B -9 0 609 784 ; C -1 ; WX 600 ; N Abreve ; B -9 0 609 784 ; C -1 ; WX 600 ; N multiply ; B 81 39 520 478 ; C -1 ; WX 600 ; N uacute ; B -1 -15 569 661 ; C -1 ; WX 600 ; N Tcaron ; B 21 0 579 790 ; C -1 ; WX 600 ; N partialdiff ; B 63 -38 537 728 ; C -1 ; WX 600 ; N ydieresis ; B -4 -142 601 638 ; C -1 ; WX 600 ; N Nacute ; B 8 -12 610 784 ; C -1 ; WX 600 ; N icircumflex ; B 73 0 523 657 ; C -1 ; WX 600 ; N Ecircumflex ; B 25 0 560 780 ; C -1 ; WX 600 ; N adieresis ; B 35 -15 570 638 ; C -1 ; WX 600 ; N edieresis ; B 40 -15 563 638 ; C -1 ; WX 600 ; N cacute ; B 40 -15 545 661 ; C -1 ; WX 600 ; N nacute ; B 18 0 592 661 ; C -1 ; WX 600 ; N umacron ; B -1 -15 569 585 ; C -1 ; WX 600 ; N Ncaron ; B 8 -12 610 790 ; C -1 ; WX 600 ; N Iacute ; B 77 0 523 784 ; C -1 ; WX 600 ; N plusminus ; B 71 24 529 515 ; C -1 ; WX 600 ; N brokenbar ; B 255 -175 345 675 ; C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ; C -1 ; WX 600 ; N Gbreve ; B 22 -18 594 784 ; C -1 ; WX 600 ; N Idotaccent ; B 77 0 523 761 ; C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ; C -1 ; WX 600 ; N Egrave ; B 25 0 560 784 ; C -1 ; WX 600 ; N racute ; B 47 0 580 661 ; C -1 ; WX 600 ; N omacron ; B 30 -15 570 585 ; C -1 ; WX 600 ; N Zacute ; B 62 0 539 784 ; C -1 ; WX 600 ; N Zcaron ; B 62 0 539 790 ; C -1 ; WX 600 ; N greaterequal ; B 26 0 523 696 ; C -1 ; WX 600 ; N Eth ; B 30 0 594 562 ; C -1 ; WX 600 ; N Ccedilla ; B 22 -206 560 580 ; C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 523 626 ; C -1 ; WX 600 ; N tcaron ; B 47 -15 532 703 ; C -1 ; WX 600 ; N eogonek ; B 40 -199 563 454 ; C -1 ; WX 600 ; N Uogonek ; B 4 -199 596 562 ; C -1 ; WX 600 ; N Aacute ; B -9 0 609 784 ; C -1 ; WX 600 ; N Adieresis ; B -9 0 609 761 ; C -1 ; WX 600 ; N egrave ; B 40 -15 563 661 ; C -1 ; WX 600 ; N zacute ; B 81 0 520 661 ; C -1 ; WX 600 ; N iogonek ; B 77 -199 523 658 ; C -1 ; WX 600 ; N Oacute ; B 22 -18 578 784 ; C -1 ; WX 600 ; N oacute ; B 30 -15 570 661 ; C -1 ; WX 600 ; N amacron ; B 35 -15 570 585 ; C -1 ; WX 600 ; N sacute ; B 68 -17 535 661 ; C -1 ; WX 600 ; N idieresis ; B 77 0 523 618 ; C -1 ; WX 600 ; N Ocircumflex ; B 22 -18 578 780 ; C -1 ; WX 600 ; N Ugrave ; B 4 -18 596 784 ; C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ; C -1 ; WX 600 ; N thorn ; B -14 -142 570 626 ; C -1 ; WX 600 ; N twosuperior ; B 143 230 436 616 ; C -1 ; WX 600 ; N Odieresis ; B 22 -18 578 761 ; C -1 ; WX 600 ; N mu ; B -1 -142 569 439 ; C -1 ; WX 600 ; N igrave ; B 77 0 523 661 ; C -1 ; WX 600 ; N ohungarumlaut ; B 30 -15 668 661 ; C -1 ; WX 600 ; N Eogonek ; B 25 -199 576 562 ; C -1 ; WX 600 ; N dcroat ; B 20 -15 591 626 ; C -1 ; WX 600 ; N threequarters ; B -47 -60 648 661 ; C -1 ; WX 600 ; N Scedilla ; B 47 -206 553 582 ; C -1 ; WX 600 ; N lcaron ; B 77 0 597 626 ; C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 599 562 ; C -1 ; WX 600 ; N Lacute ; B 39 0 578 784 ; C -1 ; WX 600 ; N trademark ; B -9 230 749 562 ; C -1 ; WX 600 ; N edotaccent ; B 40 -15 563 638 ; C -1 ; WX 600 ; N Igrave ; B 77 0 523 784 ; C -1 ; WX 600 ; N Imacron ; B 77 0 523 708 ; C -1 ; WX 600 ; N Lcaron ; B 39 0 637 562 ; C -1 ; WX 600 ; N onehalf ; B -47 -60 648 661 ; C -1 ; WX 600 ; N lessequal ; B 26 0 523 696 ; C -1 ; WX 600 ; N ocircumflex ; B 30 -15 570 657 ; C -1 ; WX 600 ; N ntilde ; B 18 0 592 636 ; C -1 ; WX 600 ; N Uhungarumlaut ; B 4 -18 638 784 ; C -1 ; WX 600 ; N Eacute ; B 25 0 560 784 ; C -1 ; WX 600 ; N emacron ; B 40 -15 563 585 ; C -1 ; WX 600 ; N gbreve ; B 30 -146 580 661 ; C -1 ; WX 600 ; N onequarter ; B -56 -60 656 661 ; C -1 ; WX 600 ; N Scaron ; B 47 -22 553 790 ; C -1 ; WX 600 ; N Scommaaccent ; B 47 -250 553 582 ; C -1 ; WX 600 ; N Ohungarumlaut ; B 22 -18 628 784 ; C -1 ; WX 600 ; N degree ; B 86 243 474 616 ; C -1 ; WX 600 ; N ograve ; B 30 -15 570 661 ; C -1 ; WX 600 ; N Ccaron ; B 22 -18 560 790 ; C -1 ; WX 600 ; N ugrave ; B -1 -15 569 661 ; C -1 ; WX 600 ; N radical ; B -19 -104 473 778 ; C -1 ; WX 600 ; N Dcaron ; B 30 0 594 790 ; C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 580 454 ; C -1 ; WX 600 ; N Ntilde ; B 8 -12 610 759 ; C -1 ; WX 600 ; N otilde ; B 30 -15 570 636 ; C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 599 562 ; C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 578 562 ; C -1 ; WX 600 ; N Atilde ; B -9 0 609 759 ; C -1 ; WX 600 ; N Aogonek ; B -9 -199 625 562 ; C -1 ; WX 600 ; N Aring ; B -9 0 609 801 ; C -1 ; WX 600 ; N Otilde ; B 22 -18 578 759 ; C -1 ; WX 600 ; N zdotaccent ; B 81 0 520 638 ; C -1 ; WX 600 ; N Ecaron ; B 25 0 560 790 ; C -1 ; WX 600 ; N Iogonek ; B 77 -199 523 562 ; C -1 ; WX 600 ; N kcommaaccent ; B 20 -250 585 626 ; C -1 ; WX 600 ; N minus ; B 71 203 529 313 ; C -1 ; WX 600 ; N Icircumflex ; B 77 0 523 780 ; C -1 ; WX 600 ; N ncaron ; B 18 0 592 667 ; C -1 ; WX 600 ; N tcommaaccent ; B 47 -250 532 562 ; C -1 ; WX 600 ; N logicalnot ; B 71 103 529 413 ; C -1 ; WX 600 ; N odieresis ; B 30 -15 570 638 ; C -1 ; WX 600 ; N udieresis ; B -1 -15 569 638 ; C -1 ; WX 600 ; N notequal ; B 12 -47 537 563 ; C -1 ; WX 600 ; N gcommaaccent ; B 30 -146 580 714 ; C -1 ; WX 600 ; N eth ; B 58 -27 543 626 ; C -1 ; WX 600 ; N zcaron ; B 81 0 520 667 ; C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 592 454 ; C -1 ; WX 600 ; N onesuperior ; B 153 230 447 616 ; C -1 ; WX 600 ; N imacron ; B 77 0 523 585 ; C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ; EndCharMetrics EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Courier-BoldOblique.afm000066400000000000000000000365751320103431700310520ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Mon Jun 23 16:28:46 1997 Comment UniqueID 43049 Comment VMusage 17529 79244 FontName Courier-BoldOblique FullName Courier Bold Oblique FamilyName Courier Weight Bold ItalicAngle -12 IsFixedPitch true CharacterSet ExtendedRoman FontBBox -57 -250 869 801 UnderlinePosition -100 UnderlineThickness 50 Version 003.000 Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. EncodingScheme AdobeStandardEncoding CapHeight 562 XHeight 439 Ascender 629 Descender -157 StdHW 84 StdVW 106 StartCharMetrics 315 C 32 ; WX 600 ; N space ; B 0 0 0 0 ; C 33 ; WX 600 ; N exclam ; B 215 -15 495 572 ; C 34 ; WX 600 ; N quotedbl ; B 211 277 585 562 ; C 35 ; WX 600 ; N numbersign ; B 88 -45 641 651 ; C 36 ; WX 600 ; N dollar ; B 87 -126 630 666 ; C 37 ; WX 600 ; N percent ; B 101 -15 625 616 ; C 38 ; WX 600 ; N ampersand ; B 61 -15 595 543 ; C 39 ; WX 600 ; N quoteright ; B 229 277 543 562 ; C 40 ; WX 600 ; N parenleft ; B 265 -102 592 616 ; C 41 ; WX 600 ; N parenright ; B 117 -102 444 616 ; C 42 ; WX 600 ; N asterisk ; B 179 219 598 601 ; C 43 ; WX 600 ; N plus ; B 114 39 596 478 ; C 44 ; WX 600 ; N comma ; B 99 -111 430 174 ; C 45 ; WX 600 ; N hyphen ; B 143 203 567 313 ; C 46 ; WX 600 ; N period ; B 206 -15 427 171 ; C 47 ; WX 600 ; N slash ; B 90 -77 626 626 ; C 48 ; WX 600 ; N zero ; B 135 -15 593 616 ; C 49 ; WX 600 ; N one ; B 93 0 562 616 ; C 50 ; WX 600 ; N two ; B 61 0 594 616 ; C 51 ; WX 600 ; N three ; B 71 -15 571 616 ; C 52 ; WX 600 ; N four ; B 81 0 559 616 ; C 53 ; WX 600 ; N five ; B 77 -15 621 601 ; C 54 ; WX 600 ; N six ; B 135 -15 652 616 ; C 55 ; WX 600 ; N seven ; B 147 0 622 601 ; C 56 ; WX 600 ; N eight ; B 115 -15 604 616 ; C 57 ; WX 600 ; N nine ; B 75 -15 592 616 ; C 58 ; WX 600 ; N colon ; B 205 -15 480 425 ; C 59 ; WX 600 ; N semicolon ; B 99 -111 481 425 ; C 60 ; WX 600 ; N less ; B 120 15 613 501 ; C 61 ; WX 600 ; N equal ; B 96 118 614 398 ; C 62 ; WX 600 ; N greater ; B 97 15 589 501 ; C 63 ; WX 600 ; N question ; B 183 -14 592 580 ; C 64 ; WX 600 ; N at ; B 65 -15 642 616 ; C 65 ; WX 600 ; N A ; B -9 0 632 562 ; C 66 ; WX 600 ; N B ; B 30 0 630 562 ; C 67 ; WX 600 ; N C ; B 74 -18 675 580 ; C 68 ; WX 600 ; N D ; B 30 0 664 562 ; C 69 ; WX 600 ; N E ; B 25 0 670 562 ; C 70 ; WX 600 ; N F ; B 39 0 684 562 ; C 71 ; WX 600 ; N G ; B 74 -18 675 580 ; C 72 ; WX 600 ; N H ; B 20 0 700 562 ; C 73 ; WX 600 ; N I ; B 77 0 643 562 ; C 74 ; WX 600 ; N J ; B 58 -18 721 562 ; C 75 ; WX 600 ; N K ; B 21 0 692 562 ; C 76 ; WX 600 ; N L ; B 39 0 636 562 ; C 77 ; WX 600 ; N M ; B -2 0 722 562 ; C 78 ; WX 600 ; N N ; B 8 -12 730 562 ; C 79 ; WX 600 ; N O ; B 74 -18 645 580 ; C 80 ; WX 600 ; N P ; B 48 0 643 562 ; C 81 ; WX 600 ; N Q ; B 83 -138 636 580 ; C 82 ; WX 600 ; N R ; B 24 0 617 562 ; C 83 ; WX 600 ; N S ; B 54 -22 673 582 ; C 84 ; WX 600 ; N T ; B 86 0 679 562 ; C 85 ; WX 600 ; N U ; B 101 -18 716 562 ; C 86 ; WX 600 ; N V ; B 84 0 733 562 ; C 87 ; WX 600 ; N W ; B 79 0 738 562 ; C 88 ; WX 600 ; N X ; B 12 0 690 562 ; C 89 ; WX 600 ; N Y ; B 109 0 709 562 ; C 90 ; WX 600 ; N Z ; B 62 0 637 562 ; C 91 ; WX 600 ; N bracketleft ; B 223 -102 606 616 ; C 92 ; WX 600 ; N backslash ; B 222 -77 496 626 ; C 93 ; WX 600 ; N bracketright ; B 103 -102 486 616 ; C 94 ; WX 600 ; N asciicircum ; B 171 250 556 616 ; C 95 ; WX 600 ; N underscore ; B -27 -125 585 -75 ; C 96 ; WX 600 ; N quoteleft ; B 297 277 487 562 ; C 97 ; WX 600 ; N a ; B 61 -15 593 454 ; C 98 ; WX 600 ; N b ; B 13 -15 636 626 ; C 99 ; WX 600 ; N c ; B 81 -15 631 459 ; C 100 ; WX 600 ; N d ; B 60 -15 645 626 ; C 101 ; WX 600 ; N e ; B 81 -15 605 454 ; C 102 ; WX 600 ; N f ; B 83 0 677 626 ; L i fi ; L l fl ; C 103 ; WX 600 ; N g ; B 40 -146 674 454 ; C 104 ; WX 600 ; N h ; B 18 0 615 626 ; C 105 ; WX 600 ; N i ; B 77 0 546 658 ; C 106 ; WX 600 ; N j ; B 36 -146 580 658 ; C 107 ; WX 600 ; N k ; B 33 0 643 626 ; C 108 ; WX 600 ; N l ; B 77 0 546 626 ; C 109 ; WX 600 ; N m ; B -22 0 649 454 ; C 110 ; WX 600 ; N n ; B 18 0 615 454 ; C 111 ; WX 600 ; N o ; B 71 -15 622 454 ; C 112 ; WX 600 ; N p ; B -32 -142 622 454 ; C 113 ; WX 600 ; N q ; B 60 -142 685 454 ; C 114 ; WX 600 ; N r ; B 47 0 655 454 ; C 115 ; WX 600 ; N s ; B 66 -17 608 459 ; C 116 ; WX 600 ; N t ; B 118 -15 567 562 ; C 117 ; WX 600 ; N u ; B 70 -15 592 439 ; C 118 ; WX 600 ; N v ; B 70 0 695 439 ; C 119 ; WX 600 ; N w ; B 53 0 712 439 ; C 120 ; WX 600 ; N x ; B 6 0 671 439 ; C 121 ; WX 600 ; N y ; B -21 -142 695 439 ; C 122 ; WX 600 ; N z ; B 81 0 614 439 ; C 123 ; WX 600 ; N braceleft ; B 203 -102 595 616 ; C 124 ; WX 600 ; N bar ; B 201 -250 505 750 ; C 125 ; WX 600 ; N braceright ; B 114 -102 506 616 ; C 126 ; WX 600 ; N asciitilde ; B 120 153 590 356 ; C 161 ; WX 600 ; N exclamdown ; B 196 -146 477 449 ; C 162 ; WX 600 ; N cent ; B 121 -49 605 614 ; C 163 ; WX 600 ; N sterling ; B 106 -28 650 611 ; C 164 ; WX 600 ; N fraction ; B 22 -60 708 661 ; C 165 ; WX 600 ; N yen ; B 98 0 710 562 ; C 166 ; WX 600 ; N florin ; B -57 -131 702 616 ; C 167 ; WX 600 ; N section ; B 74 -70 620 580 ; C 168 ; WX 600 ; N currency ; B 77 49 644 517 ; C 169 ; WX 600 ; N quotesingle ; B 303 277 493 562 ; C 170 ; WX 600 ; N quotedblleft ; B 190 277 594 562 ; C 171 ; WX 600 ; N guillemotleft ; B 62 70 639 446 ; C 172 ; WX 600 ; N guilsinglleft ; B 195 70 545 446 ; C 173 ; WX 600 ; N guilsinglright ; B 165 70 514 446 ; C 174 ; WX 600 ; N fi ; B 12 0 644 626 ; C 175 ; WX 600 ; N fl ; B 12 0 644 626 ; C 177 ; WX 600 ; N endash ; B 108 203 602 313 ; C 178 ; WX 600 ; N dagger ; B 175 -70 586 580 ; C 179 ; WX 600 ; N daggerdbl ; B 121 -70 587 580 ; C 180 ; WX 600 ; N periodcentered ; B 248 165 461 351 ; C 182 ; WX 600 ; N paragraph ; B 61 -70 700 580 ; C 183 ; WX 600 ; N bullet ; B 196 132 523 430 ; C 184 ; WX 600 ; N quotesinglbase ; B 144 -142 458 143 ; C 185 ; WX 600 ; N quotedblbase ; B 34 -142 560 143 ; C 186 ; WX 600 ; N quotedblright ; B 119 277 645 562 ; C 187 ; WX 600 ; N guillemotright ; B 71 70 647 446 ; C 188 ; WX 600 ; N ellipsis ; B 35 -15 587 116 ; C 189 ; WX 600 ; N perthousand ; B -45 -15 743 616 ; C 191 ; WX 600 ; N questiondown ; B 100 -146 509 449 ; C 193 ; WX 600 ; N grave ; B 272 508 503 661 ; C 194 ; WX 600 ; N acute ; B 312 508 609 661 ; C 195 ; WX 600 ; N circumflex ; B 212 483 607 657 ; C 196 ; WX 600 ; N tilde ; B 199 493 643 636 ; C 197 ; WX 600 ; N macron ; B 195 505 637 585 ; C 198 ; WX 600 ; N breve ; B 217 468 652 631 ; C 199 ; WX 600 ; N dotaccent ; B 348 498 493 638 ; C 200 ; WX 600 ; N dieresis ; B 246 498 595 638 ; C 202 ; WX 600 ; N ring ; B 319 481 528 678 ; C 203 ; WX 600 ; N cedilla ; B 168 -206 368 0 ; C 205 ; WX 600 ; N hungarumlaut ; B 171 488 729 661 ; C 206 ; WX 600 ; N ogonek ; B 143 -199 367 0 ; C 207 ; WX 600 ; N caron ; B 238 493 633 667 ; C 208 ; WX 600 ; N emdash ; B 33 203 677 313 ; C 225 ; WX 600 ; N AE ; B -29 0 708 562 ; C 227 ; WX 600 ; N ordfeminine ; B 188 196 526 580 ; C 232 ; WX 600 ; N Lslash ; B 39 0 636 562 ; C 233 ; WX 600 ; N Oslash ; B 48 -22 673 584 ; C 234 ; WX 600 ; N OE ; B 26 0 701 562 ; C 235 ; WX 600 ; N ordmasculine ; B 188 196 543 580 ; C 241 ; WX 600 ; N ae ; B 21 -15 652 454 ; C 245 ; WX 600 ; N dotlessi ; B 77 0 546 439 ; C 248 ; WX 600 ; N lslash ; B 77 0 587 626 ; C 249 ; WX 600 ; N oslash ; B 54 -24 638 463 ; C 250 ; WX 600 ; N oe ; B 18 -15 662 454 ; C 251 ; WX 600 ; N germandbls ; B 22 -15 629 626 ; C -1 ; WX 600 ; N Idieresis ; B 77 0 643 761 ; C -1 ; WX 600 ; N eacute ; B 81 -15 609 661 ; C -1 ; WX 600 ; N abreve ; B 61 -15 658 661 ; C -1 ; WX 600 ; N uhungarumlaut ; B 70 -15 769 661 ; C -1 ; WX 600 ; N ecaron ; B 81 -15 633 667 ; C -1 ; WX 600 ; N Ydieresis ; B 109 0 709 761 ; C -1 ; WX 600 ; N divide ; B 114 16 596 500 ; C -1 ; WX 600 ; N Yacute ; B 109 0 709 784 ; C -1 ; WX 600 ; N Acircumflex ; B -9 0 632 780 ; C -1 ; WX 600 ; N aacute ; B 61 -15 609 661 ; C -1 ; WX 600 ; N Ucircumflex ; B 101 -18 716 780 ; C -1 ; WX 600 ; N yacute ; B -21 -142 695 661 ; C -1 ; WX 600 ; N scommaaccent ; B 66 -250 608 459 ; C -1 ; WX 600 ; N ecircumflex ; B 81 -15 607 657 ; C -1 ; WX 600 ; N Uring ; B 101 -18 716 801 ; C -1 ; WX 600 ; N Udieresis ; B 101 -18 716 761 ; C -1 ; WX 600 ; N aogonek ; B 61 -199 593 454 ; C -1 ; WX 600 ; N Uacute ; B 101 -18 716 784 ; C -1 ; WX 600 ; N uogonek ; B 70 -199 592 439 ; C -1 ; WX 600 ; N Edieresis ; B 25 0 670 761 ; C -1 ; WX 600 ; N Dcroat ; B 30 0 664 562 ; C -1 ; WX 600 ; N commaaccent ; B 151 -250 385 -57 ; C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ; C -1 ; WX 600 ; N Emacron ; B 25 0 670 708 ; C -1 ; WX 600 ; N ccaron ; B 81 -15 633 667 ; C -1 ; WX 600 ; N aring ; B 61 -15 593 678 ; C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 730 562 ; C -1 ; WX 600 ; N lacute ; B 77 0 639 801 ; C -1 ; WX 600 ; N agrave ; B 61 -15 593 661 ; C -1 ; WX 600 ; N Tcommaaccent ; B 86 -250 679 562 ; C -1 ; WX 600 ; N Cacute ; B 74 -18 675 784 ; C -1 ; WX 600 ; N atilde ; B 61 -15 643 636 ; C -1 ; WX 600 ; N Edotaccent ; B 25 0 670 761 ; C -1 ; WX 600 ; N scaron ; B 66 -17 633 667 ; C -1 ; WX 600 ; N scedilla ; B 66 -206 608 459 ; C -1 ; WX 600 ; N iacute ; B 77 0 609 661 ; C -1 ; WX 600 ; N lozenge ; B 145 0 614 740 ; C -1 ; WX 600 ; N Rcaron ; B 24 0 659 790 ; C -1 ; WX 600 ; N Gcommaaccent ; B 74 -250 675 580 ; C -1 ; WX 600 ; N ucircumflex ; B 70 -15 597 657 ; C -1 ; WX 600 ; N acircumflex ; B 61 -15 607 657 ; C -1 ; WX 600 ; N Amacron ; B -9 0 633 708 ; C -1 ; WX 600 ; N rcaron ; B 47 0 655 667 ; C -1 ; WX 600 ; N ccedilla ; B 81 -206 631 459 ; C -1 ; WX 600 ; N Zdotaccent ; B 62 0 637 761 ; C -1 ; WX 600 ; N Thorn ; B 48 0 620 562 ; C -1 ; WX 600 ; N Omacron ; B 74 -18 663 708 ; C -1 ; WX 600 ; N Racute ; B 24 0 665 784 ; C -1 ; WX 600 ; N Sacute ; B 54 -22 673 784 ; C -1 ; WX 600 ; N dcaron ; B 60 -15 861 626 ; C -1 ; WX 600 ; N Umacron ; B 101 -18 716 708 ; C -1 ; WX 600 ; N uring ; B 70 -15 592 678 ; C -1 ; WX 600 ; N threesuperior ; B 193 222 526 616 ; C -1 ; WX 600 ; N Ograve ; B 74 -18 645 784 ; C -1 ; WX 600 ; N Agrave ; B -9 0 632 784 ; C -1 ; WX 600 ; N Abreve ; B -9 0 684 784 ; C -1 ; WX 600 ; N multiply ; B 104 39 606 478 ; C -1 ; WX 600 ; N uacute ; B 70 -15 599 661 ; C -1 ; WX 600 ; N Tcaron ; B 86 0 679 790 ; C -1 ; WX 600 ; N partialdiff ; B 91 -38 627 728 ; C -1 ; WX 600 ; N ydieresis ; B -21 -142 695 638 ; C -1 ; WX 600 ; N Nacute ; B 8 -12 730 784 ; C -1 ; WX 600 ; N icircumflex ; B 77 0 577 657 ; C -1 ; WX 600 ; N Ecircumflex ; B 25 0 670 780 ; C -1 ; WX 600 ; N adieresis ; B 61 -15 595 638 ; C -1 ; WX 600 ; N edieresis ; B 81 -15 605 638 ; C -1 ; WX 600 ; N cacute ; B 81 -15 649 661 ; C -1 ; WX 600 ; N nacute ; B 18 0 639 661 ; C -1 ; WX 600 ; N umacron ; B 70 -15 637 585 ; C -1 ; WX 600 ; N Ncaron ; B 8 -12 730 790 ; C -1 ; WX 600 ; N Iacute ; B 77 0 643 784 ; C -1 ; WX 600 ; N plusminus ; B 76 24 614 515 ; C -1 ; WX 600 ; N brokenbar ; B 217 -175 489 675 ; C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ; C -1 ; WX 600 ; N Gbreve ; B 74 -18 684 784 ; C -1 ; WX 600 ; N Idotaccent ; B 77 0 643 761 ; C -1 ; WX 600 ; N summation ; B 15 -10 672 706 ; C -1 ; WX 600 ; N Egrave ; B 25 0 670 784 ; C -1 ; WX 600 ; N racute ; B 47 0 655 661 ; C -1 ; WX 600 ; N omacron ; B 71 -15 637 585 ; C -1 ; WX 600 ; N Zacute ; B 62 0 665 784 ; C -1 ; WX 600 ; N Zcaron ; B 62 0 659 790 ; C -1 ; WX 600 ; N greaterequal ; B 26 0 627 696 ; C -1 ; WX 600 ; N Eth ; B 30 0 664 562 ; C -1 ; WX 600 ; N Ccedilla ; B 74 -206 675 580 ; C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 546 626 ; C -1 ; WX 600 ; N tcaron ; B 118 -15 627 703 ; C -1 ; WX 600 ; N eogonek ; B 81 -199 605 454 ; C -1 ; WX 600 ; N Uogonek ; B 101 -199 716 562 ; C -1 ; WX 600 ; N Aacute ; B -9 0 655 784 ; C -1 ; WX 600 ; N Adieresis ; B -9 0 632 761 ; C -1 ; WX 600 ; N egrave ; B 81 -15 605 661 ; C -1 ; WX 600 ; N zacute ; B 81 0 614 661 ; C -1 ; WX 600 ; N iogonek ; B 77 -199 546 658 ; C -1 ; WX 600 ; N Oacute ; B 74 -18 645 784 ; C -1 ; WX 600 ; N oacute ; B 71 -15 649 661 ; C -1 ; WX 600 ; N amacron ; B 61 -15 637 585 ; C -1 ; WX 600 ; N sacute ; B 66 -17 609 661 ; C -1 ; WX 600 ; N idieresis ; B 77 0 561 618 ; C -1 ; WX 600 ; N Ocircumflex ; B 74 -18 645 780 ; C -1 ; WX 600 ; N Ugrave ; B 101 -18 716 784 ; C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ; C -1 ; WX 600 ; N thorn ; B -32 -142 622 626 ; C -1 ; WX 600 ; N twosuperior ; B 191 230 542 616 ; C -1 ; WX 600 ; N Odieresis ; B 74 -18 645 761 ; C -1 ; WX 600 ; N mu ; B 49 -142 592 439 ; C -1 ; WX 600 ; N igrave ; B 77 0 546 661 ; C -1 ; WX 600 ; N ohungarumlaut ; B 71 -15 809 661 ; C -1 ; WX 600 ; N Eogonek ; B 25 -199 670 562 ; C -1 ; WX 600 ; N dcroat ; B 60 -15 712 626 ; C -1 ; WX 600 ; N threequarters ; B 8 -60 699 661 ; C -1 ; WX 600 ; N Scedilla ; B 54 -206 673 582 ; C -1 ; WX 600 ; N lcaron ; B 77 0 731 626 ; C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 692 562 ; C -1 ; WX 600 ; N Lacute ; B 39 0 636 784 ; C -1 ; WX 600 ; N trademark ; B 86 230 869 562 ; C -1 ; WX 600 ; N edotaccent ; B 81 -15 605 638 ; C -1 ; WX 600 ; N Igrave ; B 77 0 643 784 ; C -1 ; WX 600 ; N Imacron ; B 77 0 663 708 ; C -1 ; WX 600 ; N Lcaron ; B 39 0 757 562 ; C -1 ; WX 600 ; N onehalf ; B 22 -60 716 661 ; C -1 ; WX 600 ; N lessequal ; B 26 0 671 696 ; C -1 ; WX 600 ; N ocircumflex ; B 71 -15 622 657 ; C -1 ; WX 600 ; N ntilde ; B 18 0 643 636 ; C -1 ; WX 600 ; N Uhungarumlaut ; B 101 -18 805 784 ; C -1 ; WX 600 ; N Eacute ; B 25 0 670 784 ; C -1 ; WX 600 ; N emacron ; B 81 -15 637 585 ; C -1 ; WX 600 ; N gbreve ; B 40 -146 674 661 ; C -1 ; WX 600 ; N onequarter ; B 13 -60 707 661 ; C -1 ; WX 600 ; N Scaron ; B 54 -22 689 790 ; C -1 ; WX 600 ; N Scommaaccent ; B 54 -250 673 582 ; C -1 ; WX 600 ; N Ohungarumlaut ; B 74 -18 795 784 ; C -1 ; WX 600 ; N degree ; B 173 243 570 616 ; C -1 ; WX 600 ; N ograve ; B 71 -15 622 661 ; C -1 ; WX 600 ; N Ccaron ; B 74 -18 689 790 ; C -1 ; WX 600 ; N ugrave ; B 70 -15 592 661 ; C -1 ; WX 600 ; N radical ; B 67 -104 635 778 ; C -1 ; WX 600 ; N Dcaron ; B 30 0 664 790 ; C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 655 454 ; C -1 ; WX 600 ; N Ntilde ; B 8 -12 730 759 ; C -1 ; WX 600 ; N otilde ; B 71 -15 643 636 ; C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 617 562 ; C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 636 562 ; C -1 ; WX 600 ; N Atilde ; B -9 0 669 759 ; C -1 ; WX 600 ; N Aogonek ; B -9 -199 632 562 ; C -1 ; WX 600 ; N Aring ; B -9 0 632 801 ; C -1 ; WX 600 ; N Otilde ; B 74 -18 669 759 ; C -1 ; WX 600 ; N zdotaccent ; B 81 0 614 638 ; C -1 ; WX 600 ; N Ecaron ; B 25 0 670 790 ; C -1 ; WX 600 ; N Iogonek ; B 77 -199 643 562 ; C -1 ; WX 600 ; N kcommaaccent ; B 33 -250 643 626 ; C -1 ; WX 600 ; N minus ; B 114 203 596 313 ; C -1 ; WX 600 ; N Icircumflex ; B 77 0 643 780 ; C -1 ; WX 600 ; N ncaron ; B 18 0 633 667 ; C -1 ; WX 600 ; N tcommaaccent ; B 118 -250 567 562 ; C -1 ; WX 600 ; N logicalnot ; B 135 103 617 413 ; C -1 ; WX 600 ; N odieresis ; B 71 -15 622 638 ; C -1 ; WX 600 ; N udieresis ; B 70 -15 595 638 ; C -1 ; WX 600 ; N notequal ; B 30 -47 626 563 ; C -1 ; WX 600 ; N gcommaaccent ; B 40 -146 674 714 ; C -1 ; WX 600 ; N eth ; B 93 -27 661 626 ; C -1 ; WX 600 ; N zcaron ; B 81 0 643 667 ; C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 615 454 ; C -1 ; WX 600 ; N onesuperior ; B 212 230 514 616 ; C -1 ; WX 600 ; N imacron ; B 77 0 575 585 ; C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ; EndCharMetrics EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Courier-Oblique.afm000066400000000000000000000366471320103431700302510ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 17:37:52 1997 Comment UniqueID 43051 Comment VMusage 16248 75829 FontName Courier-Oblique FullName Courier Oblique FamilyName Courier Weight Medium ItalicAngle -12 IsFixedPitch true CharacterSet ExtendedRoman FontBBox -27 -250 849 805 UnderlinePosition -100 UnderlineThickness 50 Version 003.000 Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. EncodingScheme AdobeStandardEncoding CapHeight 562 XHeight 426 Ascender 629 Descender -157 StdHW 51 StdVW 51 StartCharMetrics 315 C 32 ; WX 600 ; N space ; B 0 0 0 0 ; C 33 ; WX 600 ; N exclam ; B 243 -15 464 572 ; C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ; C 35 ; WX 600 ; N numbersign ; B 133 -32 596 639 ; C 36 ; WX 600 ; N dollar ; B 108 -126 596 662 ; C 37 ; WX 600 ; N percent ; B 134 -15 599 622 ; C 38 ; WX 600 ; N ampersand ; B 87 -15 580 543 ; C 39 ; WX 600 ; N quoteright ; B 283 328 495 562 ; C 40 ; WX 600 ; N parenleft ; B 313 -108 572 622 ; C 41 ; WX 600 ; N parenright ; B 137 -108 396 622 ; C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ; C 43 ; WX 600 ; N plus ; B 129 44 580 470 ; C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ; C 45 ; WX 600 ; N hyphen ; B 152 231 558 285 ; C 46 ; WX 600 ; N period ; B 238 -15 382 109 ; C 47 ; WX 600 ; N slash ; B 112 -80 604 629 ; C 48 ; WX 600 ; N zero ; B 154 -15 575 622 ; C 49 ; WX 600 ; N one ; B 98 0 515 622 ; C 50 ; WX 600 ; N two ; B 70 0 568 622 ; C 51 ; WX 600 ; N three ; B 82 -15 538 622 ; C 52 ; WX 600 ; N four ; B 108 0 541 622 ; C 53 ; WX 600 ; N five ; B 99 -15 589 607 ; C 54 ; WX 600 ; N six ; B 155 -15 629 622 ; C 55 ; WX 600 ; N seven ; B 182 0 612 607 ; C 56 ; WX 600 ; N eight ; B 132 -15 588 622 ; C 57 ; WX 600 ; N nine ; B 93 -15 574 622 ; C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ; C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ; C 60 ; WX 600 ; N less ; B 96 42 610 472 ; C 61 ; WX 600 ; N equal ; B 109 138 600 376 ; C 62 ; WX 600 ; N greater ; B 85 42 599 472 ; C 63 ; WX 600 ; N question ; B 222 -15 583 572 ; C 64 ; WX 600 ; N at ; B 127 -15 582 622 ; C 65 ; WX 600 ; N A ; B 3 0 607 562 ; C 66 ; WX 600 ; N B ; B 43 0 616 562 ; C 67 ; WX 600 ; N C ; B 93 -18 655 580 ; C 68 ; WX 600 ; N D ; B 43 0 645 562 ; C 69 ; WX 600 ; N E ; B 53 0 660 562 ; C 70 ; WX 600 ; N F ; B 53 0 660 562 ; C 71 ; WX 600 ; N G ; B 83 -18 645 580 ; C 72 ; WX 600 ; N H ; B 32 0 687 562 ; C 73 ; WX 600 ; N I ; B 96 0 623 562 ; C 74 ; WX 600 ; N J ; B 52 -18 685 562 ; C 75 ; WX 600 ; N K ; B 38 0 671 562 ; C 76 ; WX 600 ; N L ; B 47 0 607 562 ; C 77 ; WX 600 ; N M ; B 4 0 715 562 ; C 78 ; WX 600 ; N N ; B 7 -13 712 562 ; C 79 ; WX 600 ; N O ; B 94 -18 625 580 ; C 80 ; WX 600 ; N P ; B 79 0 644 562 ; C 81 ; WX 600 ; N Q ; B 95 -138 625 580 ; C 82 ; WX 600 ; N R ; B 38 0 598 562 ; C 83 ; WX 600 ; N S ; B 76 -20 650 580 ; C 84 ; WX 600 ; N T ; B 108 0 665 562 ; C 85 ; WX 600 ; N U ; B 125 -18 702 562 ; C 86 ; WX 600 ; N V ; B 105 -13 723 562 ; C 87 ; WX 600 ; N W ; B 106 -13 722 562 ; C 88 ; WX 600 ; N X ; B 23 0 675 562 ; C 89 ; WX 600 ; N Y ; B 133 0 695 562 ; C 90 ; WX 600 ; N Z ; B 86 0 610 562 ; C 91 ; WX 600 ; N bracketleft ; B 246 -108 574 622 ; C 92 ; WX 600 ; N backslash ; B 249 -80 468 629 ; C 93 ; WX 600 ; N bracketright ; B 135 -108 463 622 ; C 94 ; WX 600 ; N asciicircum ; B 175 354 587 622 ; C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ; C 96 ; WX 600 ; N quoteleft ; B 343 328 457 562 ; C 97 ; WX 600 ; N a ; B 76 -15 569 441 ; C 98 ; WX 600 ; N b ; B 29 -15 625 629 ; C 99 ; WX 600 ; N c ; B 106 -15 608 441 ; C 100 ; WX 600 ; N d ; B 85 -15 640 629 ; C 101 ; WX 600 ; N e ; B 106 -15 598 441 ; C 102 ; WX 600 ; N f ; B 114 0 662 629 ; L i fi ; L l fl ; C 103 ; WX 600 ; N g ; B 61 -157 657 441 ; C 104 ; WX 600 ; N h ; B 33 0 592 629 ; C 105 ; WX 600 ; N i ; B 95 0 515 657 ; C 106 ; WX 600 ; N j ; B 52 -157 550 657 ; C 107 ; WX 600 ; N k ; B 58 0 633 629 ; C 108 ; WX 600 ; N l ; B 95 0 515 629 ; C 109 ; WX 600 ; N m ; B -5 0 615 441 ; C 110 ; WX 600 ; N n ; B 26 0 585 441 ; C 111 ; WX 600 ; N o ; B 102 -15 588 441 ; C 112 ; WX 600 ; N p ; B -24 -157 605 441 ; C 113 ; WX 600 ; N q ; B 85 -157 682 441 ; C 114 ; WX 600 ; N r ; B 60 0 636 441 ; C 115 ; WX 600 ; N s ; B 78 -15 584 441 ; C 116 ; WX 600 ; N t ; B 167 -15 561 561 ; C 117 ; WX 600 ; N u ; B 101 -15 572 426 ; C 118 ; WX 600 ; N v ; B 90 -10 681 426 ; C 119 ; WX 600 ; N w ; B 76 -10 695 426 ; C 120 ; WX 600 ; N x ; B 20 0 655 426 ; C 121 ; WX 600 ; N y ; B -4 -157 683 426 ; C 122 ; WX 600 ; N z ; B 99 0 593 426 ; C 123 ; WX 600 ; N braceleft ; B 233 -108 569 622 ; C 124 ; WX 600 ; N bar ; B 222 -250 485 750 ; C 125 ; WX 600 ; N braceright ; B 140 -108 477 622 ; C 126 ; WX 600 ; N asciitilde ; B 116 197 600 320 ; C 161 ; WX 600 ; N exclamdown ; B 225 -157 445 430 ; C 162 ; WX 600 ; N cent ; B 151 -49 588 614 ; C 163 ; WX 600 ; N sterling ; B 124 -21 621 611 ; C 164 ; WX 600 ; N fraction ; B 84 -57 646 665 ; C 165 ; WX 600 ; N yen ; B 120 0 693 562 ; C 166 ; WX 600 ; N florin ; B -26 -143 671 622 ; C 167 ; WX 600 ; N section ; B 104 -78 590 580 ; C 168 ; WX 600 ; N currency ; B 94 58 628 506 ; C 169 ; WX 600 ; N quotesingle ; B 345 328 460 562 ; C 170 ; WX 600 ; N quotedblleft ; B 262 328 541 562 ; C 171 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ; C 172 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ; C 173 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ; C 174 ; WX 600 ; N fi ; B 3 0 619 629 ; C 175 ; WX 600 ; N fl ; B 3 0 619 629 ; C 177 ; WX 600 ; N endash ; B 124 231 586 285 ; C 178 ; WX 600 ; N dagger ; B 217 -78 546 580 ; C 179 ; WX 600 ; N daggerdbl ; B 163 -78 546 580 ; C 180 ; WX 600 ; N periodcentered ; B 275 189 434 327 ; C 182 ; WX 600 ; N paragraph ; B 100 -78 630 562 ; C 183 ; WX 600 ; N bullet ; B 224 130 485 383 ; C 184 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ; C 185 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ; C 186 ; WX 600 ; N quotedblright ; B 213 328 576 562 ; C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ; C 188 ; WX 600 ; N ellipsis ; B 46 -15 575 111 ; C 189 ; WX 600 ; N perthousand ; B 59 -15 627 622 ; C 191 ; WX 600 ; N questiondown ; B 105 -157 466 430 ; C 193 ; WX 600 ; N grave ; B 294 497 484 672 ; C 194 ; WX 600 ; N acute ; B 348 497 612 672 ; C 195 ; WX 600 ; N circumflex ; B 229 477 581 654 ; C 196 ; WX 600 ; N tilde ; B 212 489 629 606 ; C 197 ; WX 600 ; N macron ; B 232 525 600 565 ; C 198 ; WX 600 ; N breve ; B 279 501 576 609 ; C 199 ; WX 600 ; N dotaccent ; B 373 537 478 640 ; C 200 ; WX 600 ; N dieresis ; B 272 537 579 640 ; C 202 ; WX 600 ; N ring ; B 332 463 500 627 ; C 203 ; WX 600 ; N cedilla ; B 197 -151 344 10 ; C 205 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ; C 206 ; WX 600 ; N ogonek ; B 189 -172 377 4 ; C 207 ; WX 600 ; N caron ; B 262 492 614 669 ; C 208 ; WX 600 ; N emdash ; B 49 231 661 285 ; C 225 ; WX 600 ; N AE ; B 3 0 655 562 ; C 227 ; WX 600 ; N ordfeminine ; B 209 249 512 580 ; C 232 ; WX 600 ; N Lslash ; B 47 0 607 562 ; C 233 ; WX 600 ; N Oslash ; B 94 -80 625 629 ; C 234 ; WX 600 ; N OE ; B 59 0 672 562 ; C 235 ; WX 600 ; N ordmasculine ; B 210 249 535 580 ; C 241 ; WX 600 ; N ae ; B 41 -15 626 441 ; C 245 ; WX 600 ; N dotlessi ; B 95 0 515 426 ; C 248 ; WX 600 ; N lslash ; B 95 0 587 629 ; C 249 ; WX 600 ; N oslash ; B 102 -80 588 506 ; C 250 ; WX 600 ; N oe ; B 54 -15 615 441 ; C 251 ; WX 600 ; N germandbls ; B 48 -15 617 629 ; C -1 ; WX 600 ; N Idieresis ; B 96 0 623 753 ; C -1 ; WX 600 ; N eacute ; B 106 -15 612 672 ; C -1 ; WX 600 ; N abreve ; B 76 -15 576 609 ; C -1 ; WX 600 ; N uhungarumlaut ; B 101 -15 723 672 ; C -1 ; WX 600 ; N ecaron ; B 106 -15 614 669 ; C -1 ; WX 600 ; N Ydieresis ; B 133 0 695 753 ; C -1 ; WX 600 ; N divide ; B 136 48 573 467 ; C -1 ; WX 600 ; N Yacute ; B 133 0 695 805 ; C -1 ; WX 600 ; N Acircumflex ; B 3 0 607 787 ; C -1 ; WX 600 ; N aacute ; B 76 -15 612 672 ; C -1 ; WX 600 ; N Ucircumflex ; B 125 -18 702 787 ; C -1 ; WX 600 ; N yacute ; B -4 -157 683 672 ; C -1 ; WX 600 ; N scommaaccent ; B 78 -250 584 441 ; C -1 ; WX 600 ; N ecircumflex ; B 106 -15 598 654 ; C -1 ; WX 600 ; N Uring ; B 125 -18 702 760 ; C -1 ; WX 600 ; N Udieresis ; B 125 -18 702 753 ; C -1 ; WX 600 ; N aogonek ; B 76 -172 569 441 ; C -1 ; WX 600 ; N Uacute ; B 125 -18 702 805 ; C -1 ; WX 600 ; N uogonek ; B 101 -172 572 426 ; C -1 ; WX 600 ; N Edieresis ; B 53 0 660 753 ; C -1 ; WX 600 ; N Dcroat ; B 43 0 645 562 ; C -1 ; WX 600 ; N commaaccent ; B 145 -250 323 -58 ; C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ; C -1 ; WX 600 ; N Emacron ; B 53 0 660 698 ; C -1 ; WX 600 ; N ccaron ; B 106 -15 614 669 ; C -1 ; WX 600 ; N aring ; B 76 -15 569 627 ; C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 712 562 ; C -1 ; WX 600 ; N lacute ; B 95 0 640 805 ; C -1 ; WX 600 ; N agrave ; B 76 -15 569 672 ; C -1 ; WX 600 ; N Tcommaaccent ; B 108 -250 665 562 ; C -1 ; WX 600 ; N Cacute ; B 93 -18 655 805 ; C -1 ; WX 600 ; N atilde ; B 76 -15 629 606 ; C -1 ; WX 600 ; N Edotaccent ; B 53 0 660 753 ; C -1 ; WX 600 ; N scaron ; B 78 -15 614 669 ; C -1 ; WX 600 ; N scedilla ; B 78 -151 584 441 ; C -1 ; WX 600 ; N iacute ; B 95 0 612 672 ; C -1 ; WX 600 ; N lozenge ; B 94 0 519 706 ; C -1 ; WX 600 ; N Rcaron ; B 38 0 642 802 ; C -1 ; WX 600 ; N Gcommaaccent ; B 83 -250 645 580 ; C -1 ; WX 600 ; N ucircumflex ; B 101 -15 572 654 ; C -1 ; WX 600 ; N acircumflex ; B 76 -15 581 654 ; C -1 ; WX 600 ; N Amacron ; B 3 0 607 698 ; C -1 ; WX 600 ; N rcaron ; B 60 0 636 669 ; C -1 ; WX 600 ; N ccedilla ; B 106 -151 614 441 ; C -1 ; WX 600 ; N Zdotaccent ; B 86 0 610 753 ; C -1 ; WX 600 ; N Thorn ; B 79 0 606 562 ; C -1 ; WX 600 ; N Omacron ; B 94 -18 628 698 ; C -1 ; WX 600 ; N Racute ; B 38 0 670 805 ; C -1 ; WX 600 ; N Sacute ; B 76 -20 650 805 ; C -1 ; WX 600 ; N dcaron ; B 85 -15 849 629 ; C -1 ; WX 600 ; N Umacron ; B 125 -18 702 698 ; C -1 ; WX 600 ; N uring ; B 101 -15 572 627 ; C -1 ; WX 600 ; N threesuperior ; B 213 240 501 622 ; C -1 ; WX 600 ; N Ograve ; B 94 -18 625 805 ; C -1 ; WX 600 ; N Agrave ; B 3 0 607 805 ; C -1 ; WX 600 ; N Abreve ; B 3 0 607 732 ; C -1 ; WX 600 ; N multiply ; B 103 43 607 470 ; C -1 ; WX 600 ; N uacute ; B 101 -15 602 672 ; C -1 ; WX 600 ; N Tcaron ; B 108 0 665 802 ; C -1 ; WX 600 ; N partialdiff ; B 45 -38 546 710 ; C -1 ; WX 600 ; N ydieresis ; B -4 -157 683 620 ; C -1 ; WX 600 ; N Nacute ; B 7 -13 712 805 ; C -1 ; WX 600 ; N icircumflex ; B 95 0 551 654 ; C -1 ; WX 600 ; N Ecircumflex ; B 53 0 660 787 ; C -1 ; WX 600 ; N adieresis ; B 76 -15 575 620 ; C -1 ; WX 600 ; N edieresis ; B 106 -15 598 620 ; C -1 ; WX 600 ; N cacute ; B 106 -15 612 672 ; C -1 ; WX 600 ; N nacute ; B 26 0 602 672 ; C -1 ; WX 600 ; N umacron ; B 101 -15 600 565 ; C -1 ; WX 600 ; N Ncaron ; B 7 -13 712 802 ; C -1 ; WX 600 ; N Iacute ; B 96 0 640 805 ; C -1 ; WX 600 ; N plusminus ; B 96 44 594 558 ; C -1 ; WX 600 ; N brokenbar ; B 238 -175 469 675 ; C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ; C -1 ; WX 600 ; N Gbreve ; B 83 -18 645 732 ; C -1 ; WX 600 ; N Idotaccent ; B 96 0 623 753 ; C -1 ; WX 600 ; N summation ; B 15 -10 670 706 ; C -1 ; WX 600 ; N Egrave ; B 53 0 660 805 ; C -1 ; WX 600 ; N racute ; B 60 0 636 672 ; C -1 ; WX 600 ; N omacron ; B 102 -15 600 565 ; C -1 ; WX 600 ; N Zacute ; B 86 0 670 805 ; C -1 ; WX 600 ; N Zcaron ; B 86 0 642 802 ; C -1 ; WX 600 ; N greaterequal ; B 98 0 594 710 ; C -1 ; WX 600 ; N Eth ; B 43 0 645 562 ; C -1 ; WX 600 ; N Ccedilla ; B 93 -151 658 580 ; C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 515 629 ; C -1 ; WX 600 ; N tcaron ; B 167 -15 587 717 ; C -1 ; WX 600 ; N eogonek ; B 106 -172 598 441 ; C -1 ; WX 600 ; N Uogonek ; B 124 -172 702 562 ; C -1 ; WX 600 ; N Aacute ; B 3 0 660 805 ; C -1 ; WX 600 ; N Adieresis ; B 3 0 607 753 ; C -1 ; WX 600 ; N egrave ; B 106 -15 598 672 ; C -1 ; WX 600 ; N zacute ; B 99 0 612 672 ; C -1 ; WX 600 ; N iogonek ; B 95 -172 515 657 ; C -1 ; WX 600 ; N Oacute ; B 94 -18 640 805 ; C -1 ; WX 600 ; N oacute ; B 102 -15 612 672 ; C -1 ; WX 600 ; N amacron ; B 76 -15 600 565 ; C -1 ; WX 600 ; N sacute ; B 78 -15 612 672 ; C -1 ; WX 600 ; N idieresis ; B 95 0 545 620 ; C -1 ; WX 600 ; N Ocircumflex ; B 94 -18 625 787 ; C -1 ; WX 600 ; N Ugrave ; B 125 -18 702 805 ; C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ; C -1 ; WX 600 ; N thorn ; B -24 -157 605 629 ; C -1 ; WX 600 ; N twosuperior ; B 230 249 535 622 ; C -1 ; WX 600 ; N Odieresis ; B 94 -18 625 753 ; C -1 ; WX 600 ; N mu ; B 72 -157 572 426 ; C -1 ; WX 600 ; N igrave ; B 95 0 515 672 ; C -1 ; WX 600 ; N ohungarumlaut ; B 102 -15 723 672 ; C -1 ; WX 600 ; N Eogonek ; B 53 -172 660 562 ; C -1 ; WX 600 ; N dcroat ; B 85 -15 704 629 ; C -1 ; WX 600 ; N threequarters ; B 73 -56 659 666 ; C -1 ; WX 600 ; N Scedilla ; B 76 -151 650 580 ; C -1 ; WX 600 ; N lcaron ; B 95 0 667 629 ; C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 671 562 ; C -1 ; WX 600 ; N Lacute ; B 47 0 607 805 ; C -1 ; WX 600 ; N trademark ; B 75 263 742 562 ; C -1 ; WX 600 ; N edotaccent ; B 106 -15 598 620 ; C -1 ; WX 600 ; N Igrave ; B 96 0 623 805 ; C -1 ; WX 600 ; N Imacron ; B 96 0 628 698 ; C -1 ; WX 600 ; N Lcaron ; B 47 0 632 562 ; C -1 ; WX 600 ; N onehalf ; B 65 -57 669 665 ; C -1 ; WX 600 ; N lessequal ; B 98 0 645 710 ; C -1 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ; C -1 ; WX 600 ; N ntilde ; B 26 0 629 606 ; C -1 ; WX 600 ; N Uhungarumlaut ; B 125 -18 761 805 ; C -1 ; WX 600 ; N Eacute ; B 53 0 670 805 ; C -1 ; WX 600 ; N emacron ; B 106 -15 600 565 ; C -1 ; WX 600 ; N gbreve ; B 61 -157 657 609 ; C -1 ; WX 600 ; N onequarter ; B 65 -57 674 665 ; C -1 ; WX 600 ; N Scaron ; B 76 -20 672 802 ; C -1 ; WX 600 ; N Scommaaccent ; B 76 -250 650 580 ; C -1 ; WX 600 ; N Ohungarumlaut ; B 94 -18 751 805 ; C -1 ; WX 600 ; N degree ; B 214 269 576 622 ; C -1 ; WX 600 ; N ograve ; B 102 -15 588 672 ; C -1 ; WX 600 ; N Ccaron ; B 93 -18 672 802 ; C -1 ; WX 600 ; N ugrave ; B 101 -15 572 672 ; C -1 ; WX 600 ; N radical ; B 85 -15 765 792 ; C -1 ; WX 600 ; N Dcaron ; B 43 0 645 802 ; C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 636 441 ; C -1 ; WX 600 ; N Ntilde ; B 7 -13 712 729 ; C -1 ; WX 600 ; N otilde ; B 102 -15 629 606 ; C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 598 562 ; C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 607 562 ; C -1 ; WX 600 ; N Atilde ; B 3 0 655 729 ; C -1 ; WX 600 ; N Aogonek ; B 3 -172 607 562 ; C -1 ; WX 600 ; N Aring ; B 3 0 607 750 ; C -1 ; WX 600 ; N Otilde ; B 94 -18 655 729 ; C -1 ; WX 600 ; N zdotaccent ; B 99 0 593 620 ; C -1 ; WX 600 ; N Ecaron ; B 53 0 660 802 ; C -1 ; WX 600 ; N Iogonek ; B 96 -172 623 562 ; C -1 ; WX 600 ; N kcommaaccent ; B 58 -250 633 629 ; C -1 ; WX 600 ; N minus ; B 129 232 580 283 ; C -1 ; WX 600 ; N Icircumflex ; B 96 0 623 787 ; C -1 ; WX 600 ; N ncaron ; B 26 0 614 669 ; C -1 ; WX 600 ; N tcommaaccent ; B 165 -250 561 561 ; C -1 ; WX 600 ; N logicalnot ; B 155 108 591 369 ; C -1 ; WX 600 ; N odieresis ; B 102 -15 588 620 ; C -1 ; WX 600 ; N udieresis ; B 101 -15 575 620 ; C -1 ; WX 600 ; N notequal ; B 43 -16 621 529 ; C -1 ; WX 600 ; N gcommaaccent ; B 61 -157 657 708 ; C -1 ; WX 600 ; N eth ; B 102 -15 639 629 ; C -1 ; WX 600 ; N zcaron ; B 99 0 624 669 ; C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 585 441 ; C -1 ; WX 600 ; N onesuperior ; B 231 249 491 622 ; C -1 ; WX 600 ; N imacron ; B 95 0 543 565 ; C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ; EndCharMetrics EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Courier.afm000066400000000000000000000364751320103431700266520ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 17:27:09 1997 Comment UniqueID 43050 Comment VMusage 39754 50779 FontName Courier FullName Courier FamilyName Courier Weight Medium ItalicAngle 0 IsFixedPitch true CharacterSet ExtendedRoman FontBBox -23 -250 715 805 UnderlinePosition -100 UnderlineThickness 50 Version 003.000 Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. EncodingScheme AdobeStandardEncoding CapHeight 562 XHeight 426 Ascender 629 Descender -157 StdHW 51 StdVW 51 StartCharMetrics 315 C 32 ; WX 600 ; N space ; B 0 0 0 0 ; C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ; C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ; C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ; C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ; C 37 ; WX 600 ; N percent ; B 81 -15 518 622 ; C 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ; C 39 ; WX 600 ; N quoteright ; B 213 328 376 562 ; C 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ; C 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ; C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ; C 43 ; WX 600 ; N plus ; B 80 44 520 470 ; C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ; C 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ; C 46 ; WX 600 ; N period ; B 229 -15 371 109 ; C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ; C 48 ; WX 600 ; N zero ; B 106 -15 494 622 ; C 49 ; WX 600 ; N one ; B 96 0 505 622 ; C 50 ; WX 600 ; N two ; B 70 0 471 622 ; C 51 ; WX 600 ; N three ; B 75 -15 466 622 ; C 52 ; WX 600 ; N four ; B 78 0 500 622 ; C 53 ; WX 600 ; N five ; B 92 -15 497 607 ; C 54 ; WX 600 ; N six ; B 111 -15 497 622 ; C 55 ; WX 600 ; N seven ; B 82 0 483 607 ; C 56 ; WX 600 ; N eight ; B 102 -15 498 622 ; C 57 ; WX 600 ; N nine ; B 96 -15 489 622 ; C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ; C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ; C 60 ; WX 600 ; N less ; B 41 42 519 472 ; C 61 ; WX 600 ; N equal ; B 80 138 520 376 ; C 62 ; WX 600 ; N greater ; B 66 42 544 472 ; C 63 ; WX 600 ; N question ; B 129 -15 492 572 ; C 64 ; WX 600 ; N at ; B 77 -15 533 622 ; C 65 ; WX 600 ; N A ; B 3 0 597 562 ; C 66 ; WX 600 ; N B ; B 43 0 559 562 ; C 67 ; WX 600 ; N C ; B 41 -18 540 580 ; C 68 ; WX 600 ; N D ; B 43 0 574 562 ; C 69 ; WX 600 ; N E ; B 53 0 550 562 ; C 70 ; WX 600 ; N F ; B 53 0 545 562 ; C 71 ; WX 600 ; N G ; B 31 -18 575 580 ; C 72 ; WX 600 ; N H ; B 32 0 568 562 ; C 73 ; WX 600 ; N I ; B 96 0 504 562 ; C 74 ; WX 600 ; N J ; B 34 -18 566 562 ; C 75 ; WX 600 ; N K ; B 38 0 582 562 ; C 76 ; WX 600 ; N L ; B 47 0 554 562 ; C 77 ; WX 600 ; N M ; B 4 0 596 562 ; C 78 ; WX 600 ; N N ; B 7 -13 593 562 ; C 79 ; WX 600 ; N O ; B 43 -18 557 580 ; C 80 ; WX 600 ; N P ; B 79 0 558 562 ; C 81 ; WX 600 ; N Q ; B 43 -138 557 580 ; C 82 ; WX 600 ; N R ; B 38 0 588 562 ; C 83 ; WX 600 ; N S ; B 72 -20 529 580 ; C 84 ; WX 600 ; N T ; B 38 0 563 562 ; C 85 ; WX 600 ; N U ; B 17 -18 583 562 ; C 86 ; WX 600 ; N V ; B -4 -13 604 562 ; C 87 ; WX 600 ; N W ; B -3 -13 603 562 ; C 88 ; WX 600 ; N X ; B 23 0 577 562 ; C 89 ; WX 600 ; N Y ; B 24 0 576 562 ; C 90 ; WX 600 ; N Z ; B 86 0 514 562 ; C 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ; C 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ; C 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ; C 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ; C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ; C 96 ; WX 600 ; N quoteleft ; B 224 328 387 562 ; C 97 ; WX 600 ; N a ; B 53 -15 559 441 ; C 98 ; WX 600 ; N b ; B 14 -15 575 629 ; C 99 ; WX 600 ; N c ; B 66 -15 529 441 ; C 100 ; WX 600 ; N d ; B 45 -15 591 629 ; C 101 ; WX 600 ; N e ; B 66 -15 548 441 ; C 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ; C 103 ; WX 600 ; N g ; B 45 -157 566 441 ; C 104 ; WX 600 ; N h ; B 18 0 582 629 ; C 105 ; WX 600 ; N i ; B 95 0 505 657 ; C 106 ; WX 600 ; N j ; B 82 -157 410 657 ; C 107 ; WX 600 ; N k ; B 43 0 580 629 ; C 108 ; WX 600 ; N l ; B 95 0 505 629 ; C 109 ; WX 600 ; N m ; B -5 0 605 441 ; C 110 ; WX 600 ; N n ; B 26 0 575 441 ; C 111 ; WX 600 ; N o ; B 62 -15 538 441 ; C 112 ; WX 600 ; N p ; B 9 -157 555 441 ; C 113 ; WX 600 ; N q ; B 45 -157 591 441 ; C 114 ; WX 600 ; N r ; B 60 0 559 441 ; C 115 ; WX 600 ; N s ; B 80 -15 513 441 ; C 116 ; WX 600 ; N t ; B 87 -15 530 561 ; C 117 ; WX 600 ; N u ; B 21 -15 562 426 ; C 118 ; WX 600 ; N v ; B 10 -10 590 426 ; C 119 ; WX 600 ; N w ; B -4 -10 604 426 ; C 120 ; WX 600 ; N x ; B 20 0 580 426 ; C 121 ; WX 600 ; N y ; B 7 -157 592 426 ; C 122 ; WX 600 ; N z ; B 99 0 502 426 ; C 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ; C 124 ; WX 600 ; N bar ; B 275 -250 326 750 ; C 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ; C 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ; C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ; C 162 ; WX 600 ; N cent ; B 96 -49 500 614 ; C 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ; C 164 ; WX 600 ; N fraction ; B 92 -57 509 665 ; C 165 ; WX 600 ; N yen ; B 26 0 574 562 ; C 166 ; WX 600 ; N florin ; B 4 -143 539 622 ; C 167 ; WX 600 ; N section ; B 113 -78 488 580 ; C 168 ; WX 600 ; N currency ; B 73 58 527 506 ; C 169 ; WX 600 ; N quotesingle ; B 259 328 341 562 ; C 170 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ; C 171 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ; C 172 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ; C 173 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ; C 174 ; WX 600 ; N fi ; B 3 0 597 629 ; C 175 ; WX 600 ; N fl ; B 3 0 597 629 ; C 177 ; WX 600 ; N endash ; B 75 231 525 285 ; C 178 ; WX 600 ; N dagger ; B 141 -78 459 580 ; C 179 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ; C 180 ; WX 600 ; N periodcentered ; B 222 189 378 327 ; C 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ; C 183 ; WX 600 ; N bullet ; B 172 130 428 383 ; C 184 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ; C 185 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ; C 186 ; WX 600 ; N quotedblright ; B 143 328 457 562 ; C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ; C 188 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ; C 189 ; WX 600 ; N perthousand ; B 3 -15 600 622 ; C 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ; C 193 ; WX 600 ; N grave ; B 151 497 378 672 ; C 194 ; WX 600 ; N acute ; B 242 497 469 672 ; C 195 ; WX 600 ; N circumflex ; B 124 477 476 654 ; C 196 ; WX 600 ; N tilde ; B 105 489 503 606 ; C 197 ; WX 600 ; N macron ; B 120 525 480 565 ; C 198 ; WX 600 ; N breve ; B 153 501 447 609 ; C 199 ; WX 600 ; N dotaccent ; B 249 537 352 640 ; C 200 ; WX 600 ; N dieresis ; B 148 537 453 640 ; C 202 ; WX 600 ; N ring ; B 218 463 382 627 ; C 203 ; WX 600 ; N cedilla ; B 224 -151 362 10 ; C 205 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ; C 206 ; WX 600 ; N ogonek ; B 211 -172 407 4 ; C 207 ; WX 600 ; N caron ; B 124 492 476 669 ; C 208 ; WX 600 ; N emdash ; B 0 231 600 285 ; C 225 ; WX 600 ; N AE ; B 3 0 550 562 ; C 227 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ; C 232 ; WX 600 ; N Lslash ; B 47 0 554 562 ; C 233 ; WX 600 ; N Oslash ; B 43 -80 557 629 ; C 234 ; WX 600 ; N OE ; B 7 0 567 562 ; C 235 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ; C 241 ; WX 600 ; N ae ; B 19 -15 570 441 ; C 245 ; WX 600 ; N dotlessi ; B 95 0 505 426 ; C 248 ; WX 600 ; N lslash ; B 95 0 505 629 ; C 249 ; WX 600 ; N oslash ; B 62 -80 538 506 ; C 250 ; WX 600 ; N oe ; B 19 -15 559 441 ; C 251 ; WX 600 ; N germandbls ; B 48 -15 588 629 ; C -1 ; WX 600 ; N Idieresis ; B 96 0 504 753 ; C -1 ; WX 600 ; N eacute ; B 66 -15 548 672 ; C -1 ; WX 600 ; N abreve ; B 53 -15 559 609 ; C -1 ; WX 600 ; N uhungarumlaut ; B 21 -15 580 672 ; C -1 ; WX 600 ; N ecaron ; B 66 -15 548 669 ; C -1 ; WX 600 ; N Ydieresis ; B 24 0 576 753 ; C -1 ; WX 600 ; N divide ; B 87 48 513 467 ; C -1 ; WX 600 ; N Yacute ; B 24 0 576 805 ; C -1 ; WX 600 ; N Acircumflex ; B 3 0 597 787 ; C -1 ; WX 600 ; N aacute ; B 53 -15 559 672 ; C -1 ; WX 600 ; N Ucircumflex ; B 17 -18 583 787 ; C -1 ; WX 600 ; N yacute ; B 7 -157 592 672 ; C -1 ; WX 600 ; N scommaaccent ; B 80 -250 513 441 ; C -1 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ; C -1 ; WX 600 ; N Uring ; B 17 -18 583 760 ; C -1 ; WX 600 ; N Udieresis ; B 17 -18 583 753 ; C -1 ; WX 600 ; N aogonek ; B 53 -172 587 441 ; C -1 ; WX 600 ; N Uacute ; B 17 -18 583 805 ; C -1 ; WX 600 ; N uogonek ; B 21 -172 590 426 ; C -1 ; WX 600 ; N Edieresis ; B 53 0 550 753 ; C -1 ; WX 600 ; N Dcroat ; B 30 0 574 562 ; C -1 ; WX 600 ; N commaaccent ; B 198 -250 335 -58 ; C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ; C -1 ; WX 600 ; N Emacron ; B 53 0 550 698 ; C -1 ; WX 600 ; N ccaron ; B 66 -15 529 669 ; C -1 ; WX 600 ; N aring ; B 53 -15 559 627 ; C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 593 562 ; C -1 ; WX 600 ; N lacute ; B 95 0 505 805 ; C -1 ; WX 600 ; N agrave ; B 53 -15 559 672 ; C -1 ; WX 600 ; N Tcommaaccent ; B 38 -250 563 562 ; C -1 ; WX 600 ; N Cacute ; B 41 -18 540 805 ; C -1 ; WX 600 ; N atilde ; B 53 -15 559 606 ; C -1 ; WX 600 ; N Edotaccent ; B 53 0 550 753 ; C -1 ; WX 600 ; N scaron ; B 80 -15 513 669 ; C -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ; C -1 ; WX 600 ; N iacute ; B 95 0 505 672 ; C -1 ; WX 600 ; N lozenge ; B 18 0 443 706 ; C -1 ; WX 600 ; N Rcaron ; B 38 0 588 802 ; C -1 ; WX 600 ; N Gcommaaccent ; B 31 -250 575 580 ; C -1 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ; C -1 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ; C -1 ; WX 600 ; N Amacron ; B 3 0 597 698 ; C -1 ; WX 600 ; N rcaron ; B 60 0 559 669 ; C -1 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ; C -1 ; WX 600 ; N Zdotaccent ; B 86 0 514 753 ; C -1 ; WX 600 ; N Thorn ; B 79 0 538 562 ; C -1 ; WX 600 ; N Omacron ; B 43 -18 557 698 ; C -1 ; WX 600 ; N Racute ; B 38 0 588 805 ; C -1 ; WX 600 ; N Sacute ; B 72 -20 529 805 ; C -1 ; WX 600 ; N dcaron ; B 45 -15 715 629 ; C -1 ; WX 600 ; N Umacron ; B 17 -18 583 698 ; C -1 ; WX 600 ; N uring ; B 21 -15 562 627 ; C -1 ; WX 600 ; N threesuperior ; B 155 240 406 622 ; C -1 ; WX 600 ; N Ograve ; B 43 -18 557 805 ; C -1 ; WX 600 ; N Agrave ; B 3 0 597 805 ; C -1 ; WX 600 ; N Abreve ; B 3 0 597 732 ; C -1 ; WX 600 ; N multiply ; B 87 43 515 470 ; C -1 ; WX 600 ; N uacute ; B 21 -15 562 672 ; C -1 ; WX 600 ; N Tcaron ; B 38 0 563 802 ; C -1 ; WX 600 ; N partialdiff ; B 17 -38 459 710 ; C -1 ; WX 600 ; N ydieresis ; B 7 -157 592 620 ; C -1 ; WX 600 ; N Nacute ; B 7 -13 593 805 ; C -1 ; WX 600 ; N icircumflex ; B 94 0 505 654 ; C -1 ; WX 600 ; N Ecircumflex ; B 53 0 550 787 ; C -1 ; WX 600 ; N adieresis ; B 53 -15 559 620 ; C -1 ; WX 600 ; N edieresis ; B 66 -15 548 620 ; C -1 ; WX 600 ; N cacute ; B 66 -15 529 672 ; C -1 ; WX 600 ; N nacute ; B 26 0 575 672 ; C -1 ; WX 600 ; N umacron ; B 21 -15 562 565 ; C -1 ; WX 600 ; N Ncaron ; B 7 -13 593 802 ; C -1 ; WX 600 ; N Iacute ; B 96 0 504 805 ; C -1 ; WX 600 ; N plusminus ; B 87 44 513 558 ; C -1 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ; C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ; C -1 ; WX 600 ; N Gbreve ; B 31 -18 575 732 ; C -1 ; WX 600 ; N Idotaccent ; B 96 0 504 753 ; C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ; C -1 ; WX 600 ; N Egrave ; B 53 0 550 805 ; C -1 ; WX 600 ; N racute ; B 60 0 559 672 ; C -1 ; WX 600 ; N omacron ; B 62 -15 538 565 ; C -1 ; WX 600 ; N Zacute ; B 86 0 514 805 ; C -1 ; WX 600 ; N Zcaron ; B 86 0 514 802 ; C -1 ; WX 600 ; N greaterequal ; B 98 0 502 710 ; C -1 ; WX 600 ; N Eth ; B 30 0 574 562 ; C -1 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ; C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 505 629 ; C -1 ; WX 600 ; N tcaron ; B 87 -15 530 717 ; C -1 ; WX 600 ; N eogonek ; B 66 -172 548 441 ; C -1 ; WX 600 ; N Uogonek ; B 17 -172 583 562 ; C -1 ; WX 600 ; N Aacute ; B 3 0 597 805 ; C -1 ; WX 600 ; N Adieresis ; B 3 0 597 753 ; C -1 ; WX 600 ; N egrave ; B 66 -15 548 672 ; C -1 ; WX 600 ; N zacute ; B 99 0 502 672 ; C -1 ; WX 600 ; N iogonek ; B 95 -172 505 657 ; C -1 ; WX 600 ; N Oacute ; B 43 -18 557 805 ; C -1 ; WX 600 ; N oacute ; B 62 -15 538 672 ; C -1 ; WX 600 ; N amacron ; B 53 -15 559 565 ; C -1 ; WX 600 ; N sacute ; B 80 -15 513 672 ; C -1 ; WX 600 ; N idieresis ; B 95 0 505 620 ; C -1 ; WX 600 ; N Ocircumflex ; B 43 -18 557 787 ; C -1 ; WX 600 ; N Ugrave ; B 17 -18 583 805 ; C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ; C -1 ; WX 600 ; N thorn ; B -6 -157 555 629 ; C -1 ; WX 600 ; N twosuperior ; B 177 249 424 622 ; C -1 ; WX 600 ; N Odieresis ; B 43 -18 557 753 ; C -1 ; WX 600 ; N mu ; B 21 -157 562 426 ; C -1 ; WX 600 ; N igrave ; B 95 0 505 672 ; C -1 ; WX 600 ; N ohungarumlaut ; B 62 -15 580 672 ; C -1 ; WX 600 ; N Eogonek ; B 53 -172 561 562 ; C -1 ; WX 600 ; N dcroat ; B 45 -15 591 629 ; C -1 ; WX 600 ; N threequarters ; B 8 -56 593 666 ; C -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ; C -1 ; WX 600 ; N lcaron ; B 95 0 533 629 ; C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 582 562 ; C -1 ; WX 600 ; N Lacute ; B 47 0 554 805 ; C -1 ; WX 600 ; N trademark ; B -23 263 623 562 ; C -1 ; WX 600 ; N edotaccent ; B 66 -15 548 620 ; C -1 ; WX 600 ; N Igrave ; B 96 0 504 805 ; C -1 ; WX 600 ; N Imacron ; B 96 0 504 698 ; C -1 ; WX 600 ; N Lcaron ; B 47 0 554 562 ; C -1 ; WX 600 ; N onehalf ; B 0 -57 611 665 ; C -1 ; WX 600 ; N lessequal ; B 98 0 502 710 ; C -1 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ; C -1 ; WX 600 ; N ntilde ; B 26 0 575 606 ; C -1 ; WX 600 ; N Uhungarumlaut ; B 17 -18 590 805 ; C -1 ; WX 600 ; N Eacute ; B 53 0 550 805 ; C -1 ; WX 600 ; N emacron ; B 66 -15 548 565 ; C -1 ; WX 600 ; N gbreve ; B 45 -157 566 609 ; C -1 ; WX 600 ; N onequarter ; B 0 -57 600 665 ; C -1 ; WX 600 ; N Scaron ; B 72 -20 529 802 ; C -1 ; WX 600 ; N Scommaaccent ; B 72 -250 529 580 ; C -1 ; WX 600 ; N Ohungarumlaut ; B 43 -18 580 805 ; C -1 ; WX 600 ; N degree ; B 123 269 477 622 ; C -1 ; WX 600 ; N ograve ; B 62 -15 538 672 ; C -1 ; WX 600 ; N Ccaron ; B 41 -18 540 802 ; C -1 ; WX 600 ; N ugrave ; B 21 -15 562 672 ; C -1 ; WX 600 ; N radical ; B 3 -15 597 792 ; C -1 ; WX 600 ; N Dcaron ; B 43 0 574 802 ; C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 559 441 ; C -1 ; WX 600 ; N Ntilde ; B 7 -13 593 729 ; C -1 ; WX 600 ; N otilde ; B 62 -15 538 606 ; C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 588 562 ; C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 554 562 ; C -1 ; WX 600 ; N Atilde ; B 3 0 597 729 ; C -1 ; WX 600 ; N Aogonek ; B 3 -172 608 562 ; C -1 ; WX 600 ; N Aring ; B 3 0 597 750 ; C -1 ; WX 600 ; N Otilde ; B 43 -18 557 729 ; C -1 ; WX 600 ; N zdotaccent ; B 99 0 502 620 ; C -1 ; WX 600 ; N Ecaron ; B 53 0 550 802 ; C -1 ; WX 600 ; N Iogonek ; B 96 -172 504 562 ; C -1 ; WX 600 ; N kcommaaccent ; B 43 -250 580 629 ; C -1 ; WX 600 ; N minus ; B 80 232 520 283 ; C -1 ; WX 600 ; N Icircumflex ; B 96 0 504 787 ; C -1 ; WX 600 ; N ncaron ; B 26 0 575 669 ; C -1 ; WX 600 ; N tcommaaccent ; B 87 -250 530 561 ; C -1 ; WX 600 ; N logicalnot ; B 87 108 513 369 ; C -1 ; WX 600 ; N odieresis ; B 62 -15 538 620 ; C -1 ; WX 600 ; N udieresis ; B 21 -15 562 620 ; C -1 ; WX 600 ; N notequal ; B 15 -16 540 529 ; C -1 ; WX 600 ; N gcommaaccent ; B 45 -157 566 708 ; C -1 ; WX 600 ; N eth ; B 62 -15 538 629 ; C -1 ; WX 600 ; N zcaron ; B 99 0 502 669 ; C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 575 441 ; C -1 ; WX 600 ; N onesuperior ; B 172 249 428 622 ; C -1 ; WX 600 ; N imacron ; B 95 0 505 565 ; C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ; EndCharMetrics EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Helvetica-Bold.afm000066400000000000000000002146401320103431700300140ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 12:43:52 1997 Comment UniqueID 43052 Comment VMusage 37169 48194 FontName Helvetica-Bold FullName Helvetica Bold FamilyName Helvetica Weight Bold ItalicAngle 0 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -170 -228 1003 962 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme AdobeStandardEncoding CapHeight 718 XHeight 532 Ascender 718 Descender -207 StdHW 118 StdVW 140 StartCharMetrics 315 C 32 ; WX 278 ; N space ; B 0 0 0 0 ; C 33 ; WX 333 ; N exclam ; B 90 0 244 718 ; C 34 ; WX 474 ; N quotedbl ; B 98 447 376 718 ; C 35 ; WX 556 ; N numbersign ; B 18 0 538 698 ; C 36 ; WX 556 ; N dollar ; B 30 -115 523 775 ; C 37 ; WX 889 ; N percent ; B 28 -19 861 710 ; C 38 ; WX 722 ; N ampersand ; B 54 -19 701 718 ; C 39 ; WX 278 ; N quoteright ; B 69 445 209 718 ; C 40 ; WX 333 ; N parenleft ; B 35 -208 314 734 ; C 41 ; WX 333 ; N parenright ; B 19 -208 298 734 ; C 42 ; WX 389 ; N asterisk ; B 27 387 362 718 ; C 43 ; WX 584 ; N plus ; B 40 0 544 506 ; C 44 ; WX 278 ; N comma ; B 64 -168 214 146 ; C 45 ; WX 333 ; N hyphen ; B 27 215 306 345 ; C 46 ; WX 278 ; N period ; B 64 0 214 146 ; C 47 ; WX 278 ; N slash ; B -33 -19 311 737 ; C 48 ; WX 556 ; N zero ; B 32 -19 524 710 ; C 49 ; WX 556 ; N one ; B 69 0 378 710 ; C 50 ; WX 556 ; N two ; B 26 0 511 710 ; C 51 ; WX 556 ; N three ; B 27 -19 516 710 ; C 52 ; WX 556 ; N four ; B 27 0 526 710 ; C 53 ; WX 556 ; N five ; B 27 -19 516 698 ; C 54 ; WX 556 ; N six ; B 31 -19 520 710 ; C 55 ; WX 556 ; N seven ; B 25 0 528 698 ; C 56 ; WX 556 ; N eight ; B 32 -19 524 710 ; C 57 ; WX 556 ; N nine ; B 30 -19 522 710 ; C 58 ; WX 333 ; N colon ; B 92 0 242 512 ; C 59 ; WX 333 ; N semicolon ; B 92 -168 242 512 ; C 60 ; WX 584 ; N less ; B 38 -8 546 514 ; C 61 ; WX 584 ; N equal ; B 40 87 544 419 ; C 62 ; WX 584 ; N greater ; B 38 -8 546 514 ; C 63 ; WX 611 ; N question ; B 60 0 556 727 ; C 64 ; WX 975 ; N at ; B 118 -19 856 737 ; C 65 ; WX 722 ; N A ; B 20 0 702 718 ; C 66 ; WX 722 ; N B ; B 76 0 669 718 ; C 67 ; WX 722 ; N C ; B 44 -19 684 737 ; C 68 ; WX 722 ; N D ; B 76 0 685 718 ; C 69 ; WX 667 ; N E ; B 76 0 621 718 ; C 70 ; WX 611 ; N F ; B 76 0 587 718 ; C 71 ; WX 778 ; N G ; B 44 -19 713 737 ; C 72 ; WX 722 ; N H ; B 71 0 651 718 ; C 73 ; WX 278 ; N I ; B 64 0 214 718 ; C 74 ; WX 556 ; N J ; B 22 -18 484 718 ; C 75 ; WX 722 ; N K ; B 87 0 722 718 ; C 76 ; WX 611 ; N L ; B 76 0 583 718 ; C 77 ; WX 833 ; N M ; B 69 0 765 718 ; C 78 ; WX 722 ; N N ; B 69 0 654 718 ; C 79 ; WX 778 ; N O ; B 44 -19 734 737 ; C 80 ; WX 667 ; N P ; B 76 0 627 718 ; C 81 ; WX 778 ; N Q ; B 44 -52 737 737 ; C 82 ; WX 722 ; N R ; B 76 0 677 718 ; C 83 ; WX 667 ; N S ; B 39 -19 629 737 ; C 84 ; WX 611 ; N T ; B 14 0 598 718 ; C 85 ; WX 722 ; N U ; B 72 -19 651 718 ; C 86 ; WX 667 ; N V ; B 19 0 648 718 ; C 87 ; WX 944 ; N W ; B 16 0 929 718 ; C 88 ; WX 667 ; N X ; B 14 0 653 718 ; C 89 ; WX 667 ; N Y ; B 15 0 653 718 ; C 90 ; WX 611 ; N Z ; B 25 0 586 718 ; C 91 ; WX 333 ; N bracketleft ; B 63 -196 309 722 ; C 92 ; WX 278 ; N backslash ; B -33 -19 311 737 ; C 93 ; WX 333 ; N bracketright ; B 24 -196 270 722 ; C 94 ; WX 584 ; N asciicircum ; B 62 323 522 698 ; C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ; C 96 ; WX 278 ; N quoteleft ; B 69 454 209 727 ; C 97 ; WX 556 ; N a ; B 29 -14 527 546 ; C 98 ; WX 611 ; N b ; B 61 -14 578 718 ; C 99 ; WX 556 ; N c ; B 34 -14 524 546 ; C 100 ; WX 611 ; N d ; B 34 -14 551 718 ; C 101 ; WX 556 ; N e ; B 23 -14 528 546 ; C 102 ; WX 333 ; N f ; B 10 0 318 727 ; L i fi ; L l fl ; C 103 ; WX 611 ; N g ; B 40 -217 553 546 ; C 104 ; WX 611 ; N h ; B 65 0 546 718 ; C 105 ; WX 278 ; N i ; B 69 0 209 725 ; C 106 ; WX 278 ; N j ; B 3 -214 209 725 ; C 107 ; WX 556 ; N k ; B 69 0 562 718 ; C 108 ; WX 278 ; N l ; B 69 0 209 718 ; C 109 ; WX 889 ; N m ; B 64 0 826 546 ; C 110 ; WX 611 ; N n ; B 65 0 546 546 ; C 111 ; WX 611 ; N o ; B 34 -14 578 546 ; C 112 ; WX 611 ; N p ; B 62 -207 578 546 ; C 113 ; WX 611 ; N q ; B 34 -207 552 546 ; C 114 ; WX 389 ; N r ; B 64 0 373 546 ; C 115 ; WX 556 ; N s ; B 30 -14 519 546 ; C 116 ; WX 333 ; N t ; B 10 -6 309 676 ; C 117 ; WX 611 ; N u ; B 66 -14 545 532 ; C 118 ; WX 556 ; N v ; B 13 0 543 532 ; C 119 ; WX 778 ; N w ; B 10 0 769 532 ; C 120 ; WX 556 ; N x ; B 15 0 541 532 ; C 121 ; WX 556 ; N y ; B 10 -214 539 532 ; C 122 ; WX 500 ; N z ; B 20 0 480 532 ; C 123 ; WX 389 ; N braceleft ; B 48 -196 365 722 ; C 124 ; WX 280 ; N bar ; B 84 -225 196 775 ; C 125 ; WX 389 ; N braceright ; B 24 -196 341 722 ; C 126 ; WX 584 ; N asciitilde ; B 61 163 523 343 ; C 161 ; WX 333 ; N exclamdown ; B 90 -186 244 532 ; C 162 ; WX 556 ; N cent ; B 34 -118 524 628 ; C 163 ; WX 556 ; N sterling ; B 28 -16 541 718 ; C 164 ; WX 167 ; N fraction ; B -170 -19 336 710 ; C 165 ; WX 556 ; N yen ; B -9 0 565 698 ; C 166 ; WX 556 ; N florin ; B -10 -210 516 737 ; C 167 ; WX 556 ; N section ; B 34 -184 522 727 ; C 168 ; WX 556 ; N currency ; B -3 76 559 636 ; C 169 ; WX 238 ; N quotesingle ; B 70 447 168 718 ; C 170 ; WX 500 ; N quotedblleft ; B 64 454 436 727 ; C 171 ; WX 556 ; N guillemotleft ; B 88 76 468 484 ; C 172 ; WX 333 ; N guilsinglleft ; B 83 76 250 484 ; C 173 ; WX 333 ; N guilsinglright ; B 83 76 250 484 ; C 174 ; WX 611 ; N fi ; B 10 0 542 727 ; C 175 ; WX 611 ; N fl ; B 10 0 542 727 ; C 177 ; WX 556 ; N endash ; B 0 227 556 333 ; C 178 ; WX 556 ; N dagger ; B 36 -171 520 718 ; C 179 ; WX 556 ; N daggerdbl ; B 36 -171 520 718 ; C 180 ; WX 278 ; N periodcentered ; B 58 172 220 334 ; C 182 ; WX 556 ; N paragraph ; B -8 -191 539 700 ; C 183 ; WX 350 ; N bullet ; B 10 194 340 524 ; C 184 ; WX 278 ; N quotesinglbase ; B 69 -146 209 127 ; C 185 ; WX 500 ; N quotedblbase ; B 64 -146 436 127 ; C 186 ; WX 500 ; N quotedblright ; B 64 445 436 718 ; C 187 ; WX 556 ; N guillemotright ; B 88 76 468 484 ; C 188 ; WX 1000 ; N ellipsis ; B 92 0 908 146 ; C 189 ; WX 1000 ; N perthousand ; B -3 -19 1003 710 ; C 191 ; WX 611 ; N questiondown ; B 55 -195 551 532 ; C 193 ; WX 333 ; N grave ; B -23 604 225 750 ; C 194 ; WX 333 ; N acute ; B 108 604 356 750 ; C 195 ; WX 333 ; N circumflex ; B -10 604 343 750 ; C 196 ; WX 333 ; N tilde ; B -17 610 350 737 ; C 197 ; WX 333 ; N macron ; B -6 604 339 678 ; C 198 ; WX 333 ; N breve ; B -2 604 335 750 ; C 199 ; WX 333 ; N dotaccent ; B 104 614 230 729 ; C 200 ; WX 333 ; N dieresis ; B 6 614 327 729 ; C 202 ; WX 333 ; N ring ; B 59 568 275 776 ; C 203 ; WX 333 ; N cedilla ; B 6 -228 245 0 ; C 205 ; WX 333 ; N hungarumlaut ; B 9 604 486 750 ; C 206 ; WX 333 ; N ogonek ; B 71 -228 304 0 ; C 207 ; WX 333 ; N caron ; B -10 604 343 750 ; C 208 ; WX 1000 ; N emdash ; B 0 227 1000 333 ; C 225 ; WX 1000 ; N AE ; B 5 0 954 718 ; C 227 ; WX 370 ; N ordfeminine ; B 22 401 347 737 ; C 232 ; WX 611 ; N Lslash ; B -20 0 583 718 ; C 233 ; WX 778 ; N Oslash ; B 33 -27 744 745 ; C 234 ; WX 1000 ; N OE ; B 37 -19 961 737 ; C 235 ; WX 365 ; N ordmasculine ; B 6 401 360 737 ; C 241 ; WX 889 ; N ae ; B 29 -14 858 546 ; C 245 ; WX 278 ; N dotlessi ; B 69 0 209 532 ; C 248 ; WX 278 ; N lslash ; B -18 0 296 718 ; C 249 ; WX 611 ; N oslash ; B 22 -29 589 560 ; C 250 ; WX 944 ; N oe ; B 34 -14 912 546 ; C 251 ; WX 611 ; N germandbls ; B 69 -14 579 731 ; C -1 ; WX 278 ; N Idieresis ; B -21 0 300 915 ; C -1 ; WX 556 ; N eacute ; B 23 -14 528 750 ; C -1 ; WX 556 ; N abreve ; B 29 -14 527 750 ; C -1 ; WX 611 ; N uhungarumlaut ; B 66 -14 625 750 ; C -1 ; WX 556 ; N ecaron ; B 23 -14 528 750 ; C -1 ; WX 667 ; N Ydieresis ; B 15 0 653 915 ; C -1 ; WX 584 ; N divide ; B 40 -42 544 548 ; C -1 ; WX 667 ; N Yacute ; B 15 0 653 936 ; C -1 ; WX 722 ; N Acircumflex ; B 20 0 702 936 ; C -1 ; WX 556 ; N aacute ; B 29 -14 527 750 ; C -1 ; WX 722 ; N Ucircumflex ; B 72 -19 651 936 ; C -1 ; WX 556 ; N yacute ; B 10 -214 539 750 ; C -1 ; WX 556 ; N scommaaccent ; B 30 -228 519 546 ; C -1 ; WX 556 ; N ecircumflex ; B 23 -14 528 750 ; C -1 ; WX 722 ; N Uring ; B 72 -19 651 962 ; C -1 ; WX 722 ; N Udieresis ; B 72 -19 651 915 ; C -1 ; WX 556 ; N aogonek ; B 29 -224 545 546 ; C -1 ; WX 722 ; N Uacute ; B 72 -19 651 936 ; C -1 ; WX 611 ; N uogonek ; B 66 -228 545 532 ; C -1 ; WX 667 ; N Edieresis ; B 76 0 621 915 ; C -1 ; WX 722 ; N Dcroat ; B -5 0 685 718 ; C -1 ; WX 250 ; N commaaccent ; B 64 -228 199 -50 ; C -1 ; WX 737 ; N copyright ; B -11 -19 749 737 ; C -1 ; WX 667 ; N Emacron ; B 76 0 621 864 ; C -1 ; WX 556 ; N ccaron ; B 34 -14 524 750 ; C -1 ; WX 556 ; N aring ; B 29 -14 527 776 ; C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 654 718 ; C -1 ; WX 278 ; N lacute ; B 69 0 329 936 ; C -1 ; WX 556 ; N agrave ; B 29 -14 527 750 ; C -1 ; WX 611 ; N Tcommaaccent ; B 14 -228 598 718 ; C -1 ; WX 722 ; N Cacute ; B 44 -19 684 936 ; C -1 ; WX 556 ; N atilde ; B 29 -14 527 737 ; C -1 ; WX 667 ; N Edotaccent ; B 76 0 621 915 ; C -1 ; WX 556 ; N scaron ; B 30 -14 519 750 ; C -1 ; WX 556 ; N scedilla ; B 30 -228 519 546 ; C -1 ; WX 278 ; N iacute ; B 69 0 329 750 ; C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ; C -1 ; WX 722 ; N Rcaron ; B 76 0 677 936 ; C -1 ; WX 778 ; N Gcommaaccent ; B 44 -228 713 737 ; C -1 ; WX 611 ; N ucircumflex ; B 66 -14 545 750 ; C -1 ; WX 556 ; N acircumflex ; B 29 -14 527 750 ; C -1 ; WX 722 ; N Amacron ; B 20 0 702 864 ; C -1 ; WX 389 ; N rcaron ; B 18 0 373 750 ; C -1 ; WX 556 ; N ccedilla ; B 34 -228 524 546 ; C -1 ; WX 611 ; N Zdotaccent ; B 25 0 586 915 ; C -1 ; WX 667 ; N Thorn ; B 76 0 627 718 ; C -1 ; WX 778 ; N Omacron ; B 44 -19 734 864 ; C -1 ; WX 722 ; N Racute ; B 76 0 677 936 ; C -1 ; WX 667 ; N Sacute ; B 39 -19 629 936 ; C -1 ; WX 743 ; N dcaron ; B 34 -14 750 718 ; C -1 ; WX 722 ; N Umacron ; B 72 -19 651 864 ; C -1 ; WX 611 ; N uring ; B 66 -14 545 776 ; C -1 ; WX 333 ; N threesuperior ; B 8 271 326 710 ; C -1 ; WX 778 ; N Ograve ; B 44 -19 734 936 ; C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ; C -1 ; WX 722 ; N Abreve ; B 20 0 702 936 ; C -1 ; WX 584 ; N multiply ; B 40 1 545 505 ; C -1 ; WX 611 ; N uacute ; B 66 -14 545 750 ; C -1 ; WX 611 ; N Tcaron ; B 14 0 598 936 ; C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ; C -1 ; WX 556 ; N ydieresis ; B 10 -214 539 729 ; C -1 ; WX 722 ; N Nacute ; B 69 0 654 936 ; C -1 ; WX 278 ; N icircumflex ; B -37 0 316 750 ; C -1 ; WX 667 ; N Ecircumflex ; B 76 0 621 936 ; C -1 ; WX 556 ; N adieresis ; B 29 -14 527 729 ; C -1 ; WX 556 ; N edieresis ; B 23 -14 528 729 ; C -1 ; WX 556 ; N cacute ; B 34 -14 524 750 ; C -1 ; WX 611 ; N nacute ; B 65 0 546 750 ; C -1 ; WX 611 ; N umacron ; B 66 -14 545 678 ; C -1 ; WX 722 ; N Ncaron ; B 69 0 654 936 ; C -1 ; WX 278 ; N Iacute ; B 64 0 329 936 ; C -1 ; WX 584 ; N plusminus ; B 40 0 544 506 ; C -1 ; WX 280 ; N brokenbar ; B 84 -150 196 700 ; C -1 ; WX 737 ; N registered ; B -11 -19 748 737 ; C -1 ; WX 778 ; N Gbreve ; B 44 -19 713 936 ; C -1 ; WX 278 ; N Idotaccent ; B 64 0 214 915 ; C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ; C -1 ; WX 667 ; N Egrave ; B 76 0 621 936 ; C -1 ; WX 389 ; N racute ; B 64 0 384 750 ; C -1 ; WX 611 ; N omacron ; B 34 -14 578 678 ; C -1 ; WX 611 ; N Zacute ; B 25 0 586 936 ; C -1 ; WX 611 ; N Zcaron ; B 25 0 586 936 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ; C -1 ; WX 722 ; N Eth ; B -5 0 685 718 ; C -1 ; WX 722 ; N Ccedilla ; B 44 -228 684 737 ; C -1 ; WX 278 ; N lcommaaccent ; B 69 -228 213 718 ; C -1 ; WX 389 ; N tcaron ; B 10 -6 421 878 ; C -1 ; WX 556 ; N eogonek ; B 23 -228 528 546 ; C -1 ; WX 722 ; N Uogonek ; B 72 -228 651 718 ; C -1 ; WX 722 ; N Aacute ; B 20 0 702 936 ; C -1 ; WX 722 ; N Adieresis ; B 20 0 702 915 ; C -1 ; WX 556 ; N egrave ; B 23 -14 528 750 ; C -1 ; WX 500 ; N zacute ; B 20 0 480 750 ; C -1 ; WX 278 ; N iogonek ; B 16 -224 249 725 ; C -1 ; WX 778 ; N Oacute ; B 44 -19 734 936 ; C -1 ; WX 611 ; N oacute ; B 34 -14 578 750 ; C -1 ; WX 556 ; N amacron ; B 29 -14 527 678 ; C -1 ; WX 556 ; N sacute ; B 30 -14 519 750 ; C -1 ; WX 278 ; N idieresis ; B -21 0 300 729 ; C -1 ; WX 778 ; N Ocircumflex ; B 44 -19 734 936 ; C -1 ; WX 722 ; N Ugrave ; B 72 -19 651 936 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C -1 ; WX 611 ; N thorn ; B 62 -208 578 718 ; C -1 ; WX 333 ; N twosuperior ; B 9 283 324 710 ; C -1 ; WX 778 ; N Odieresis ; B 44 -19 734 915 ; C -1 ; WX 611 ; N mu ; B 66 -207 545 532 ; C -1 ; WX 278 ; N igrave ; B -50 0 209 750 ; C -1 ; WX 611 ; N ohungarumlaut ; B 34 -14 625 750 ; C -1 ; WX 667 ; N Eogonek ; B 76 -224 639 718 ; C -1 ; WX 611 ; N dcroat ; B 34 -14 650 718 ; C -1 ; WX 834 ; N threequarters ; B 16 -19 799 710 ; C -1 ; WX 667 ; N Scedilla ; B 39 -228 629 737 ; C -1 ; WX 400 ; N lcaron ; B 69 0 408 718 ; C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 722 718 ; C -1 ; WX 611 ; N Lacute ; B 76 0 583 936 ; C -1 ; WX 1000 ; N trademark ; B 44 306 956 718 ; C -1 ; WX 556 ; N edotaccent ; B 23 -14 528 729 ; C -1 ; WX 278 ; N Igrave ; B -50 0 214 936 ; C -1 ; WX 278 ; N Imacron ; B -33 0 312 864 ; C -1 ; WX 611 ; N Lcaron ; B 76 0 583 718 ; C -1 ; WX 834 ; N onehalf ; B 26 -19 794 710 ; C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ; C -1 ; WX 611 ; N ocircumflex ; B 34 -14 578 750 ; C -1 ; WX 611 ; N ntilde ; B 65 0 546 737 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 72 -19 681 936 ; C -1 ; WX 667 ; N Eacute ; B 76 0 621 936 ; C -1 ; WX 556 ; N emacron ; B 23 -14 528 678 ; C -1 ; WX 611 ; N gbreve ; B 40 -217 553 750 ; C -1 ; WX 834 ; N onequarter ; B 26 -19 766 710 ; C -1 ; WX 667 ; N Scaron ; B 39 -19 629 936 ; C -1 ; WX 667 ; N Scommaaccent ; B 39 -228 629 737 ; C -1 ; WX 778 ; N Ohungarumlaut ; B 44 -19 734 936 ; C -1 ; WX 400 ; N degree ; B 57 426 343 712 ; C -1 ; WX 611 ; N ograve ; B 34 -14 578 750 ; C -1 ; WX 722 ; N Ccaron ; B 44 -19 684 936 ; C -1 ; WX 611 ; N ugrave ; B 66 -14 545 750 ; C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ; C -1 ; WX 722 ; N Dcaron ; B 76 0 685 936 ; C -1 ; WX 389 ; N rcommaaccent ; B 64 -228 373 546 ; C -1 ; WX 722 ; N Ntilde ; B 69 0 654 923 ; C -1 ; WX 611 ; N otilde ; B 34 -14 578 737 ; C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 677 718 ; C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 583 718 ; C -1 ; WX 722 ; N Atilde ; B 20 0 702 923 ; C -1 ; WX 722 ; N Aogonek ; B 20 -224 742 718 ; C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ; C -1 ; WX 778 ; N Otilde ; B 44 -19 734 923 ; C -1 ; WX 500 ; N zdotaccent ; B 20 0 480 729 ; C -1 ; WX 667 ; N Ecaron ; B 76 0 621 936 ; C -1 ; WX 278 ; N Iogonek ; B -11 -228 222 718 ; C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 562 718 ; C -1 ; WX 584 ; N minus ; B 40 197 544 309 ; C -1 ; WX 278 ; N Icircumflex ; B -37 0 316 936 ; C -1 ; WX 611 ; N ncaron ; B 65 0 546 750 ; C -1 ; WX 333 ; N tcommaaccent ; B 10 -228 309 676 ; C -1 ; WX 584 ; N logicalnot ; B 40 108 544 419 ; C -1 ; WX 611 ; N odieresis ; B 34 -14 578 729 ; C -1 ; WX 611 ; N udieresis ; B 66 -14 545 729 ; C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ; C -1 ; WX 611 ; N gcommaaccent ; B 40 -217 553 850 ; C -1 ; WX 611 ; N eth ; B 34 -14 578 737 ; C -1 ; WX 500 ; N zcaron ; B 20 0 480 750 ; C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 546 546 ; C -1 ; WX 333 ; N onesuperior ; B 26 283 237 710 ; C -1 ; WX 278 ; N imacron ; B -8 0 285 678 ; C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2481 KPX A C -40 KPX A Cacute -40 KPX A Ccaron -40 KPX A Ccedilla -40 KPX A G -50 KPX A Gbreve -50 KPX A Gcommaaccent -50 KPX A O -40 KPX A Oacute -40 KPX A Ocircumflex -40 KPX A Odieresis -40 KPX A Ograve -40 KPX A Ohungarumlaut -40 KPX A Omacron -40 KPX A Oslash -40 KPX A Otilde -40 KPX A Q -40 KPX A T -90 KPX A Tcaron -90 KPX A Tcommaaccent -90 KPX A U -50 KPX A Uacute -50 KPX A Ucircumflex -50 KPX A Udieresis -50 KPX A Ugrave -50 KPX A Uhungarumlaut -50 KPX A Umacron -50 KPX A Uogonek -50 KPX A Uring -50 KPX A V -80 KPX A W -60 KPX A Y -110 KPX A Yacute -110 KPX A Ydieresis -110 KPX A u -30 KPX A uacute -30 KPX A ucircumflex -30 KPX A udieresis -30 KPX A ugrave -30 KPX A uhungarumlaut -30 KPX A umacron -30 KPX A uogonek -30 KPX A uring -30 KPX A v -40 KPX A w -30 KPX A y -30 KPX A yacute -30 KPX A ydieresis -30 KPX Aacute C -40 KPX Aacute Cacute -40 KPX Aacute Ccaron -40 KPX Aacute Ccedilla -40 KPX Aacute G -50 KPX Aacute Gbreve -50 KPX Aacute Gcommaaccent -50 KPX Aacute O -40 KPX Aacute Oacute -40 KPX Aacute Ocircumflex -40 KPX Aacute Odieresis -40 KPX Aacute Ograve -40 KPX Aacute Ohungarumlaut -40 KPX Aacute Omacron -40 KPX Aacute Oslash -40 KPX Aacute Otilde -40 KPX Aacute Q -40 KPX Aacute T -90 KPX Aacute Tcaron -90 KPX Aacute Tcommaaccent -90 KPX Aacute U -50 KPX Aacute Uacute -50 KPX Aacute Ucircumflex -50 KPX Aacute Udieresis -50 KPX Aacute Ugrave -50 KPX Aacute Uhungarumlaut -50 KPX Aacute Umacron -50 KPX Aacute Uogonek -50 KPX Aacute Uring -50 KPX Aacute V -80 KPX Aacute W -60 KPX Aacute Y -110 KPX Aacute Yacute -110 KPX Aacute Ydieresis -110 KPX Aacute u -30 KPX Aacute uacute -30 KPX Aacute ucircumflex -30 KPX Aacute udieresis -30 KPX Aacute ugrave -30 KPX Aacute uhungarumlaut -30 KPX Aacute umacron -30 KPX Aacute uogonek -30 KPX Aacute uring -30 KPX Aacute v -40 KPX Aacute w -30 KPX Aacute y -30 KPX Aacute yacute -30 KPX Aacute ydieresis -30 KPX Abreve C -40 KPX Abreve Cacute -40 KPX Abreve Ccaron -40 KPX Abreve Ccedilla -40 KPX Abreve G -50 KPX Abreve Gbreve -50 KPX Abreve Gcommaaccent -50 KPX Abreve O -40 KPX Abreve Oacute -40 KPX Abreve Ocircumflex -40 KPX Abreve Odieresis -40 KPX Abreve Ograve -40 KPX Abreve Ohungarumlaut -40 KPX Abreve Omacron -40 KPX Abreve Oslash -40 KPX Abreve Otilde -40 KPX Abreve Q -40 KPX Abreve T -90 KPX Abreve Tcaron -90 KPX Abreve Tcommaaccent -90 KPX Abreve U -50 KPX Abreve Uacute -50 KPX Abreve Ucircumflex -50 KPX Abreve Udieresis -50 KPX Abreve Ugrave -50 KPX Abreve Uhungarumlaut -50 KPX Abreve Umacron -50 KPX Abreve Uogonek -50 KPX Abreve Uring -50 KPX Abreve V -80 KPX Abreve W -60 KPX Abreve Y -110 KPX Abreve Yacute -110 KPX Abreve Ydieresis -110 KPX Abreve u -30 KPX Abreve uacute -30 KPX Abreve ucircumflex -30 KPX Abreve udieresis -30 KPX Abreve ugrave -30 KPX Abreve uhungarumlaut -30 KPX Abreve umacron -30 KPX Abreve uogonek -30 KPX Abreve uring -30 KPX Abreve v -40 KPX Abreve w -30 KPX Abreve y -30 KPX Abreve yacute -30 KPX Abreve ydieresis -30 KPX Acircumflex C -40 KPX Acircumflex Cacute -40 KPX Acircumflex Ccaron -40 KPX Acircumflex Ccedilla -40 KPX Acircumflex G -50 KPX Acircumflex Gbreve -50 KPX Acircumflex Gcommaaccent -50 KPX Acircumflex O -40 KPX Acircumflex Oacute -40 KPX Acircumflex Ocircumflex -40 KPX Acircumflex Odieresis -40 KPX Acircumflex Ograve -40 KPX Acircumflex Ohungarumlaut -40 KPX Acircumflex Omacron -40 KPX Acircumflex Oslash -40 KPX Acircumflex Otilde -40 KPX Acircumflex Q -40 KPX Acircumflex T -90 KPX Acircumflex Tcaron -90 KPX Acircumflex Tcommaaccent -90 KPX Acircumflex U -50 KPX Acircumflex Uacute -50 KPX Acircumflex Ucircumflex -50 KPX Acircumflex Udieresis -50 KPX Acircumflex Ugrave -50 KPX Acircumflex Uhungarumlaut -50 KPX Acircumflex Umacron -50 KPX Acircumflex Uogonek -50 KPX Acircumflex Uring -50 KPX Acircumflex V -80 KPX Acircumflex W -60 KPX Acircumflex Y -110 KPX Acircumflex Yacute -110 KPX Acircumflex Ydieresis -110 KPX Acircumflex u -30 KPX Acircumflex uacute -30 KPX Acircumflex ucircumflex -30 KPX Acircumflex udieresis -30 KPX Acircumflex ugrave -30 KPX Acircumflex uhungarumlaut -30 KPX Acircumflex umacron -30 KPX Acircumflex uogonek -30 KPX Acircumflex uring -30 KPX Acircumflex v -40 KPX Acircumflex w -30 KPX Acircumflex y -30 KPX Acircumflex yacute -30 KPX Acircumflex ydieresis -30 KPX Adieresis C -40 KPX Adieresis Cacute -40 KPX Adieresis Ccaron -40 KPX Adieresis Ccedilla -40 KPX Adieresis G -50 KPX Adieresis Gbreve -50 KPX Adieresis Gcommaaccent -50 KPX Adieresis O -40 KPX Adieresis Oacute -40 KPX Adieresis Ocircumflex -40 KPX Adieresis Odieresis -40 KPX Adieresis Ograve -40 KPX Adieresis Ohungarumlaut -40 KPX Adieresis Omacron -40 KPX Adieresis Oslash -40 KPX Adieresis Otilde -40 KPX Adieresis Q -40 KPX Adieresis T -90 KPX Adieresis Tcaron -90 KPX Adieresis Tcommaaccent -90 KPX Adieresis U -50 KPX Adieresis Uacute -50 KPX Adieresis Ucircumflex -50 KPX Adieresis Udieresis -50 KPX Adieresis Ugrave -50 KPX Adieresis Uhungarumlaut -50 KPX Adieresis Umacron -50 KPX Adieresis Uogonek -50 KPX Adieresis Uring -50 KPX Adieresis V -80 KPX Adieresis W -60 KPX Adieresis Y -110 KPX Adieresis Yacute -110 KPX Adieresis Ydieresis -110 KPX Adieresis u -30 KPX Adieresis uacute -30 KPX Adieresis ucircumflex -30 KPX Adieresis udieresis -30 KPX Adieresis ugrave -30 KPX Adieresis uhungarumlaut -30 KPX Adieresis umacron -30 KPX Adieresis uogonek -30 KPX Adieresis uring -30 KPX Adieresis v -40 KPX Adieresis w -30 KPX Adieresis y -30 KPX Adieresis yacute -30 KPX Adieresis ydieresis -30 KPX Agrave C -40 KPX Agrave Cacute -40 KPX Agrave Ccaron -40 KPX Agrave Ccedilla -40 KPX Agrave G -50 KPX Agrave Gbreve -50 KPX Agrave Gcommaaccent -50 KPX Agrave O -40 KPX Agrave Oacute -40 KPX Agrave Ocircumflex -40 KPX Agrave Odieresis -40 KPX Agrave Ograve -40 KPX Agrave Ohungarumlaut -40 KPX Agrave Omacron -40 KPX Agrave Oslash -40 KPX Agrave Otilde -40 KPX Agrave Q -40 KPX Agrave T -90 KPX Agrave Tcaron -90 KPX Agrave Tcommaaccent -90 KPX Agrave U -50 KPX Agrave Uacute -50 KPX Agrave Ucircumflex -50 KPX Agrave Udieresis -50 KPX Agrave Ugrave -50 KPX Agrave Uhungarumlaut -50 KPX Agrave Umacron -50 KPX Agrave Uogonek -50 KPX Agrave Uring -50 KPX Agrave V -80 KPX Agrave W -60 KPX Agrave Y -110 KPX Agrave Yacute -110 KPX Agrave Ydieresis -110 KPX Agrave u -30 KPX Agrave uacute -30 KPX Agrave ucircumflex -30 KPX Agrave udieresis -30 KPX Agrave ugrave -30 KPX Agrave uhungarumlaut -30 KPX Agrave umacron -30 KPX Agrave uogonek -30 KPX Agrave uring -30 KPX Agrave v -40 KPX Agrave w -30 KPX Agrave y -30 KPX Agrave yacute -30 KPX Agrave ydieresis -30 KPX Amacron C -40 KPX Amacron Cacute -40 KPX Amacron Ccaron -40 KPX Amacron Ccedilla -40 KPX Amacron G -50 KPX Amacron Gbreve -50 KPX Amacron Gcommaaccent -50 KPX Amacron O -40 KPX Amacron Oacute -40 KPX Amacron Ocircumflex -40 KPX Amacron Odieresis -40 KPX Amacron Ograve -40 KPX Amacron Ohungarumlaut -40 KPX Amacron Omacron -40 KPX Amacron Oslash -40 KPX Amacron Otilde -40 KPX Amacron Q -40 KPX Amacron T -90 KPX Amacron Tcaron -90 KPX Amacron Tcommaaccent -90 KPX Amacron U -50 KPX Amacron Uacute -50 KPX Amacron Ucircumflex -50 KPX Amacron Udieresis -50 KPX Amacron Ugrave -50 KPX Amacron Uhungarumlaut -50 KPX Amacron Umacron -50 KPX Amacron Uogonek -50 KPX Amacron Uring -50 KPX Amacron V -80 KPX Amacron W -60 KPX Amacron Y -110 KPX Amacron Yacute -110 KPX Amacron Ydieresis -110 KPX Amacron u -30 KPX Amacron uacute -30 KPX Amacron ucircumflex -30 KPX Amacron udieresis -30 KPX Amacron ugrave -30 KPX Amacron uhungarumlaut -30 KPX Amacron umacron -30 KPX Amacron uogonek -30 KPX Amacron uring -30 KPX Amacron v -40 KPX Amacron w -30 KPX Amacron y -30 KPX Amacron yacute -30 KPX Amacron ydieresis -30 KPX Aogonek C -40 KPX Aogonek Cacute -40 KPX Aogonek Ccaron -40 KPX Aogonek Ccedilla -40 KPX Aogonek G -50 KPX Aogonek Gbreve -50 KPX Aogonek Gcommaaccent -50 KPX Aogonek O -40 KPX Aogonek Oacute -40 KPX Aogonek Ocircumflex -40 KPX Aogonek Odieresis -40 KPX Aogonek Ograve -40 KPX Aogonek Ohungarumlaut -40 KPX Aogonek Omacron -40 KPX Aogonek Oslash -40 KPX Aogonek Otilde -40 KPX Aogonek Q -40 KPX Aogonek T -90 KPX Aogonek Tcaron -90 KPX Aogonek Tcommaaccent -90 KPX Aogonek U -50 KPX Aogonek Uacute -50 KPX Aogonek Ucircumflex -50 KPX Aogonek Udieresis -50 KPX Aogonek Ugrave -50 KPX Aogonek Uhungarumlaut -50 KPX Aogonek Umacron -50 KPX Aogonek Uogonek -50 KPX Aogonek Uring -50 KPX Aogonek V -80 KPX Aogonek W -60 KPX Aogonek Y -110 KPX Aogonek Yacute -110 KPX Aogonek Ydieresis -110 KPX Aogonek u -30 KPX Aogonek uacute -30 KPX Aogonek ucircumflex -30 KPX Aogonek udieresis -30 KPX Aogonek ugrave -30 KPX Aogonek uhungarumlaut -30 KPX Aogonek umacron -30 KPX Aogonek uogonek -30 KPX Aogonek uring -30 KPX Aogonek v -40 KPX Aogonek w -30 KPX Aogonek y -30 KPX Aogonek yacute -30 KPX Aogonek ydieresis -30 KPX Aring C -40 KPX Aring Cacute -40 KPX Aring Ccaron -40 KPX Aring Ccedilla -40 KPX Aring G -50 KPX Aring Gbreve -50 KPX Aring Gcommaaccent -50 KPX Aring O -40 KPX Aring Oacute -40 KPX Aring Ocircumflex -40 KPX Aring Odieresis -40 KPX Aring Ograve -40 KPX Aring Ohungarumlaut -40 KPX Aring Omacron -40 KPX Aring Oslash -40 KPX Aring Otilde -40 KPX Aring Q -40 KPX Aring T -90 KPX Aring Tcaron -90 KPX Aring Tcommaaccent -90 KPX Aring U -50 KPX Aring Uacute -50 KPX Aring Ucircumflex -50 KPX Aring Udieresis -50 KPX Aring Ugrave -50 KPX Aring Uhungarumlaut -50 KPX Aring Umacron -50 KPX Aring Uogonek -50 KPX Aring Uring -50 KPX Aring V -80 KPX Aring W -60 KPX Aring Y -110 KPX Aring Yacute -110 KPX Aring Ydieresis -110 KPX Aring u -30 KPX Aring uacute -30 KPX Aring ucircumflex -30 KPX Aring udieresis -30 KPX Aring ugrave -30 KPX Aring uhungarumlaut -30 KPX Aring umacron -30 KPX Aring uogonek -30 KPX Aring uring -30 KPX Aring v -40 KPX Aring w -30 KPX Aring y -30 KPX Aring yacute -30 KPX Aring ydieresis -30 KPX Atilde C -40 KPX Atilde Cacute -40 KPX Atilde Ccaron -40 KPX Atilde Ccedilla -40 KPX Atilde G -50 KPX Atilde Gbreve -50 KPX Atilde Gcommaaccent -50 KPX Atilde O -40 KPX Atilde Oacute -40 KPX Atilde Ocircumflex -40 KPX Atilde Odieresis -40 KPX Atilde Ograve -40 KPX Atilde Ohungarumlaut -40 KPX Atilde Omacron -40 KPX Atilde Oslash -40 KPX Atilde Otilde -40 KPX Atilde Q -40 KPX Atilde T -90 KPX Atilde Tcaron -90 KPX Atilde Tcommaaccent -90 KPX Atilde U -50 KPX Atilde Uacute -50 KPX Atilde Ucircumflex -50 KPX Atilde Udieresis -50 KPX Atilde Ugrave -50 KPX Atilde Uhungarumlaut -50 KPX Atilde Umacron -50 KPX Atilde Uogonek -50 KPX Atilde Uring -50 KPX Atilde V -80 KPX Atilde W -60 KPX Atilde Y -110 KPX Atilde Yacute -110 KPX Atilde Ydieresis -110 KPX Atilde u -30 KPX Atilde uacute -30 KPX Atilde ucircumflex -30 KPX Atilde udieresis -30 KPX Atilde ugrave -30 KPX Atilde uhungarumlaut -30 KPX Atilde umacron -30 KPX Atilde uogonek -30 KPX Atilde uring -30 KPX Atilde v -40 KPX Atilde w -30 KPX Atilde y -30 KPX Atilde yacute -30 KPX Atilde ydieresis -30 KPX B A -30 KPX B Aacute -30 KPX B Abreve -30 KPX B Acircumflex -30 KPX B Adieresis -30 KPX B Agrave -30 KPX B Amacron -30 KPX B Aogonek -30 KPX B Aring -30 KPX B Atilde -30 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX D A -40 KPX D Aacute -40 KPX D Abreve -40 KPX D Acircumflex -40 KPX D Adieresis -40 KPX D Agrave -40 KPX D Amacron -40 KPX D Aogonek -40 KPX D Aring -40 KPX D Atilde -40 KPX D V -40 KPX D W -40 KPX D Y -70 KPX D Yacute -70 KPX D Ydieresis -70 KPX D comma -30 KPX D period -30 KPX Dcaron A -40 KPX Dcaron Aacute -40 KPX Dcaron Abreve -40 KPX Dcaron Acircumflex -40 KPX Dcaron Adieresis -40 KPX Dcaron Agrave -40 KPX Dcaron Amacron -40 KPX Dcaron Aogonek -40 KPX Dcaron Aring -40 KPX Dcaron Atilde -40 KPX Dcaron V -40 KPX Dcaron W -40 KPX Dcaron Y -70 KPX Dcaron Yacute -70 KPX Dcaron Ydieresis -70 KPX Dcaron comma -30 KPX Dcaron period -30 KPX Dcroat A -40 KPX Dcroat Aacute -40 KPX Dcroat Abreve -40 KPX Dcroat Acircumflex -40 KPX Dcroat Adieresis -40 KPX Dcroat Agrave -40 KPX Dcroat Amacron -40 KPX Dcroat Aogonek -40 KPX Dcroat Aring -40 KPX Dcroat Atilde -40 KPX Dcroat V -40 KPX Dcroat W -40 KPX Dcroat Y -70 KPX Dcroat Yacute -70 KPX Dcroat Ydieresis -70 KPX Dcroat comma -30 KPX Dcroat period -30 KPX F A -80 KPX F Aacute -80 KPX F Abreve -80 KPX F Acircumflex -80 KPX F Adieresis -80 KPX F Agrave -80 KPX F Amacron -80 KPX F Aogonek -80 KPX F Aring -80 KPX F Atilde -80 KPX F a -20 KPX F aacute -20 KPX F abreve -20 KPX F acircumflex -20 KPX F adieresis -20 KPX F agrave -20 KPX F amacron -20 KPX F aogonek -20 KPX F aring -20 KPX F atilde -20 KPX F comma -100 KPX F period -100 KPX J A -20 KPX J Aacute -20 KPX J Abreve -20 KPX J Acircumflex -20 KPX J Adieresis -20 KPX J Agrave -20 KPX J Amacron -20 KPX J Aogonek -20 KPX J Aring -20 KPX J Atilde -20 KPX J comma -20 KPX J period -20 KPX J u -20 KPX J uacute -20 KPX J ucircumflex -20 KPX J udieresis -20 KPX J ugrave -20 KPX J uhungarumlaut -20 KPX J umacron -20 KPX J uogonek -20 KPX J uring -20 KPX K O -30 KPX K Oacute -30 KPX K Ocircumflex -30 KPX K Odieresis -30 KPX K Ograve -30 KPX K Ohungarumlaut -30 KPX K Omacron -30 KPX K Oslash -30 KPX K Otilde -30 KPX K e -15 KPX K eacute -15 KPX K ecaron -15 KPX K ecircumflex -15 KPX K edieresis -15 KPX K edotaccent -15 KPX K egrave -15 KPX K emacron -15 KPX K eogonek -15 KPX K o -35 KPX K oacute -35 KPX K ocircumflex -35 KPX K odieresis -35 KPX K ograve -35 KPX K ohungarumlaut -35 KPX K omacron -35 KPX K oslash -35 KPX K otilde -35 KPX K u -30 KPX K uacute -30 KPX K ucircumflex -30 KPX K udieresis -30 KPX K ugrave -30 KPX K uhungarumlaut -30 KPX K umacron -30 KPX K uogonek -30 KPX K uring -30 KPX K y -40 KPX K yacute -40 KPX K ydieresis -40 KPX Kcommaaccent O -30 KPX Kcommaaccent Oacute -30 KPX Kcommaaccent Ocircumflex -30 KPX Kcommaaccent Odieresis -30 KPX Kcommaaccent Ograve -30 KPX Kcommaaccent Ohungarumlaut -30 KPX Kcommaaccent Omacron -30 KPX Kcommaaccent Oslash -30 KPX Kcommaaccent Otilde -30 KPX Kcommaaccent e -15 KPX Kcommaaccent eacute -15 KPX Kcommaaccent ecaron -15 KPX Kcommaaccent ecircumflex -15 KPX Kcommaaccent edieresis -15 KPX Kcommaaccent edotaccent -15 KPX Kcommaaccent egrave -15 KPX Kcommaaccent emacron -15 KPX Kcommaaccent eogonek -15 KPX Kcommaaccent o -35 KPX Kcommaaccent oacute -35 KPX Kcommaaccent ocircumflex -35 KPX Kcommaaccent odieresis -35 KPX Kcommaaccent ograve -35 KPX Kcommaaccent ohungarumlaut -35 KPX Kcommaaccent omacron -35 KPX Kcommaaccent oslash -35 KPX Kcommaaccent otilde -35 KPX Kcommaaccent u -30 KPX Kcommaaccent uacute -30 KPX Kcommaaccent ucircumflex -30 KPX Kcommaaccent udieresis -30 KPX Kcommaaccent ugrave -30 KPX Kcommaaccent uhungarumlaut -30 KPX Kcommaaccent umacron -30 KPX Kcommaaccent uogonek -30 KPX Kcommaaccent uring -30 KPX Kcommaaccent y -40 KPX Kcommaaccent yacute -40 KPX Kcommaaccent ydieresis -40 KPX L T -90 KPX L Tcaron -90 KPX L Tcommaaccent -90 KPX L V -110 KPX L W -80 KPX L Y -120 KPX L Yacute -120 KPX L Ydieresis -120 KPX L quotedblright -140 KPX L quoteright -140 KPX L y -30 KPX L yacute -30 KPX L ydieresis -30 KPX Lacute T -90 KPX Lacute Tcaron -90 KPX Lacute Tcommaaccent -90 KPX Lacute V -110 KPX Lacute W -80 KPX Lacute Y -120 KPX Lacute Yacute -120 KPX Lacute Ydieresis -120 KPX Lacute quotedblright -140 KPX Lacute quoteright -140 KPX Lacute y -30 KPX Lacute yacute -30 KPX Lacute ydieresis -30 KPX Lcommaaccent T -90 KPX Lcommaaccent Tcaron -90 KPX Lcommaaccent Tcommaaccent -90 KPX Lcommaaccent V -110 KPX Lcommaaccent W -80 KPX Lcommaaccent Y -120 KPX Lcommaaccent Yacute -120 KPX Lcommaaccent Ydieresis -120 KPX Lcommaaccent quotedblright -140 KPX Lcommaaccent quoteright -140 KPX Lcommaaccent y -30 KPX Lcommaaccent yacute -30 KPX Lcommaaccent ydieresis -30 KPX Lslash T -90 KPX Lslash Tcaron -90 KPX Lslash Tcommaaccent -90 KPX Lslash V -110 KPX Lslash W -80 KPX Lslash Y -120 KPX Lslash Yacute -120 KPX Lslash Ydieresis -120 KPX Lslash quotedblright -140 KPX Lslash quoteright -140 KPX Lslash y -30 KPX Lslash yacute -30 KPX Lslash ydieresis -30 KPX O A -50 KPX O Aacute -50 KPX O Abreve -50 KPX O Acircumflex -50 KPX O Adieresis -50 KPX O Agrave -50 KPX O Amacron -50 KPX O Aogonek -50 KPX O Aring -50 KPX O Atilde -50 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -50 KPX O X -50 KPX O Y -70 KPX O Yacute -70 KPX O Ydieresis -70 KPX O comma -40 KPX O period -40 KPX Oacute A -50 KPX Oacute Aacute -50 KPX Oacute Abreve -50 KPX Oacute Acircumflex -50 KPX Oacute Adieresis -50 KPX Oacute Agrave -50 KPX Oacute Amacron -50 KPX Oacute Aogonek -50 KPX Oacute Aring -50 KPX Oacute Atilde -50 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -50 KPX Oacute X -50 KPX Oacute Y -70 KPX Oacute Yacute -70 KPX Oacute Ydieresis -70 KPX Oacute comma -40 KPX Oacute period -40 KPX Ocircumflex A -50 KPX Ocircumflex Aacute -50 KPX Ocircumflex Abreve -50 KPX Ocircumflex Acircumflex -50 KPX Ocircumflex Adieresis -50 KPX Ocircumflex Agrave -50 KPX Ocircumflex Amacron -50 KPX Ocircumflex Aogonek -50 KPX Ocircumflex Aring -50 KPX Ocircumflex Atilde -50 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -50 KPX Ocircumflex X -50 KPX Ocircumflex Y -70 KPX Ocircumflex Yacute -70 KPX Ocircumflex Ydieresis -70 KPX Ocircumflex comma -40 KPX Ocircumflex period -40 KPX Odieresis A -50 KPX Odieresis Aacute -50 KPX Odieresis Abreve -50 KPX Odieresis Acircumflex -50 KPX Odieresis Adieresis -50 KPX Odieresis Agrave -50 KPX Odieresis Amacron -50 KPX Odieresis Aogonek -50 KPX Odieresis Aring -50 KPX Odieresis Atilde -50 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -50 KPX Odieresis X -50 KPX Odieresis Y -70 KPX Odieresis Yacute -70 KPX Odieresis Ydieresis -70 KPX Odieresis comma -40 KPX Odieresis period -40 KPX Ograve A -50 KPX Ograve Aacute -50 KPX Ograve Abreve -50 KPX Ograve Acircumflex -50 KPX Ograve Adieresis -50 KPX Ograve Agrave -50 KPX Ograve Amacron -50 KPX Ograve Aogonek -50 KPX Ograve Aring -50 KPX Ograve Atilde -50 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -50 KPX Ograve X -50 KPX Ograve Y -70 KPX Ograve Yacute -70 KPX Ograve Ydieresis -70 KPX Ograve comma -40 KPX Ograve period -40 KPX Ohungarumlaut A -50 KPX Ohungarumlaut Aacute -50 KPX Ohungarumlaut Abreve -50 KPX Ohungarumlaut Acircumflex -50 KPX Ohungarumlaut Adieresis -50 KPX Ohungarumlaut Agrave -50 KPX Ohungarumlaut Amacron -50 KPX Ohungarumlaut Aogonek -50 KPX Ohungarumlaut Aring -50 KPX Ohungarumlaut Atilde -50 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -50 KPX Ohungarumlaut X -50 KPX Ohungarumlaut Y -70 KPX Ohungarumlaut Yacute -70 KPX Ohungarumlaut Ydieresis -70 KPX Ohungarumlaut comma -40 KPX Ohungarumlaut period -40 KPX Omacron A -50 KPX Omacron Aacute -50 KPX Omacron Abreve -50 KPX Omacron Acircumflex -50 KPX Omacron Adieresis -50 KPX Omacron Agrave -50 KPX Omacron Amacron -50 KPX Omacron Aogonek -50 KPX Omacron Aring -50 KPX Omacron Atilde -50 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -50 KPX Omacron X -50 KPX Omacron Y -70 KPX Omacron Yacute -70 KPX Omacron Ydieresis -70 KPX Omacron comma -40 KPX Omacron period -40 KPX Oslash A -50 KPX Oslash Aacute -50 KPX Oslash Abreve -50 KPX Oslash Acircumflex -50 KPX Oslash Adieresis -50 KPX Oslash Agrave -50 KPX Oslash Amacron -50 KPX Oslash Aogonek -50 KPX Oslash Aring -50 KPX Oslash Atilde -50 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -50 KPX Oslash X -50 KPX Oslash Y -70 KPX Oslash Yacute -70 KPX Oslash Ydieresis -70 KPX Oslash comma -40 KPX Oslash period -40 KPX Otilde A -50 KPX Otilde Aacute -50 KPX Otilde Abreve -50 KPX Otilde Acircumflex -50 KPX Otilde Adieresis -50 KPX Otilde Agrave -50 KPX Otilde Amacron -50 KPX Otilde Aogonek -50 KPX Otilde Aring -50 KPX Otilde Atilde -50 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -50 KPX Otilde X -50 KPX Otilde Y -70 KPX Otilde Yacute -70 KPX Otilde Ydieresis -70 KPX Otilde comma -40 KPX Otilde period -40 KPX P A -100 KPX P Aacute -100 KPX P Abreve -100 KPX P Acircumflex -100 KPX P Adieresis -100 KPX P Agrave -100 KPX P Amacron -100 KPX P Aogonek -100 KPX P Aring -100 KPX P Atilde -100 KPX P a -30 KPX P aacute -30 KPX P abreve -30 KPX P acircumflex -30 KPX P adieresis -30 KPX P agrave -30 KPX P amacron -30 KPX P aogonek -30 KPX P aring -30 KPX P atilde -30 KPX P comma -120 KPX P e -30 KPX P eacute -30 KPX P ecaron -30 KPX P ecircumflex -30 KPX P edieresis -30 KPX P edotaccent -30 KPX P egrave -30 KPX P emacron -30 KPX P eogonek -30 KPX P o -40 KPX P oacute -40 KPX P ocircumflex -40 KPX P odieresis -40 KPX P ograve -40 KPX P ohungarumlaut -40 KPX P omacron -40 KPX P oslash -40 KPX P otilde -40 KPX P period -120 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX Q comma 20 KPX Q period 20 KPX R O -20 KPX R Oacute -20 KPX R Ocircumflex -20 KPX R Odieresis -20 KPX R Ograve -20 KPX R Ohungarumlaut -20 KPX R Omacron -20 KPX R Oslash -20 KPX R Otilde -20 KPX R T -20 KPX R Tcaron -20 KPX R Tcommaaccent -20 KPX R U -20 KPX R Uacute -20 KPX R Ucircumflex -20 KPX R Udieresis -20 KPX R Ugrave -20 KPX R Uhungarumlaut -20 KPX R Umacron -20 KPX R Uogonek -20 KPX R Uring -20 KPX R V -50 KPX R W -40 KPX R Y -50 KPX R Yacute -50 KPX R Ydieresis -50 KPX Racute O -20 KPX Racute Oacute -20 KPX Racute Ocircumflex -20 KPX Racute Odieresis -20 KPX Racute Ograve -20 KPX Racute Ohungarumlaut -20 KPX Racute Omacron -20 KPX Racute Oslash -20 KPX Racute Otilde -20 KPX Racute T -20 KPX Racute Tcaron -20 KPX Racute Tcommaaccent -20 KPX Racute U -20 KPX Racute Uacute -20 KPX Racute Ucircumflex -20 KPX Racute Udieresis -20 KPX Racute Ugrave -20 KPX Racute Uhungarumlaut -20 KPX Racute Umacron -20 KPX Racute Uogonek -20 KPX Racute Uring -20 KPX Racute V -50 KPX Racute W -40 KPX Racute Y -50 KPX Racute Yacute -50 KPX Racute Ydieresis -50 KPX Rcaron O -20 KPX Rcaron Oacute -20 KPX Rcaron Ocircumflex -20 KPX Rcaron Odieresis -20 KPX Rcaron Ograve -20 KPX Rcaron Ohungarumlaut -20 KPX Rcaron Omacron -20 KPX Rcaron Oslash -20 KPX Rcaron Otilde -20 KPX Rcaron T -20 KPX Rcaron Tcaron -20 KPX Rcaron Tcommaaccent -20 KPX Rcaron U -20 KPX Rcaron Uacute -20 KPX Rcaron Ucircumflex -20 KPX Rcaron Udieresis -20 KPX Rcaron Ugrave -20 KPX Rcaron Uhungarumlaut -20 KPX Rcaron Umacron -20 KPX Rcaron Uogonek -20 KPX Rcaron Uring -20 KPX Rcaron V -50 KPX Rcaron W -40 KPX Rcaron Y -50 KPX Rcaron Yacute -50 KPX Rcaron Ydieresis -50 KPX Rcommaaccent O -20 KPX Rcommaaccent Oacute -20 KPX Rcommaaccent Ocircumflex -20 KPX Rcommaaccent Odieresis -20 KPX Rcommaaccent Ograve -20 KPX Rcommaaccent Ohungarumlaut -20 KPX Rcommaaccent Omacron -20 KPX Rcommaaccent Oslash -20 KPX Rcommaaccent Otilde -20 KPX Rcommaaccent T -20 KPX Rcommaaccent Tcaron -20 KPX Rcommaaccent Tcommaaccent -20 KPX Rcommaaccent U -20 KPX Rcommaaccent Uacute -20 KPX Rcommaaccent Ucircumflex -20 KPX Rcommaaccent Udieresis -20 KPX Rcommaaccent Ugrave -20 KPX Rcommaaccent Uhungarumlaut -20 KPX Rcommaaccent Umacron -20 KPX Rcommaaccent Uogonek -20 KPX Rcommaaccent Uring -20 KPX Rcommaaccent V -50 KPX Rcommaaccent W -40 KPX Rcommaaccent Y -50 KPX Rcommaaccent Yacute -50 KPX Rcommaaccent Ydieresis -50 KPX T A -90 KPX T Aacute -90 KPX T Abreve -90 KPX T Acircumflex -90 KPX T Adieresis -90 KPX T Agrave -90 KPX T Amacron -90 KPX T Aogonek -90 KPX T Aring -90 KPX T Atilde -90 KPX T O -40 KPX T Oacute -40 KPX T Ocircumflex -40 KPX T Odieresis -40 KPX T Ograve -40 KPX T Ohungarumlaut -40 KPX T Omacron -40 KPX T Oslash -40 KPX T Otilde -40 KPX T a -80 KPX T aacute -80 KPX T abreve -80 KPX T acircumflex -80 KPX T adieresis -80 KPX T agrave -80 KPX T amacron -80 KPX T aogonek -80 KPX T aring -80 KPX T atilde -80 KPX T colon -40 KPX T comma -80 KPX T e -60 KPX T eacute -60 KPX T ecaron -60 KPX T ecircumflex -60 KPX T edieresis -60 KPX T edotaccent -60 KPX T egrave -60 KPX T emacron -60 KPX T eogonek -60 KPX T hyphen -120 KPX T o -80 KPX T oacute -80 KPX T ocircumflex -80 KPX T odieresis -80 KPX T ograve -80 KPX T ohungarumlaut -80 KPX T omacron -80 KPX T oslash -80 KPX T otilde -80 KPX T period -80 KPX T r -80 KPX T racute -80 KPX T rcommaaccent -80 KPX T semicolon -40 KPX T u -90 KPX T uacute -90 KPX T ucircumflex -90 KPX T udieresis -90 KPX T ugrave -90 KPX T uhungarumlaut -90 KPX T umacron -90 KPX T uogonek -90 KPX T uring -90 KPX T w -60 KPX T y -60 KPX T yacute -60 KPX T ydieresis -60 KPX Tcaron A -90 KPX Tcaron Aacute -90 KPX Tcaron Abreve -90 KPX Tcaron Acircumflex -90 KPX Tcaron Adieresis -90 KPX Tcaron Agrave -90 KPX Tcaron Amacron -90 KPX Tcaron Aogonek -90 KPX Tcaron Aring -90 KPX Tcaron Atilde -90 KPX Tcaron O -40 KPX Tcaron Oacute -40 KPX Tcaron Ocircumflex -40 KPX Tcaron Odieresis -40 KPX Tcaron Ograve -40 KPX Tcaron Ohungarumlaut -40 KPX Tcaron Omacron -40 KPX Tcaron Oslash -40 KPX Tcaron Otilde -40 KPX Tcaron a -80 KPX Tcaron aacute -80 KPX Tcaron abreve -80 KPX Tcaron acircumflex -80 KPX Tcaron adieresis -80 KPX Tcaron agrave -80 KPX Tcaron amacron -80 KPX Tcaron aogonek -80 KPX Tcaron aring -80 KPX Tcaron atilde -80 KPX Tcaron colon -40 KPX Tcaron comma -80 KPX Tcaron e -60 KPX Tcaron eacute -60 KPX Tcaron ecaron -60 KPX Tcaron ecircumflex -60 KPX Tcaron edieresis -60 KPX Tcaron edotaccent -60 KPX Tcaron egrave -60 KPX Tcaron emacron -60 KPX Tcaron eogonek -60 KPX Tcaron hyphen -120 KPX Tcaron o -80 KPX Tcaron oacute -80 KPX Tcaron ocircumflex -80 KPX Tcaron odieresis -80 KPX Tcaron ograve -80 KPX Tcaron ohungarumlaut -80 KPX Tcaron omacron -80 KPX Tcaron oslash -80 KPX Tcaron otilde -80 KPX Tcaron period -80 KPX Tcaron r -80 KPX Tcaron racute -80 KPX Tcaron rcommaaccent -80 KPX Tcaron semicolon -40 KPX Tcaron u -90 KPX Tcaron uacute -90 KPX Tcaron ucircumflex -90 KPX Tcaron udieresis -90 KPX Tcaron ugrave -90 KPX Tcaron uhungarumlaut -90 KPX Tcaron umacron -90 KPX Tcaron uogonek -90 KPX Tcaron uring -90 KPX Tcaron w -60 KPX Tcaron y -60 KPX Tcaron yacute -60 KPX Tcaron ydieresis -60 KPX Tcommaaccent A -90 KPX Tcommaaccent Aacute -90 KPX Tcommaaccent Abreve -90 KPX Tcommaaccent Acircumflex -90 KPX Tcommaaccent Adieresis -90 KPX Tcommaaccent Agrave -90 KPX Tcommaaccent Amacron -90 KPX Tcommaaccent Aogonek -90 KPX Tcommaaccent Aring -90 KPX Tcommaaccent Atilde -90 KPX Tcommaaccent O -40 KPX Tcommaaccent Oacute -40 KPX Tcommaaccent Ocircumflex -40 KPX Tcommaaccent Odieresis -40 KPX Tcommaaccent Ograve -40 KPX Tcommaaccent Ohungarumlaut -40 KPX Tcommaaccent Omacron -40 KPX Tcommaaccent Oslash -40 KPX Tcommaaccent Otilde -40 KPX Tcommaaccent a -80 KPX Tcommaaccent aacute -80 KPX Tcommaaccent abreve -80 KPX Tcommaaccent acircumflex -80 KPX Tcommaaccent adieresis -80 KPX Tcommaaccent agrave -80 KPX Tcommaaccent amacron -80 KPX Tcommaaccent aogonek -80 KPX Tcommaaccent aring -80 KPX Tcommaaccent atilde -80 KPX Tcommaaccent colon -40 KPX Tcommaaccent comma -80 KPX Tcommaaccent e -60 KPX Tcommaaccent eacute -60 KPX Tcommaaccent ecaron -60 KPX Tcommaaccent ecircumflex -60 KPX Tcommaaccent edieresis -60 KPX Tcommaaccent edotaccent -60 KPX Tcommaaccent egrave -60 KPX Tcommaaccent emacron -60 KPX Tcommaaccent eogonek -60 KPX Tcommaaccent hyphen -120 KPX Tcommaaccent o -80 KPX Tcommaaccent oacute -80 KPX Tcommaaccent ocircumflex -80 KPX Tcommaaccent odieresis -80 KPX Tcommaaccent ograve -80 KPX Tcommaaccent ohungarumlaut -80 KPX Tcommaaccent omacron -80 KPX Tcommaaccent oslash -80 KPX Tcommaaccent otilde -80 KPX Tcommaaccent period -80 KPX Tcommaaccent r -80 KPX Tcommaaccent racute -80 KPX Tcommaaccent rcommaaccent -80 KPX Tcommaaccent semicolon -40 KPX Tcommaaccent u -90 KPX Tcommaaccent uacute -90 KPX Tcommaaccent ucircumflex -90 KPX Tcommaaccent udieresis -90 KPX Tcommaaccent ugrave -90 KPX Tcommaaccent uhungarumlaut -90 KPX Tcommaaccent umacron -90 KPX Tcommaaccent uogonek -90 KPX Tcommaaccent uring -90 KPX Tcommaaccent w -60 KPX Tcommaaccent y -60 KPX Tcommaaccent yacute -60 KPX Tcommaaccent ydieresis -60 KPX U A -50 KPX U Aacute -50 KPX U Abreve -50 KPX U Acircumflex -50 KPX U Adieresis -50 KPX U Agrave -50 KPX U Amacron -50 KPX U Aogonek -50 KPX U Aring -50 KPX U Atilde -50 KPX U comma -30 KPX U period -30 KPX Uacute A -50 KPX Uacute Aacute -50 KPX Uacute Abreve -50 KPX Uacute Acircumflex -50 KPX Uacute Adieresis -50 KPX Uacute Agrave -50 KPX Uacute Amacron -50 KPX Uacute Aogonek -50 KPX Uacute Aring -50 KPX Uacute Atilde -50 KPX Uacute comma -30 KPX Uacute period -30 KPX Ucircumflex A -50 KPX Ucircumflex Aacute -50 KPX Ucircumflex Abreve -50 KPX Ucircumflex Acircumflex -50 KPX Ucircumflex Adieresis -50 KPX Ucircumflex Agrave -50 KPX Ucircumflex Amacron -50 KPX Ucircumflex Aogonek -50 KPX Ucircumflex Aring -50 KPX Ucircumflex Atilde -50 KPX Ucircumflex comma -30 KPX Ucircumflex period -30 KPX Udieresis A -50 KPX Udieresis Aacute -50 KPX Udieresis Abreve -50 KPX Udieresis Acircumflex -50 KPX Udieresis Adieresis -50 KPX Udieresis Agrave -50 KPX Udieresis Amacron -50 KPX Udieresis Aogonek -50 KPX Udieresis Aring -50 KPX Udieresis Atilde -50 KPX Udieresis comma -30 KPX Udieresis period -30 KPX Ugrave A -50 KPX Ugrave Aacute -50 KPX Ugrave Abreve -50 KPX Ugrave Acircumflex -50 KPX Ugrave Adieresis -50 KPX Ugrave Agrave -50 KPX Ugrave Amacron -50 KPX Ugrave Aogonek -50 KPX Ugrave Aring -50 KPX Ugrave Atilde -50 KPX Ugrave comma -30 KPX Ugrave period -30 KPX Uhungarumlaut A -50 KPX Uhungarumlaut Aacute -50 KPX Uhungarumlaut Abreve -50 KPX Uhungarumlaut Acircumflex -50 KPX Uhungarumlaut Adieresis -50 KPX Uhungarumlaut Agrave -50 KPX Uhungarumlaut Amacron -50 KPX Uhungarumlaut Aogonek -50 KPX Uhungarumlaut Aring -50 KPX Uhungarumlaut Atilde -50 KPX Uhungarumlaut comma -30 KPX Uhungarumlaut period -30 KPX Umacron A -50 KPX Umacron Aacute -50 KPX Umacron Abreve -50 KPX Umacron Acircumflex -50 KPX Umacron Adieresis -50 KPX Umacron Agrave -50 KPX Umacron Amacron -50 KPX Umacron Aogonek -50 KPX Umacron Aring -50 KPX Umacron Atilde -50 KPX Umacron comma -30 KPX Umacron period -30 KPX Uogonek A -50 KPX Uogonek Aacute -50 KPX Uogonek Abreve -50 KPX Uogonek Acircumflex -50 KPX Uogonek Adieresis -50 KPX Uogonek Agrave -50 KPX Uogonek Amacron -50 KPX Uogonek Aogonek -50 KPX Uogonek Aring -50 KPX Uogonek Atilde -50 KPX Uogonek comma -30 KPX Uogonek period -30 KPX Uring A -50 KPX Uring Aacute -50 KPX Uring Abreve -50 KPX Uring Acircumflex -50 KPX Uring Adieresis -50 KPX Uring Agrave -50 KPX Uring Amacron -50 KPX Uring Aogonek -50 KPX Uring Aring -50 KPX Uring Atilde -50 KPX Uring comma -30 KPX Uring period -30 KPX V A -80 KPX V Aacute -80 KPX V Abreve -80 KPX V Acircumflex -80 KPX V Adieresis -80 KPX V Agrave -80 KPX V Amacron -80 KPX V Aogonek -80 KPX V Aring -80 KPX V Atilde -80 KPX V G -50 KPX V Gbreve -50 KPX V Gcommaaccent -50 KPX V O -50 KPX V Oacute -50 KPX V Ocircumflex -50 KPX V Odieresis -50 KPX V Ograve -50 KPX V Ohungarumlaut -50 KPX V Omacron -50 KPX V Oslash -50 KPX V Otilde -50 KPX V a -60 KPX V aacute -60 KPX V abreve -60 KPX V acircumflex -60 KPX V adieresis -60 KPX V agrave -60 KPX V amacron -60 KPX V aogonek -60 KPX V aring -60 KPX V atilde -60 KPX V colon -40 KPX V comma -120 KPX V e -50 KPX V eacute -50 KPX V ecaron -50 KPX V ecircumflex -50 KPX V edieresis -50 KPX V edotaccent -50 KPX V egrave -50 KPX V emacron -50 KPX V eogonek -50 KPX V hyphen -80 KPX V o -90 KPX V oacute -90 KPX V ocircumflex -90 KPX V odieresis -90 KPX V ograve -90 KPX V ohungarumlaut -90 KPX V omacron -90 KPX V oslash -90 KPX V otilde -90 KPX V period -120 KPX V semicolon -40 KPX V u -60 KPX V uacute -60 KPX V ucircumflex -60 KPX V udieresis -60 KPX V ugrave -60 KPX V uhungarumlaut -60 KPX V umacron -60 KPX V uogonek -60 KPX V uring -60 KPX W A -60 KPX W Aacute -60 KPX W Abreve -60 KPX W Acircumflex -60 KPX W Adieresis -60 KPX W Agrave -60 KPX W Amacron -60 KPX W Aogonek -60 KPX W Aring -60 KPX W Atilde -60 KPX W O -20 KPX W Oacute -20 KPX W Ocircumflex -20 KPX W Odieresis -20 KPX W Ograve -20 KPX W Ohungarumlaut -20 KPX W Omacron -20 KPX W Oslash -20 KPX W Otilde -20 KPX W a -40 KPX W aacute -40 KPX W abreve -40 KPX W acircumflex -40 KPX W adieresis -40 KPX W agrave -40 KPX W amacron -40 KPX W aogonek -40 KPX W aring -40 KPX W atilde -40 KPX W colon -10 KPX W comma -80 KPX W e -35 KPX W eacute -35 KPX W ecaron -35 KPX W ecircumflex -35 KPX W edieresis -35 KPX W edotaccent -35 KPX W egrave -35 KPX W emacron -35 KPX W eogonek -35 KPX W hyphen -40 KPX W o -60 KPX W oacute -60 KPX W ocircumflex -60 KPX W odieresis -60 KPX W ograve -60 KPX W ohungarumlaut -60 KPX W omacron -60 KPX W oslash -60 KPX W otilde -60 KPX W period -80 KPX W semicolon -10 KPX W u -45 KPX W uacute -45 KPX W ucircumflex -45 KPX W udieresis -45 KPX W ugrave -45 KPX W uhungarumlaut -45 KPX W umacron -45 KPX W uogonek -45 KPX W uring -45 KPX W y -20 KPX W yacute -20 KPX W ydieresis -20 KPX Y A -110 KPX Y Aacute -110 KPX Y Abreve -110 KPX Y Acircumflex -110 KPX Y Adieresis -110 KPX Y Agrave -110 KPX Y Amacron -110 KPX Y Aogonek -110 KPX Y Aring -110 KPX Y Atilde -110 KPX Y O -70 KPX Y Oacute -70 KPX Y Ocircumflex -70 KPX Y Odieresis -70 KPX Y Ograve -70 KPX Y Ohungarumlaut -70 KPX Y Omacron -70 KPX Y Oslash -70 KPX Y Otilde -70 KPX Y a -90 KPX Y aacute -90 KPX Y abreve -90 KPX Y acircumflex -90 KPX Y adieresis -90 KPX Y agrave -90 KPX Y amacron -90 KPX Y aogonek -90 KPX Y aring -90 KPX Y atilde -90 KPX Y colon -50 KPX Y comma -100 KPX Y e -80 KPX Y eacute -80 KPX Y ecaron -80 KPX Y ecircumflex -80 KPX Y edieresis -80 KPX Y edotaccent -80 KPX Y egrave -80 KPX Y emacron -80 KPX Y eogonek -80 KPX Y o -100 KPX Y oacute -100 KPX Y ocircumflex -100 KPX Y odieresis -100 KPX Y ograve -100 KPX Y ohungarumlaut -100 KPX Y omacron -100 KPX Y oslash -100 KPX Y otilde -100 KPX Y period -100 KPX Y semicolon -50 KPX Y u -100 KPX Y uacute -100 KPX Y ucircumflex -100 KPX Y udieresis -100 KPX Y ugrave -100 KPX Y uhungarumlaut -100 KPX Y umacron -100 KPX Y uogonek -100 KPX Y uring -100 KPX Yacute A -110 KPX Yacute Aacute -110 KPX Yacute Abreve -110 KPX Yacute Acircumflex -110 KPX Yacute Adieresis -110 KPX Yacute Agrave -110 KPX Yacute Amacron -110 KPX Yacute Aogonek -110 KPX Yacute Aring -110 KPX Yacute Atilde -110 KPX Yacute O -70 KPX Yacute Oacute -70 KPX Yacute Ocircumflex -70 KPX Yacute Odieresis -70 KPX Yacute Ograve -70 KPX Yacute Ohungarumlaut -70 KPX Yacute Omacron -70 KPX Yacute Oslash -70 KPX Yacute Otilde -70 KPX Yacute a -90 KPX Yacute aacute -90 KPX Yacute abreve -90 KPX Yacute acircumflex -90 KPX Yacute adieresis -90 KPX Yacute agrave -90 KPX Yacute amacron -90 KPX Yacute aogonek -90 KPX Yacute aring -90 KPX Yacute atilde -90 KPX Yacute colon -50 KPX Yacute comma -100 KPX Yacute e -80 KPX Yacute eacute -80 KPX Yacute ecaron -80 KPX Yacute ecircumflex -80 KPX Yacute edieresis -80 KPX Yacute edotaccent -80 KPX Yacute egrave -80 KPX Yacute emacron -80 KPX Yacute eogonek -80 KPX Yacute o -100 KPX Yacute oacute -100 KPX Yacute ocircumflex -100 KPX Yacute odieresis -100 KPX Yacute ograve -100 KPX Yacute ohungarumlaut -100 KPX Yacute omacron -100 KPX Yacute oslash -100 KPX Yacute otilde -100 KPX Yacute period -100 KPX Yacute semicolon -50 KPX Yacute u -100 KPX Yacute uacute -100 KPX Yacute ucircumflex -100 KPX Yacute udieresis -100 KPX Yacute ugrave -100 KPX Yacute uhungarumlaut -100 KPX Yacute umacron -100 KPX Yacute uogonek -100 KPX Yacute uring -100 KPX Ydieresis A -110 KPX Ydieresis Aacute -110 KPX Ydieresis Abreve -110 KPX Ydieresis Acircumflex -110 KPX Ydieresis Adieresis -110 KPX Ydieresis Agrave -110 KPX Ydieresis Amacron -110 KPX Ydieresis Aogonek -110 KPX Ydieresis Aring -110 KPX Ydieresis Atilde -110 KPX Ydieresis O -70 KPX Ydieresis Oacute -70 KPX Ydieresis Ocircumflex -70 KPX Ydieresis Odieresis -70 KPX Ydieresis Ograve -70 KPX Ydieresis Ohungarumlaut -70 KPX Ydieresis Omacron -70 KPX Ydieresis Oslash -70 KPX Ydieresis Otilde -70 KPX Ydieresis a -90 KPX Ydieresis aacute -90 KPX Ydieresis abreve -90 KPX Ydieresis acircumflex -90 KPX Ydieresis adieresis -90 KPX Ydieresis agrave -90 KPX Ydieresis amacron -90 KPX Ydieresis aogonek -90 KPX Ydieresis aring -90 KPX Ydieresis atilde -90 KPX Ydieresis colon -50 KPX Ydieresis comma -100 KPX Ydieresis e -80 KPX Ydieresis eacute -80 KPX Ydieresis ecaron -80 KPX Ydieresis ecircumflex -80 KPX Ydieresis edieresis -80 KPX Ydieresis edotaccent -80 KPX Ydieresis egrave -80 KPX Ydieresis emacron -80 KPX Ydieresis eogonek -80 KPX Ydieresis o -100 KPX Ydieresis oacute -100 KPX Ydieresis ocircumflex -100 KPX Ydieresis odieresis -100 KPX Ydieresis ograve -100 KPX Ydieresis ohungarumlaut -100 KPX Ydieresis omacron -100 KPX Ydieresis oslash -100 KPX Ydieresis otilde -100 KPX Ydieresis period -100 KPX Ydieresis semicolon -50 KPX Ydieresis u -100 KPX Ydieresis uacute -100 KPX Ydieresis ucircumflex -100 KPX Ydieresis udieresis -100 KPX Ydieresis ugrave -100 KPX Ydieresis uhungarumlaut -100 KPX Ydieresis umacron -100 KPX Ydieresis uogonek -100 KPX Ydieresis uring -100 KPX a g -10 KPX a gbreve -10 KPX a gcommaaccent -10 KPX a v -15 KPX a w -15 KPX a y -20 KPX a yacute -20 KPX a ydieresis -20 KPX aacute g -10 KPX aacute gbreve -10 KPX aacute gcommaaccent -10 KPX aacute v -15 KPX aacute w -15 KPX aacute y -20 KPX aacute yacute -20 KPX aacute ydieresis -20 KPX abreve g -10 KPX abreve gbreve -10 KPX abreve gcommaaccent -10 KPX abreve v -15 KPX abreve w -15 KPX abreve y -20 KPX abreve yacute -20 KPX abreve ydieresis -20 KPX acircumflex g -10 KPX acircumflex gbreve -10 KPX acircumflex gcommaaccent -10 KPX acircumflex v -15 KPX acircumflex w -15 KPX acircumflex y -20 KPX acircumflex yacute -20 KPX acircumflex ydieresis -20 KPX adieresis g -10 KPX adieresis gbreve -10 KPX adieresis gcommaaccent -10 KPX adieresis v -15 KPX adieresis w -15 KPX adieresis y -20 KPX adieresis yacute -20 KPX adieresis ydieresis -20 KPX agrave g -10 KPX agrave gbreve -10 KPX agrave gcommaaccent -10 KPX agrave v -15 KPX agrave w -15 KPX agrave y -20 KPX agrave yacute -20 KPX agrave ydieresis -20 KPX amacron g -10 KPX amacron gbreve -10 KPX amacron gcommaaccent -10 KPX amacron v -15 KPX amacron w -15 KPX amacron y -20 KPX amacron yacute -20 KPX amacron ydieresis -20 KPX aogonek g -10 KPX aogonek gbreve -10 KPX aogonek gcommaaccent -10 KPX aogonek v -15 KPX aogonek w -15 KPX aogonek y -20 KPX aogonek yacute -20 KPX aogonek ydieresis -20 KPX aring g -10 KPX aring gbreve -10 KPX aring gcommaaccent -10 KPX aring v -15 KPX aring w -15 KPX aring y -20 KPX aring yacute -20 KPX aring ydieresis -20 KPX atilde g -10 KPX atilde gbreve -10 KPX atilde gcommaaccent -10 KPX atilde v -15 KPX atilde w -15 KPX atilde y -20 KPX atilde yacute -20 KPX atilde ydieresis -20 KPX b l -10 KPX b lacute -10 KPX b lcommaaccent -10 KPX b lslash -10 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX b v -20 KPX b y -20 KPX b yacute -20 KPX b ydieresis -20 KPX c h -10 KPX c k -20 KPX c kcommaaccent -20 KPX c l -20 KPX c lacute -20 KPX c lcommaaccent -20 KPX c lslash -20 KPX c y -10 KPX c yacute -10 KPX c ydieresis -10 KPX cacute h -10 KPX cacute k -20 KPX cacute kcommaaccent -20 KPX cacute l -20 KPX cacute lacute -20 KPX cacute lcommaaccent -20 KPX cacute lslash -20 KPX cacute y -10 KPX cacute yacute -10 KPX cacute ydieresis -10 KPX ccaron h -10 KPX ccaron k -20 KPX ccaron kcommaaccent -20 KPX ccaron l -20 KPX ccaron lacute -20 KPX ccaron lcommaaccent -20 KPX ccaron lslash -20 KPX ccaron y -10 KPX ccaron yacute -10 KPX ccaron ydieresis -10 KPX ccedilla h -10 KPX ccedilla k -20 KPX ccedilla kcommaaccent -20 KPX ccedilla l -20 KPX ccedilla lacute -20 KPX ccedilla lcommaaccent -20 KPX ccedilla lslash -20 KPX ccedilla y -10 KPX ccedilla yacute -10 KPX ccedilla ydieresis -10 KPX colon space -40 KPX comma quotedblright -120 KPX comma quoteright -120 KPX comma space -40 KPX d d -10 KPX d dcroat -10 KPX d v -15 KPX d w -15 KPX d y -15 KPX d yacute -15 KPX d ydieresis -15 KPX dcroat d -10 KPX dcroat dcroat -10 KPX dcroat v -15 KPX dcroat w -15 KPX dcroat y -15 KPX dcroat yacute -15 KPX dcroat ydieresis -15 KPX e comma 10 KPX e period 20 KPX e v -15 KPX e w -15 KPX e x -15 KPX e y -15 KPX e yacute -15 KPX e ydieresis -15 KPX eacute comma 10 KPX eacute period 20 KPX eacute v -15 KPX eacute w -15 KPX eacute x -15 KPX eacute y -15 KPX eacute yacute -15 KPX eacute ydieresis -15 KPX ecaron comma 10 KPX ecaron period 20 KPX ecaron v -15 KPX ecaron w -15 KPX ecaron x -15 KPX ecaron y -15 KPX ecaron yacute -15 KPX ecaron ydieresis -15 KPX ecircumflex comma 10 KPX ecircumflex period 20 KPX ecircumflex v -15 KPX ecircumflex w -15 KPX ecircumflex x -15 KPX ecircumflex y -15 KPX ecircumflex yacute -15 KPX ecircumflex ydieresis -15 KPX edieresis comma 10 KPX edieresis period 20 KPX edieresis v -15 KPX edieresis w -15 KPX edieresis x -15 KPX edieresis y -15 KPX edieresis yacute -15 KPX edieresis ydieresis -15 KPX edotaccent comma 10 KPX edotaccent period 20 KPX edotaccent v -15 KPX edotaccent w -15 KPX edotaccent x -15 KPX edotaccent y -15 KPX edotaccent yacute -15 KPX edotaccent ydieresis -15 KPX egrave comma 10 KPX egrave period 20 KPX egrave v -15 KPX egrave w -15 KPX egrave x -15 KPX egrave y -15 KPX egrave yacute -15 KPX egrave ydieresis -15 KPX emacron comma 10 KPX emacron period 20 KPX emacron v -15 KPX emacron w -15 KPX emacron x -15 KPX emacron y -15 KPX emacron yacute -15 KPX emacron ydieresis -15 KPX eogonek comma 10 KPX eogonek period 20 KPX eogonek v -15 KPX eogonek w -15 KPX eogonek x -15 KPX eogonek y -15 KPX eogonek yacute -15 KPX eogonek ydieresis -15 KPX f comma -10 KPX f e -10 KPX f eacute -10 KPX f ecaron -10 KPX f ecircumflex -10 KPX f edieresis -10 KPX f edotaccent -10 KPX f egrave -10 KPX f emacron -10 KPX f eogonek -10 KPX f o -20 KPX f oacute -20 KPX f ocircumflex -20 KPX f odieresis -20 KPX f ograve -20 KPX f ohungarumlaut -20 KPX f omacron -20 KPX f oslash -20 KPX f otilde -20 KPX f period -10 KPX f quotedblright 30 KPX f quoteright 30 KPX g e 10 KPX g eacute 10 KPX g ecaron 10 KPX g ecircumflex 10 KPX g edieresis 10 KPX g edotaccent 10 KPX g egrave 10 KPX g emacron 10 KPX g eogonek 10 KPX g g -10 KPX g gbreve -10 KPX g gcommaaccent -10 KPX gbreve e 10 KPX gbreve eacute 10 KPX gbreve ecaron 10 KPX gbreve ecircumflex 10 KPX gbreve edieresis 10 KPX gbreve edotaccent 10 KPX gbreve egrave 10 KPX gbreve emacron 10 KPX gbreve eogonek 10 KPX gbreve g -10 KPX gbreve gbreve -10 KPX gbreve gcommaaccent -10 KPX gcommaaccent e 10 KPX gcommaaccent eacute 10 KPX gcommaaccent ecaron 10 KPX gcommaaccent ecircumflex 10 KPX gcommaaccent edieresis 10 KPX gcommaaccent edotaccent 10 KPX gcommaaccent egrave 10 KPX gcommaaccent emacron 10 KPX gcommaaccent eogonek 10 KPX gcommaaccent g -10 KPX gcommaaccent gbreve -10 KPX gcommaaccent gcommaaccent -10 KPX h y -20 KPX h yacute -20 KPX h ydieresis -20 KPX k o -15 KPX k oacute -15 KPX k ocircumflex -15 KPX k odieresis -15 KPX k ograve -15 KPX k ohungarumlaut -15 KPX k omacron -15 KPX k oslash -15 KPX k otilde -15 KPX kcommaaccent o -15 KPX kcommaaccent oacute -15 KPX kcommaaccent ocircumflex -15 KPX kcommaaccent odieresis -15 KPX kcommaaccent ograve -15 KPX kcommaaccent ohungarumlaut -15 KPX kcommaaccent omacron -15 KPX kcommaaccent oslash -15 KPX kcommaaccent otilde -15 KPX l w -15 KPX l y -15 KPX l yacute -15 KPX l ydieresis -15 KPX lacute w -15 KPX lacute y -15 KPX lacute yacute -15 KPX lacute ydieresis -15 KPX lcommaaccent w -15 KPX lcommaaccent y -15 KPX lcommaaccent yacute -15 KPX lcommaaccent ydieresis -15 KPX lslash w -15 KPX lslash y -15 KPX lslash yacute -15 KPX lslash ydieresis -15 KPX m u -20 KPX m uacute -20 KPX m ucircumflex -20 KPX m udieresis -20 KPX m ugrave -20 KPX m uhungarumlaut -20 KPX m umacron -20 KPX m uogonek -20 KPX m uring -20 KPX m y -30 KPX m yacute -30 KPX m ydieresis -30 KPX n u -10 KPX n uacute -10 KPX n ucircumflex -10 KPX n udieresis -10 KPX n ugrave -10 KPX n uhungarumlaut -10 KPX n umacron -10 KPX n uogonek -10 KPX n uring -10 KPX n v -40 KPX n y -20 KPX n yacute -20 KPX n ydieresis -20 KPX nacute u -10 KPX nacute uacute -10 KPX nacute ucircumflex -10 KPX nacute udieresis -10 KPX nacute ugrave -10 KPX nacute uhungarumlaut -10 KPX nacute umacron -10 KPX nacute uogonek -10 KPX nacute uring -10 KPX nacute v -40 KPX nacute y -20 KPX nacute yacute -20 KPX nacute ydieresis -20 KPX ncaron u -10 KPX ncaron uacute -10 KPX ncaron ucircumflex -10 KPX ncaron udieresis -10 KPX ncaron ugrave -10 KPX ncaron uhungarumlaut -10 KPX ncaron umacron -10 KPX ncaron uogonek -10 KPX ncaron uring -10 KPX ncaron v -40 KPX ncaron y -20 KPX ncaron yacute -20 KPX ncaron ydieresis -20 KPX ncommaaccent u -10 KPX ncommaaccent uacute -10 KPX ncommaaccent ucircumflex -10 KPX ncommaaccent udieresis -10 KPX ncommaaccent ugrave -10 KPX ncommaaccent uhungarumlaut -10 KPX ncommaaccent umacron -10 KPX ncommaaccent uogonek -10 KPX ncommaaccent uring -10 KPX ncommaaccent v -40 KPX ncommaaccent y -20 KPX ncommaaccent yacute -20 KPX ncommaaccent ydieresis -20 KPX ntilde u -10 KPX ntilde uacute -10 KPX ntilde ucircumflex -10 KPX ntilde udieresis -10 KPX ntilde ugrave -10 KPX ntilde uhungarumlaut -10 KPX ntilde umacron -10 KPX ntilde uogonek -10 KPX ntilde uring -10 KPX ntilde v -40 KPX ntilde y -20 KPX ntilde yacute -20 KPX ntilde ydieresis -20 KPX o v -20 KPX o w -15 KPX o x -30 KPX o y -20 KPX o yacute -20 KPX o ydieresis -20 KPX oacute v -20 KPX oacute w -15 KPX oacute x -30 KPX oacute y -20 KPX oacute yacute -20 KPX oacute ydieresis -20 KPX ocircumflex v -20 KPX ocircumflex w -15 KPX ocircumflex x -30 KPX ocircumflex y -20 KPX ocircumflex yacute -20 KPX ocircumflex ydieresis -20 KPX odieresis v -20 KPX odieresis w -15 KPX odieresis x -30 KPX odieresis y -20 KPX odieresis yacute -20 KPX odieresis ydieresis -20 KPX ograve v -20 KPX ograve w -15 KPX ograve x -30 KPX ograve y -20 KPX ograve yacute -20 KPX ograve ydieresis -20 KPX ohungarumlaut v -20 KPX ohungarumlaut w -15 KPX ohungarumlaut x -30 KPX ohungarumlaut y -20 KPX ohungarumlaut yacute -20 KPX ohungarumlaut ydieresis -20 KPX omacron v -20 KPX omacron w -15 KPX omacron x -30 KPX omacron y -20 KPX omacron yacute -20 KPX omacron ydieresis -20 KPX oslash v -20 KPX oslash w -15 KPX oslash x -30 KPX oslash y -20 KPX oslash yacute -20 KPX oslash ydieresis -20 KPX otilde v -20 KPX otilde w -15 KPX otilde x -30 KPX otilde y -20 KPX otilde yacute -20 KPX otilde ydieresis -20 KPX p y -15 KPX p yacute -15 KPX p ydieresis -15 KPX period quotedblright -120 KPX period quoteright -120 KPX period space -40 KPX quotedblright space -80 KPX quoteleft quoteleft -46 KPX quoteright d -80 KPX quoteright dcroat -80 KPX quoteright l -20 KPX quoteright lacute -20 KPX quoteright lcommaaccent -20 KPX quoteright lslash -20 KPX quoteright quoteright -46 KPX quoteright r -40 KPX quoteright racute -40 KPX quoteright rcaron -40 KPX quoteright rcommaaccent -40 KPX quoteright s -60 KPX quoteright sacute -60 KPX quoteright scaron -60 KPX quoteright scedilla -60 KPX quoteright scommaaccent -60 KPX quoteright space -80 KPX quoteright v -20 KPX r c -20 KPX r cacute -20 KPX r ccaron -20 KPX r ccedilla -20 KPX r comma -60 KPX r d -20 KPX r dcroat -20 KPX r g -15 KPX r gbreve -15 KPX r gcommaaccent -15 KPX r hyphen -20 KPX r o -20 KPX r oacute -20 KPX r ocircumflex -20 KPX r odieresis -20 KPX r ograve -20 KPX r ohungarumlaut -20 KPX r omacron -20 KPX r oslash -20 KPX r otilde -20 KPX r period -60 KPX r q -20 KPX r s -15 KPX r sacute -15 KPX r scaron -15 KPX r scedilla -15 KPX r scommaaccent -15 KPX r t 20 KPX r tcommaaccent 20 KPX r v 10 KPX r y 10 KPX r yacute 10 KPX r ydieresis 10 KPX racute c -20 KPX racute cacute -20 KPX racute ccaron -20 KPX racute ccedilla -20 KPX racute comma -60 KPX racute d -20 KPX racute dcroat -20 KPX racute g -15 KPX racute gbreve -15 KPX racute gcommaaccent -15 KPX racute hyphen -20 KPX racute o -20 KPX racute oacute -20 KPX racute ocircumflex -20 KPX racute odieresis -20 KPX racute ograve -20 KPX racute ohungarumlaut -20 KPX racute omacron -20 KPX racute oslash -20 KPX racute otilde -20 KPX racute period -60 KPX racute q -20 KPX racute s -15 KPX racute sacute -15 KPX racute scaron -15 KPX racute scedilla -15 KPX racute scommaaccent -15 KPX racute t 20 KPX racute tcommaaccent 20 KPX racute v 10 KPX racute y 10 KPX racute yacute 10 KPX racute ydieresis 10 KPX rcaron c -20 KPX rcaron cacute -20 KPX rcaron ccaron -20 KPX rcaron ccedilla -20 KPX rcaron comma -60 KPX rcaron d -20 KPX rcaron dcroat -20 KPX rcaron g -15 KPX rcaron gbreve -15 KPX rcaron gcommaaccent -15 KPX rcaron hyphen -20 KPX rcaron o -20 KPX rcaron oacute -20 KPX rcaron ocircumflex -20 KPX rcaron odieresis -20 KPX rcaron ograve -20 KPX rcaron ohungarumlaut -20 KPX rcaron omacron -20 KPX rcaron oslash -20 KPX rcaron otilde -20 KPX rcaron period -60 KPX rcaron q -20 KPX rcaron s -15 KPX rcaron sacute -15 KPX rcaron scaron -15 KPX rcaron scedilla -15 KPX rcaron scommaaccent -15 KPX rcaron t 20 KPX rcaron tcommaaccent 20 KPX rcaron v 10 KPX rcaron y 10 KPX rcaron yacute 10 KPX rcaron ydieresis 10 KPX rcommaaccent c -20 KPX rcommaaccent cacute -20 KPX rcommaaccent ccaron -20 KPX rcommaaccent ccedilla -20 KPX rcommaaccent comma -60 KPX rcommaaccent d -20 KPX rcommaaccent dcroat -20 KPX rcommaaccent g -15 KPX rcommaaccent gbreve -15 KPX rcommaaccent gcommaaccent -15 KPX rcommaaccent hyphen -20 KPX rcommaaccent o -20 KPX rcommaaccent oacute -20 KPX rcommaaccent ocircumflex -20 KPX rcommaaccent odieresis -20 KPX rcommaaccent ograve -20 KPX rcommaaccent ohungarumlaut -20 KPX rcommaaccent omacron -20 KPX rcommaaccent oslash -20 KPX rcommaaccent otilde -20 KPX rcommaaccent period -60 KPX rcommaaccent q -20 KPX rcommaaccent s -15 KPX rcommaaccent sacute -15 KPX rcommaaccent scaron -15 KPX rcommaaccent scedilla -15 KPX rcommaaccent scommaaccent -15 KPX rcommaaccent t 20 KPX rcommaaccent tcommaaccent 20 KPX rcommaaccent v 10 KPX rcommaaccent y 10 KPX rcommaaccent yacute 10 KPX rcommaaccent ydieresis 10 KPX s w -15 KPX sacute w -15 KPX scaron w -15 KPX scedilla w -15 KPX scommaaccent w -15 KPX semicolon space -40 KPX space T -100 KPX space Tcaron -100 KPX space Tcommaaccent -100 KPX space V -80 KPX space W -80 KPX space Y -120 KPX space Yacute -120 KPX space Ydieresis -120 KPX space quotedblleft -80 KPX space quoteleft -60 KPX v a -20 KPX v aacute -20 KPX v abreve -20 KPX v acircumflex -20 KPX v adieresis -20 KPX v agrave -20 KPX v amacron -20 KPX v aogonek -20 KPX v aring -20 KPX v atilde -20 KPX v comma -80 KPX v o -30 KPX v oacute -30 KPX v ocircumflex -30 KPX v odieresis -30 KPX v ograve -30 KPX v ohungarumlaut -30 KPX v omacron -30 KPX v oslash -30 KPX v otilde -30 KPX v period -80 KPX w comma -40 KPX w o -20 KPX w oacute -20 KPX w ocircumflex -20 KPX w odieresis -20 KPX w ograve -20 KPX w ohungarumlaut -20 KPX w omacron -20 KPX w oslash -20 KPX w otilde -20 KPX w period -40 KPX x e -10 KPX x eacute -10 KPX x ecaron -10 KPX x ecircumflex -10 KPX x edieresis -10 KPX x edotaccent -10 KPX x egrave -10 KPX x emacron -10 KPX x eogonek -10 KPX y a -30 KPX y aacute -30 KPX y abreve -30 KPX y acircumflex -30 KPX y adieresis -30 KPX y agrave -30 KPX y amacron -30 KPX y aogonek -30 KPX y aring -30 KPX y atilde -30 KPX y comma -80 KPX y e -10 KPX y eacute -10 KPX y ecaron -10 KPX y ecircumflex -10 KPX y edieresis -10 KPX y edotaccent -10 KPX y egrave -10 KPX y emacron -10 KPX y eogonek -10 KPX y o -25 KPX y oacute -25 KPX y ocircumflex -25 KPX y odieresis -25 KPX y ograve -25 KPX y ohungarumlaut -25 KPX y omacron -25 KPX y oslash -25 KPX y otilde -25 KPX y period -80 KPX yacute a -30 KPX yacute aacute -30 KPX yacute abreve -30 KPX yacute acircumflex -30 KPX yacute adieresis -30 KPX yacute agrave -30 KPX yacute amacron -30 KPX yacute aogonek -30 KPX yacute aring -30 KPX yacute atilde -30 KPX yacute comma -80 KPX yacute e -10 KPX yacute eacute -10 KPX yacute ecaron -10 KPX yacute ecircumflex -10 KPX yacute edieresis -10 KPX yacute edotaccent -10 KPX yacute egrave -10 KPX yacute emacron -10 KPX yacute eogonek -10 KPX yacute o -25 KPX yacute oacute -25 KPX yacute ocircumflex -25 KPX yacute odieresis -25 KPX yacute ograve -25 KPX yacute ohungarumlaut -25 KPX yacute omacron -25 KPX yacute oslash -25 KPX yacute otilde -25 KPX yacute period -80 KPX ydieresis a -30 KPX ydieresis aacute -30 KPX ydieresis abreve -30 KPX ydieresis acircumflex -30 KPX ydieresis adieresis -30 KPX ydieresis agrave -30 KPX ydieresis amacron -30 KPX ydieresis aogonek -30 KPX ydieresis aring -30 KPX ydieresis atilde -30 KPX ydieresis comma -80 KPX ydieresis e -10 KPX ydieresis eacute -10 KPX ydieresis ecaron -10 KPX ydieresis ecircumflex -10 KPX ydieresis edieresis -10 KPX ydieresis edotaccent -10 KPX ydieresis egrave -10 KPX ydieresis emacron -10 KPX ydieresis eogonek -10 KPX ydieresis o -25 KPX ydieresis oacute -25 KPX ydieresis ocircumflex -25 KPX ydieresis odieresis -25 KPX ydieresis ograve -25 KPX ydieresis ohungarumlaut -25 KPX ydieresis omacron -25 KPX ydieresis oslash -25 KPX ydieresis otilde -25 KPX ydieresis period -80 KPX z e 10 KPX z eacute 10 KPX z ecaron 10 KPX z ecircumflex 10 KPX z edieresis 10 KPX z edotaccent 10 KPX z egrave 10 KPX z emacron 10 KPX z eogonek 10 KPX zacute e 10 KPX zacute eacute 10 KPX zacute ecaron 10 KPX zacute ecircumflex 10 KPX zacute edieresis 10 KPX zacute edotaccent 10 KPX zacute egrave 10 KPX zacute emacron 10 KPX zacute eogonek 10 KPX zcaron e 10 KPX zcaron eacute 10 KPX zcaron ecaron 10 KPX zcaron ecircumflex 10 KPX zcaron edieresis 10 KPX zcaron edotaccent 10 KPX zcaron egrave 10 KPX zcaron emacron 10 KPX zcaron eogonek 10 KPX zdotaccent e 10 KPX zdotaccent eacute 10 KPX zdotaccent ecaron 10 KPX zdotaccent ecircumflex 10 KPX zdotaccent edieresis 10 KPX zdotaccent edotaccent 10 KPX zdotaccent egrave 10 KPX zdotaccent emacron 10 KPX zdotaccent eogonek 10 EndKernPairs EndKernData EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Helvetica-BoldOblique.afm000066400000000000000000002150001320103431700313240ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 12:45:12 1997 Comment UniqueID 43053 Comment VMusage 14482 68586 FontName Helvetica-BoldOblique FullName Helvetica Bold Oblique FamilyName Helvetica Weight Bold ItalicAngle -12 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -174 -228 1114 962 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme AdobeStandardEncoding CapHeight 718 XHeight 532 Ascender 718 Descender -207 StdHW 118 StdVW 140 StartCharMetrics 315 C 32 ; WX 278 ; N space ; B 0 0 0 0 ; C 33 ; WX 333 ; N exclam ; B 94 0 397 718 ; C 34 ; WX 474 ; N quotedbl ; B 193 447 529 718 ; C 35 ; WX 556 ; N numbersign ; B 60 0 644 698 ; C 36 ; WX 556 ; N dollar ; B 67 -115 622 775 ; C 37 ; WX 889 ; N percent ; B 136 -19 901 710 ; C 38 ; WX 722 ; N ampersand ; B 89 -19 732 718 ; C 39 ; WX 278 ; N quoteright ; B 167 445 362 718 ; C 40 ; WX 333 ; N parenleft ; B 76 -208 470 734 ; C 41 ; WX 333 ; N parenright ; B -25 -208 369 734 ; C 42 ; WX 389 ; N asterisk ; B 146 387 481 718 ; C 43 ; WX 584 ; N plus ; B 82 0 610 506 ; C 44 ; WX 278 ; N comma ; B 28 -168 245 146 ; C 45 ; WX 333 ; N hyphen ; B 73 215 379 345 ; C 46 ; WX 278 ; N period ; B 64 0 245 146 ; C 47 ; WX 278 ; N slash ; B -37 -19 468 737 ; C 48 ; WX 556 ; N zero ; B 86 -19 617 710 ; C 49 ; WX 556 ; N one ; B 173 0 529 710 ; C 50 ; WX 556 ; N two ; B 26 0 619 710 ; C 51 ; WX 556 ; N three ; B 65 -19 608 710 ; C 52 ; WX 556 ; N four ; B 60 0 598 710 ; C 53 ; WX 556 ; N five ; B 64 -19 636 698 ; C 54 ; WX 556 ; N six ; B 85 -19 619 710 ; C 55 ; WX 556 ; N seven ; B 125 0 676 698 ; C 56 ; WX 556 ; N eight ; B 69 -19 616 710 ; C 57 ; WX 556 ; N nine ; B 78 -19 615 710 ; C 58 ; WX 333 ; N colon ; B 92 0 351 512 ; C 59 ; WX 333 ; N semicolon ; B 56 -168 351 512 ; C 60 ; WX 584 ; N less ; B 82 -8 655 514 ; C 61 ; WX 584 ; N equal ; B 58 87 633 419 ; C 62 ; WX 584 ; N greater ; B 36 -8 609 514 ; C 63 ; WX 611 ; N question ; B 165 0 671 727 ; C 64 ; WX 975 ; N at ; B 186 -19 954 737 ; C 65 ; WX 722 ; N A ; B 20 0 702 718 ; C 66 ; WX 722 ; N B ; B 76 0 764 718 ; C 67 ; WX 722 ; N C ; B 107 -19 789 737 ; C 68 ; WX 722 ; N D ; B 76 0 777 718 ; C 69 ; WX 667 ; N E ; B 76 0 757 718 ; C 70 ; WX 611 ; N F ; B 76 0 740 718 ; C 71 ; WX 778 ; N G ; B 108 -19 817 737 ; C 72 ; WX 722 ; N H ; B 71 0 804 718 ; C 73 ; WX 278 ; N I ; B 64 0 367 718 ; C 74 ; WX 556 ; N J ; B 60 -18 637 718 ; C 75 ; WX 722 ; N K ; B 87 0 858 718 ; C 76 ; WX 611 ; N L ; B 76 0 611 718 ; C 77 ; WX 833 ; N M ; B 69 0 918 718 ; C 78 ; WX 722 ; N N ; B 69 0 807 718 ; C 79 ; WX 778 ; N O ; B 107 -19 823 737 ; C 80 ; WX 667 ; N P ; B 76 0 738 718 ; C 81 ; WX 778 ; N Q ; B 107 -52 823 737 ; C 82 ; WX 722 ; N R ; B 76 0 778 718 ; C 83 ; WX 667 ; N S ; B 81 -19 718 737 ; C 84 ; WX 611 ; N T ; B 140 0 751 718 ; C 85 ; WX 722 ; N U ; B 116 -19 804 718 ; C 86 ; WX 667 ; N V ; B 172 0 801 718 ; C 87 ; WX 944 ; N W ; B 169 0 1082 718 ; C 88 ; WX 667 ; N X ; B 14 0 791 718 ; C 89 ; WX 667 ; N Y ; B 168 0 806 718 ; C 90 ; WX 611 ; N Z ; B 25 0 737 718 ; C 91 ; WX 333 ; N bracketleft ; B 21 -196 462 722 ; C 92 ; WX 278 ; N backslash ; B 124 -19 307 737 ; C 93 ; WX 333 ; N bracketright ; B -18 -196 423 722 ; C 94 ; WX 584 ; N asciicircum ; B 131 323 591 698 ; C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ; C 96 ; WX 278 ; N quoteleft ; B 165 454 361 727 ; C 97 ; WX 556 ; N a ; B 55 -14 583 546 ; C 98 ; WX 611 ; N b ; B 61 -14 645 718 ; C 99 ; WX 556 ; N c ; B 79 -14 599 546 ; C 100 ; WX 611 ; N d ; B 82 -14 704 718 ; C 101 ; WX 556 ; N e ; B 70 -14 593 546 ; C 102 ; WX 333 ; N f ; B 87 0 469 727 ; L i fi ; L l fl ; C 103 ; WX 611 ; N g ; B 38 -217 666 546 ; C 104 ; WX 611 ; N h ; B 65 0 629 718 ; C 105 ; WX 278 ; N i ; B 69 0 363 725 ; C 106 ; WX 278 ; N j ; B -42 -214 363 725 ; C 107 ; WX 556 ; N k ; B 69 0 670 718 ; C 108 ; WX 278 ; N l ; B 69 0 362 718 ; C 109 ; WX 889 ; N m ; B 64 0 909 546 ; C 110 ; WX 611 ; N n ; B 65 0 629 546 ; C 111 ; WX 611 ; N o ; B 82 -14 643 546 ; C 112 ; WX 611 ; N p ; B 18 -207 645 546 ; C 113 ; WX 611 ; N q ; B 80 -207 665 546 ; C 114 ; WX 389 ; N r ; B 64 0 489 546 ; C 115 ; WX 556 ; N s ; B 63 -14 584 546 ; C 116 ; WX 333 ; N t ; B 100 -6 422 676 ; C 117 ; WX 611 ; N u ; B 98 -14 658 532 ; C 118 ; WX 556 ; N v ; B 126 0 656 532 ; C 119 ; WX 778 ; N w ; B 123 0 882 532 ; C 120 ; WX 556 ; N x ; B 15 0 648 532 ; C 121 ; WX 556 ; N y ; B 42 -214 652 532 ; C 122 ; WX 500 ; N z ; B 20 0 583 532 ; C 123 ; WX 389 ; N braceleft ; B 94 -196 518 722 ; C 124 ; WX 280 ; N bar ; B 36 -225 361 775 ; C 125 ; WX 389 ; N braceright ; B -18 -196 407 722 ; C 126 ; WX 584 ; N asciitilde ; B 115 163 577 343 ; C 161 ; WX 333 ; N exclamdown ; B 50 -186 353 532 ; C 162 ; WX 556 ; N cent ; B 79 -118 599 628 ; C 163 ; WX 556 ; N sterling ; B 50 -16 635 718 ; C 164 ; WX 167 ; N fraction ; B -174 -19 487 710 ; C 165 ; WX 556 ; N yen ; B 60 0 713 698 ; C 166 ; WX 556 ; N florin ; B -50 -210 669 737 ; C 167 ; WX 556 ; N section ; B 61 -184 598 727 ; C 168 ; WX 556 ; N currency ; B 27 76 680 636 ; C 169 ; WX 238 ; N quotesingle ; B 165 447 321 718 ; C 170 ; WX 500 ; N quotedblleft ; B 160 454 588 727 ; C 171 ; WX 556 ; N guillemotleft ; B 135 76 571 484 ; C 172 ; WX 333 ; N guilsinglleft ; B 130 76 353 484 ; C 173 ; WX 333 ; N guilsinglright ; B 99 76 322 484 ; C 174 ; WX 611 ; N fi ; B 87 0 696 727 ; C 175 ; WX 611 ; N fl ; B 87 0 695 727 ; C 177 ; WX 556 ; N endash ; B 48 227 627 333 ; C 178 ; WX 556 ; N dagger ; B 118 -171 626 718 ; C 179 ; WX 556 ; N daggerdbl ; B 46 -171 628 718 ; C 180 ; WX 278 ; N periodcentered ; B 110 172 276 334 ; C 182 ; WX 556 ; N paragraph ; B 98 -191 688 700 ; C 183 ; WX 350 ; N bullet ; B 83 194 420 524 ; C 184 ; WX 278 ; N quotesinglbase ; B 41 -146 236 127 ; C 185 ; WX 500 ; N quotedblbase ; B 36 -146 463 127 ; C 186 ; WX 500 ; N quotedblright ; B 162 445 589 718 ; C 187 ; WX 556 ; N guillemotright ; B 104 76 540 484 ; C 188 ; WX 1000 ; N ellipsis ; B 92 0 939 146 ; C 189 ; WX 1000 ; N perthousand ; B 76 -19 1038 710 ; C 191 ; WX 611 ; N questiondown ; B 53 -195 559 532 ; C 193 ; WX 333 ; N grave ; B 136 604 353 750 ; C 194 ; WX 333 ; N acute ; B 236 604 515 750 ; C 195 ; WX 333 ; N circumflex ; B 118 604 471 750 ; C 196 ; WX 333 ; N tilde ; B 113 610 507 737 ; C 197 ; WX 333 ; N macron ; B 122 604 483 678 ; C 198 ; WX 333 ; N breve ; B 156 604 494 750 ; C 199 ; WX 333 ; N dotaccent ; B 235 614 385 729 ; C 200 ; WX 333 ; N dieresis ; B 137 614 482 729 ; C 202 ; WX 333 ; N ring ; B 200 568 420 776 ; C 203 ; WX 333 ; N cedilla ; B -37 -228 220 0 ; C 205 ; WX 333 ; N hungarumlaut ; B 137 604 645 750 ; C 206 ; WX 333 ; N ogonek ; B 41 -228 264 0 ; C 207 ; WX 333 ; N caron ; B 149 604 502 750 ; C 208 ; WX 1000 ; N emdash ; B 48 227 1071 333 ; C 225 ; WX 1000 ; N AE ; B 5 0 1100 718 ; C 227 ; WX 370 ; N ordfeminine ; B 125 401 465 737 ; C 232 ; WX 611 ; N Lslash ; B 34 0 611 718 ; C 233 ; WX 778 ; N Oslash ; B 35 -27 894 745 ; C 234 ; WX 1000 ; N OE ; B 99 -19 1114 737 ; C 235 ; WX 365 ; N ordmasculine ; B 123 401 485 737 ; C 241 ; WX 889 ; N ae ; B 56 -14 923 546 ; C 245 ; WX 278 ; N dotlessi ; B 69 0 322 532 ; C 248 ; WX 278 ; N lslash ; B 40 0 407 718 ; C 249 ; WX 611 ; N oslash ; B 22 -29 701 560 ; C 250 ; WX 944 ; N oe ; B 82 -14 977 546 ; C 251 ; WX 611 ; N germandbls ; B 69 -14 657 731 ; C -1 ; WX 278 ; N Idieresis ; B 64 0 494 915 ; C -1 ; WX 556 ; N eacute ; B 70 -14 627 750 ; C -1 ; WX 556 ; N abreve ; B 55 -14 606 750 ; C -1 ; WX 611 ; N uhungarumlaut ; B 98 -14 784 750 ; C -1 ; WX 556 ; N ecaron ; B 70 -14 614 750 ; C -1 ; WX 667 ; N Ydieresis ; B 168 0 806 915 ; C -1 ; WX 584 ; N divide ; B 82 -42 610 548 ; C -1 ; WX 667 ; N Yacute ; B 168 0 806 936 ; C -1 ; WX 722 ; N Acircumflex ; B 20 0 706 936 ; C -1 ; WX 556 ; N aacute ; B 55 -14 627 750 ; C -1 ; WX 722 ; N Ucircumflex ; B 116 -19 804 936 ; C -1 ; WX 556 ; N yacute ; B 42 -214 652 750 ; C -1 ; WX 556 ; N scommaaccent ; B 63 -228 584 546 ; C -1 ; WX 556 ; N ecircumflex ; B 70 -14 593 750 ; C -1 ; WX 722 ; N Uring ; B 116 -19 804 962 ; C -1 ; WX 722 ; N Udieresis ; B 116 -19 804 915 ; C -1 ; WX 556 ; N aogonek ; B 55 -224 583 546 ; C -1 ; WX 722 ; N Uacute ; B 116 -19 804 936 ; C -1 ; WX 611 ; N uogonek ; B 98 -228 658 532 ; C -1 ; WX 667 ; N Edieresis ; B 76 0 757 915 ; C -1 ; WX 722 ; N Dcroat ; B 62 0 777 718 ; C -1 ; WX 250 ; N commaaccent ; B 16 -228 188 -50 ; C -1 ; WX 737 ; N copyright ; B 56 -19 835 737 ; C -1 ; WX 667 ; N Emacron ; B 76 0 757 864 ; C -1 ; WX 556 ; N ccaron ; B 79 -14 614 750 ; C -1 ; WX 556 ; N aring ; B 55 -14 583 776 ; C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 807 718 ; C -1 ; WX 278 ; N lacute ; B 69 0 528 936 ; C -1 ; WX 556 ; N agrave ; B 55 -14 583 750 ; C -1 ; WX 611 ; N Tcommaaccent ; B 140 -228 751 718 ; C -1 ; WX 722 ; N Cacute ; B 107 -19 789 936 ; C -1 ; WX 556 ; N atilde ; B 55 -14 619 737 ; C -1 ; WX 667 ; N Edotaccent ; B 76 0 757 915 ; C -1 ; WX 556 ; N scaron ; B 63 -14 614 750 ; C -1 ; WX 556 ; N scedilla ; B 63 -228 584 546 ; C -1 ; WX 278 ; N iacute ; B 69 0 488 750 ; C -1 ; WX 494 ; N lozenge ; B 90 0 564 745 ; C -1 ; WX 722 ; N Rcaron ; B 76 0 778 936 ; C -1 ; WX 778 ; N Gcommaaccent ; B 108 -228 817 737 ; C -1 ; WX 611 ; N ucircumflex ; B 98 -14 658 750 ; C -1 ; WX 556 ; N acircumflex ; B 55 -14 583 750 ; C -1 ; WX 722 ; N Amacron ; B 20 0 718 864 ; C -1 ; WX 389 ; N rcaron ; B 64 0 530 750 ; C -1 ; WX 556 ; N ccedilla ; B 79 -228 599 546 ; C -1 ; WX 611 ; N Zdotaccent ; B 25 0 737 915 ; C -1 ; WX 667 ; N Thorn ; B 76 0 716 718 ; C -1 ; WX 778 ; N Omacron ; B 107 -19 823 864 ; C -1 ; WX 722 ; N Racute ; B 76 0 778 936 ; C -1 ; WX 667 ; N Sacute ; B 81 -19 722 936 ; C -1 ; WX 743 ; N dcaron ; B 82 -14 903 718 ; C -1 ; WX 722 ; N Umacron ; B 116 -19 804 864 ; C -1 ; WX 611 ; N uring ; B 98 -14 658 776 ; C -1 ; WX 333 ; N threesuperior ; B 91 271 441 710 ; C -1 ; WX 778 ; N Ograve ; B 107 -19 823 936 ; C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ; C -1 ; WX 722 ; N Abreve ; B 20 0 729 936 ; C -1 ; WX 584 ; N multiply ; B 57 1 635 505 ; C -1 ; WX 611 ; N uacute ; B 98 -14 658 750 ; C -1 ; WX 611 ; N Tcaron ; B 140 0 751 936 ; C -1 ; WX 494 ; N partialdiff ; B 43 -21 585 750 ; C -1 ; WX 556 ; N ydieresis ; B 42 -214 652 729 ; C -1 ; WX 722 ; N Nacute ; B 69 0 807 936 ; C -1 ; WX 278 ; N icircumflex ; B 69 0 444 750 ; C -1 ; WX 667 ; N Ecircumflex ; B 76 0 757 936 ; C -1 ; WX 556 ; N adieresis ; B 55 -14 594 729 ; C -1 ; WX 556 ; N edieresis ; B 70 -14 594 729 ; C -1 ; WX 556 ; N cacute ; B 79 -14 627 750 ; C -1 ; WX 611 ; N nacute ; B 65 0 654 750 ; C -1 ; WX 611 ; N umacron ; B 98 -14 658 678 ; C -1 ; WX 722 ; N Ncaron ; B 69 0 807 936 ; C -1 ; WX 278 ; N Iacute ; B 64 0 528 936 ; C -1 ; WX 584 ; N plusminus ; B 40 0 625 506 ; C -1 ; WX 280 ; N brokenbar ; B 52 -150 345 700 ; C -1 ; WX 737 ; N registered ; B 55 -19 834 737 ; C -1 ; WX 778 ; N Gbreve ; B 108 -19 817 936 ; C -1 ; WX 278 ; N Idotaccent ; B 64 0 397 915 ; C -1 ; WX 600 ; N summation ; B 14 -10 670 706 ; C -1 ; WX 667 ; N Egrave ; B 76 0 757 936 ; C -1 ; WX 389 ; N racute ; B 64 0 543 750 ; C -1 ; WX 611 ; N omacron ; B 82 -14 643 678 ; C -1 ; WX 611 ; N Zacute ; B 25 0 737 936 ; C -1 ; WX 611 ; N Zcaron ; B 25 0 737 936 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 629 704 ; C -1 ; WX 722 ; N Eth ; B 62 0 777 718 ; C -1 ; WX 722 ; N Ccedilla ; B 107 -228 789 737 ; C -1 ; WX 278 ; N lcommaaccent ; B 30 -228 362 718 ; C -1 ; WX 389 ; N tcaron ; B 100 -6 608 878 ; C -1 ; WX 556 ; N eogonek ; B 70 -228 593 546 ; C -1 ; WX 722 ; N Uogonek ; B 116 -228 804 718 ; C -1 ; WX 722 ; N Aacute ; B 20 0 750 936 ; C -1 ; WX 722 ; N Adieresis ; B 20 0 716 915 ; C -1 ; WX 556 ; N egrave ; B 70 -14 593 750 ; C -1 ; WX 500 ; N zacute ; B 20 0 599 750 ; C -1 ; WX 278 ; N iogonek ; B -14 -224 363 725 ; C -1 ; WX 778 ; N Oacute ; B 107 -19 823 936 ; C -1 ; WX 611 ; N oacute ; B 82 -14 654 750 ; C -1 ; WX 556 ; N amacron ; B 55 -14 595 678 ; C -1 ; WX 556 ; N sacute ; B 63 -14 627 750 ; C -1 ; WX 278 ; N idieresis ; B 69 0 455 729 ; C -1 ; WX 778 ; N Ocircumflex ; B 107 -19 823 936 ; C -1 ; WX 722 ; N Ugrave ; B 116 -19 804 936 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C -1 ; WX 611 ; N thorn ; B 18 -208 645 718 ; C -1 ; WX 333 ; N twosuperior ; B 69 283 449 710 ; C -1 ; WX 778 ; N Odieresis ; B 107 -19 823 915 ; C -1 ; WX 611 ; N mu ; B 22 -207 658 532 ; C -1 ; WX 278 ; N igrave ; B 69 0 326 750 ; C -1 ; WX 611 ; N ohungarumlaut ; B 82 -14 784 750 ; C -1 ; WX 667 ; N Eogonek ; B 76 -224 757 718 ; C -1 ; WX 611 ; N dcroat ; B 82 -14 789 718 ; C -1 ; WX 834 ; N threequarters ; B 99 -19 839 710 ; C -1 ; WX 667 ; N Scedilla ; B 81 -228 718 737 ; C -1 ; WX 400 ; N lcaron ; B 69 0 561 718 ; C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 858 718 ; C -1 ; WX 611 ; N Lacute ; B 76 0 611 936 ; C -1 ; WX 1000 ; N trademark ; B 179 306 1109 718 ; C -1 ; WX 556 ; N edotaccent ; B 70 -14 593 729 ; C -1 ; WX 278 ; N Igrave ; B 64 0 367 936 ; C -1 ; WX 278 ; N Imacron ; B 64 0 496 864 ; C -1 ; WX 611 ; N Lcaron ; B 76 0 643 718 ; C -1 ; WX 834 ; N onehalf ; B 132 -19 858 710 ; C -1 ; WX 549 ; N lessequal ; B 29 0 676 704 ; C -1 ; WX 611 ; N ocircumflex ; B 82 -14 643 750 ; C -1 ; WX 611 ; N ntilde ; B 65 0 646 737 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 116 -19 880 936 ; C -1 ; WX 667 ; N Eacute ; B 76 0 757 936 ; C -1 ; WX 556 ; N emacron ; B 70 -14 595 678 ; C -1 ; WX 611 ; N gbreve ; B 38 -217 666 750 ; C -1 ; WX 834 ; N onequarter ; B 132 -19 806 710 ; C -1 ; WX 667 ; N Scaron ; B 81 -19 718 936 ; C -1 ; WX 667 ; N Scommaaccent ; B 81 -228 718 737 ; C -1 ; WX 778 ; N Ohungarumlaut ; B 107 -19 908 936 ; C -1 ; WX 400 ; N degree ; B 175 426 467 712 ; C -1 ; WX 611 ; N ograve ; B 82 -14 643 750 ; C -1 ; WX 722 ; N Ccaron ; B 107 -19 789 936 ; C -1 ; WX 611 ; N ugrave ; B 98 -14 658 750 ; C -1 ; WX 549 ; N radical ; B 112 -46 689 850 ; C -1 ; WX 722 ; N Dcaron ; B 76 0 777 936 ; C -1 ; WX 389 ; N rcommaaccent ; B 26 -228 489 546 ; C -1 ; WX 722 ; N Ntilde ; B 69 0 807 923 ; C -1 ; WX 611 ; N otilde ; B 82 -14 646 737 ; C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 778 718 ; C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 611 718 ; C -1 ; WX 722 ; N Atilde ; B 20 0 741 923 ; C -1 ; WX 722 ; N Aogonek ; B 20 -224 702 718 ; C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ; C -1 ; WX 778 ; N Otilde ; B 107 -19 823 923 ; C -1 ; WX 500 ; N zdotaccent ; B 20 0 583 729 ; C -1 ; WX 667 ; N Ecaron ; B 76 0 757 936 ; C -1 ; WX 278 ; N Iogonek ; B -41 -228 367 718 ; C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 670 718 ; C -1 ; WX 584 ; N minus ; B 82 197 610 309 ; C -1 ; WX 278 ; N Icircumflex ; B 64 0 484 936 ; C -1 ; WX 611 ; N ncaron ; B 65 0 641 750 ; C -1 ; WX 333 ; N tcommaaccent ; B 58 -228 422 676 ; C -1 ; WX 584 ; N logicalnot ; B 105 108 633 419 ; C -1 ; WX 611 ; N odieresis ; B 82 -14 643 729 ; C -1 ; WX 611 ; N udieresis ; B 98 -14 658 729 ; C -1 ; WX 549 ; N notequal ; B 32 -49 630 570 ; C -1 ; WX 611 ; N gcommaaccent ; B 38 -217 666 850 ; C -1 ; WX 611 ; N eth ; B 82 -14 670 737 ; C -1 ; WX 500 ; N zcaron ; B 20 0 586 750 ; C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 629 546 ; C -1 ; WX 333 ; N onesuperior ; B 148 283 388 710 ; C -1 ; WX 278 ; N imacron ; B 69 0 429 678 ; C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2481 KPX A C -40 KPX A Cacute -40 KPX A Ccaron -40 KPX A Ccedilla -40 KPX A G -50 KPX A Gbreve -50 KPX A Gcommaaccent -50 KPX A O -40 KPX A Oacute -40 KPX A Ocircumflex -40 KPX A Odieresis -40 KPX A Ograve -40 KPX A Ohungarumlaut -40 KPX A Omacron -40 KPX A Oslash -40 KPX A Otilde -40 KPX A Q -40 KPX A T -90 KPX A Tcaron -90 KPX A Tcommaaccent -90 KPX A U -50 KPX A Uacute -50 KPX A Ucircumflex -50 KPX A Udieresis -50 KPX A Ugrave -50 KPX A Uhungarumlaut -50 KPX A Umacron -50 KPX A Uogonek -50 KPX A Uring -50 KPX A V -80 KPX A W -60 KPX A Y -110 KPX A Yacute -110 KPX A Ydieresis -110 KPX A u -30 KPX A uacute -30 KPX A ucircumflex -30 KPX A udieresis -30 KPX A ugrave -30 KPX A uhungarumlaut -30 KPX A umacron -30 KPX A uogonek -30 KPX A uring -30 KPX A v -40 KPX A w -30 KPX A y -30 KPX A yacute -30 KPX A ydieresis -30 KPX Aacute C -40 KPX Aacute Cacute -40 KPX Aacute Ccaron -40 KPX Aacute Ccedilla -40 KPX Aacute G -50 KPX Aacute Gbreve -50 KPX Aacute Gcommaaccent -50 KPX Aacute O -40 KPX Aacute Oacute -40 KPX Aacute Ocircumflex -40 KPX Aacute Odieresis -40 KPX Aacute Ograve -40 KPX Aacute Ohungarumlaut -40 KPX Aacute Omacron -40 KPX Aacute Oslash -40 KPX Aacute Otilde -40 KPX Aacute Q -40 KPX Aacute T -90 KPX Aacute Tcaron -90 KPX Aacute Tcommaaccent -90 KPX Aacute U -50 KPX Aacute Uacute -50 KPX Aacute Ucircumflex -50 KPX Aacute Udieresis -50 KPX Aacute Ugrave -50 KPX Aacute Uhungarumlaut -50 KPX Aacute Umacron -50 KPX Aacute Uogonek -50 KPX Aacute Uring -50 KPX Aacute V -80 KPX Aacute W -60 KPX Aacute Y -110 KPX Aacute Yacute -110 KPX Aacute Ydieresis -110 KPX Aacute u -30 KPX Aacute uacute -30 KPX Aacute ucircumflex -30 KPX Aacute udieresis -30 KPX Aacute ugrave -30 KPX Aacute uhungarumlaut -30 KPX Aacute umacron -30 KPX Aacute uogonek -30 KPX Aacute uring -30 KPX Aacute v -40 KPX Aacute w -30 KPX Aacute y -30 KPX Aacute yacute -30 KPX Aacute ydieresis -30 KPX Abreve C -40 KPX Abreve Cacute -40 KPX Abreve Ccaron -40 KPX Abreve Ccedilla -40 KPX Abreve G -50 KPX Abreve Gbreve -50 KPX Abreve Gcommaaccent -50 KPX Abreve O -40 KPX Abreve Oacute -40 KPX Abreve Ocircumflex -40 KPX Abreve Odieresis -40 KPX Abreve Ograve -40 KPX Abreve Ohungarumlaut -40 KPX Abreve Omacron -40 KPX Abreve Oslash -40 KPX Abreve Otilde -40 KPX Abreve Q -40 KPX Abreve T -90 KPX Abreve Tcaron -90 KPX Abreve Tcommaaccent -90 KPX Abreve U -50 KPX Abreve Uacute -50 KPX Abreve Ucircumflex -50 KPX Abreve Udieresis -50 KPX Abreve Ugrave -50 KPX Abreve Uhungarumlaut -50 KPX Abreve Umacron -50 KPX Abreve Uogonek -50 KPX Abreve Uring -50 KPX Abreve V -80 KPX Abreve W -60 KPX Abreve Y -110 KPX Abreve Yacute -110 KPX Abreve Ydieresis -110 KPX Abreve u -30 KPX Abreve uacute -30 KPX Abreve ucircumflex -30 KPX Abreve udieresis -30 KPX Abreve ugrave -30 KPX Abreve uhungarumlaut -30 KPX Abreve umacron -30 KPX Abreve uogonek -30 KPX Abreve uring -30 KPX Abreve v -40 KPX Abreve w -30 KPX Abreve y -30 KPX Abreve yacute -30 KPX Abreve ydieresis -30 KPX Acircumflex C -40 KPX Acircumflex Cacute -40 KPX Acircumflex Ccaron -40 KPX Acircumflex Ccedilla -40 KPX Acircumflex G -50 KPX Acircumflex Gbreve -50 KPX Acircumflex Gcommaaccent -50 KPX Acircumflex O -40 KPX Acircumflex Oacute -40 KPX Acircumflex Ocircumflex -40 KPX Acircumflex Odieresis -40 KPX Acircumflex Ograve -40 KPX Acircumflex Ohungarumlaut -40 KPX Acircumflex Omacron -40 KPX Acircumflex Oslash -40 KPX Acircumflex Otilde -40 KPX Acircumflex Q -40 KPX Acircumflex T -90 KPX Acircumflex Tcaron -90 KPX Acircumflex Tcommaaccent -90 KPX Acircumflex U -50 KPX Acircumflex Uacute -50 KPX Acircumflex Ucircumflex -50 KPX Acircumflex Udieresis -50 KPX Acircumflex Ugrave -50 KPX Acircumflex Uhungarumlaut -50 KPX Acircumflex Umacron -50 KPX Acircumflex Uogonek -50 KPX Acircumflex Uring -50 KPX Acircumflex V -80 KPX Acircumflex W -60 KPX Acircumflex Y -110 KPX Acircumflex Yacute -110 KPX Acircumflex Ydieresis -110 KPX Acircumflex u -30 KPX Acircumflex uacute -30 KPX Acircumflex ucircumflex -30 KPX Acircumflex udieresis -30 KPX Acircumflex ugrave -30 KPX Acircumflex uhungarumlaut -30 KPX Acircumflex umacron -30 KPX Acircumflex uogonek -30 KPX Acircumflex uring -30 KPX Acircumflex v -40 KPX Acircumflex w -30 KPX Acircumflex y -30 KPX Acircumflex yacute -30 KPX Acircumflex ydieresis -30 KPX Adieresis C -40 KPX Adieresis Cacute -40 KPX Adieresis Ccaron -40 KPX Adieresis Ccedilla -40 KPX Adieresis G -50 KPX Adieresis Gbreve -50 KPX Adieresis Gcommaaccent -50 KPX Adieresis O -40 KPX Adieresis Oacute -40 KPX Adieresis Ocircumflex -40 KPX Adieresis Odieresis -40 KPX Adieresis Ograve -40 KPX Adieresis Ohungarumlaut -40 KPX Adieresis Omacron -40 KPX Adieresis Oslash -40 KPX Adieresis Otilde -40 KPX Adieresis Q -40 KPX Adieresis T -90 KPX Adieresis Tcaron -90 KPX Adieresis Tcommaaccent -90 KPX Adieresis U -50 KPX Adieresis Uacute -50 KPX Adieresis Ucircumflex -50 KPX Adieresis Udieresis -50 KPX Adieresis Ugrave -50 KPX Adieresis Uhungarumlaut -50 KPX Adieresis Umacron -50 KPX Adieresis Uogonek -50 KPX Adieresis Uring -50 KPX Adieresis V -80 KPX Adieresis W -60 KPX Adieresis Y -110 KPX Adieresis Yacute -110 KPX Adieresis Ydieresis -110 KPX Adieresis u -30 KPX Adieresis uacute -30 KPX Adieresis ucircumflex -30 KPX Adieresis udieresis -30 KPX Adieresis ugrave -30 KPX Adieresis uhungarumlaut -30 KPX Adieresis umacron -30 KPX Adieresis uogonek -30 KPX Adieresis uring -30 KPX Adieresis v -40 KPX Adieresis w -30 KPX Adieresis y -30 KPX Adieresis yacute -30 KPX Adieresis ydieresis -30 KPX Agrave C -40 KPX Agrave Cacute -40 KPX Agrave Ccaron -40 KPX Agrave Ccedilla -40 KPX Agrave G -50 KPX Agrave Gbreve -50 KPX Agrave Gcommaaccent -50 KPX Agrave O -40 KPX Agrave Oacute -40 KPX Agrave Ocircumflex -40 KPX Agrave Odieresis -40 KPX Agrave Ograve -40 KPX Agrave Ohungarumlaut -40 KPX Agrave Omacron -40 KPX Agrave Oslash -40 KPX Agrave Otilde -40 KPX Agrave Q -40 KPX Agrave T -90 KPX Agrave Tcaron -90 KPX Agrave Tcommaaccent -90 KPX Agrave U -50 KPX Agrave Uacute -50 KPX Agrave Ucircumflex -50 KPX Agrave Udieresis -50 KPX Agrave Ugrave -50 KPX Agrave Uhungarumlaut -50 KPX Agrave Umacron -50 KPX Agrave Uogonek -50 KPX Agrave Uring -50 KPX Agrave V -80 KPX Agrave W -60 KPX Agrave Y -110 KPX Agrave Yacute -110 KPX Agrave Ydieresis -110 KPX Agrave u -30 KPX Agrave uacute -30 KPX Agrave ucircumflex -30 KPX Agrave udieresis -30 KPX Agrave ugrave -30 KPX Agrave uhungarumlaut -30 KPX Agrave umacron -30 KPX Agrave uogonek -30 KPX Agrave uring -30 KPX Agrave v -40 KPX Agrave w -30 KPX Agrave y -30 KPX Agrave yacute -30 KPX Agrave ydieresis -30 KPX Amacron C -40 KPX Amacron Cacute -40 KPX Amacron Ccaron -40 KPX Amacron Ccedilla -40 KPX Amacron G -50 KPX Amacron Gbreve -50 KPX Amacron Gcommaaccent -50 KPX Amacron O -40 KPX Amacron Oacute -40 KPX Amacron Ocircumflex -40 KPX Amacron Odieresis -40 KPX Amacron Ograve -40 KPX Amacron Ohungarumlaut -40 KPX Amacron Omacron -40 KPX Amacron Oslash -40 KPX Amacron Otilde -40 KPX Amacron Q -40 KPX Amacron T -90 KPX Amacron Tcaron -90 KPX Amacron Tcommaaccent -90 KPX Amacron U -50 KPX Amacron Uacute -50 KPX Amacron Ucircumflex -50 KPX Amacron Udieresis -50 KPX Amacron Ugrave -50 KPX Amacron Uhungarumlaut -50 KPX Amacron Umacron -50 KPX Amacron Uogonek -50 KPX Amacron Uring -50 KPX Amacron V -80 KPX Amacron W -60 KPX Amacron Y -110 KPX Amacron Yacute -110 KPX Amacron Ydieresis -110 KPX Amacron u -30 KPX Amacron uacute -30 KPX Amacron ucircumflex -30 KPX Amacron udieresis -30 KPX Amacron ugrave -30 KPX Amacron uhungarumlaut -30 KPX Amacron umacron -30 KPX Amacron uogonek -30 KPX Amacron uring -30 KPX Amacron v -40 KPX Amacron w -30 KPX Amacron y -30 KPX Amacron yacute -30 KPX Amacron ydieresis -30 KPX Aogonek C -40 KPX Aogonek Cacute -40 KPX Aogonek Ccaron -40 KPX Aogonek Ccedilla -40 KPX Aogonek G -50 KPX Aogonek Gbreve -50 KPX Aogonek Gcommaaccent -50 KPX Aogonek O -40 KPX Aogonek Oacute -40 KPX Aogonek Ocircumflex -40 KPX Aogonek Odieresis -40 KPX Aogonek Ograve -40 KPX Aogonek Ohungarumlaut -40 KPX Aogonek Omacron -40 KPX Aogonek Oslash -40 KPX Aogonek Otilde -40 KPX Aogonek Q -40 KPX Aogonek T -90 KPX Aogonek Tcaron -90 KPX Aogonek Tcommaaccent -90 KPX Aogonek U -50 KPX Aogonek Uacute -50 KPX Aogonek Ucircumflex -50 KPX Aogonek Udieresis -50 KPX Aogonek Ugrave -50 KPX Aogonek Uhungarumlaut -50 KPX Aogonek Umacron -50 KPX Aogonek Uogonek -50 KPX Aogonek Uring -50 KPX Aogonek V -80 KPX Aogonek W -60 KPX Aogonek Y -110 KPX Aogonek Yacute -110 KPX Aogonek Ydieresis -110 KPX Aogonek u -30 KPX Aogonek uacute -30 KPX Aogonek ucircumflex -30 KPX Aogonek udieresis -30 KPX Aogonek ugrave -30 KPX Aogonek uhungarumlaut -30 KPX Aogonek umacron -30 KPX Aogonek uogonek -30 KPX Aogonek uring -30 KPX Aogonek v -40 KPX Aogonek w -30 KPX Aogonek y -30 KPX Aogonek yacute -30 KPX Aogonek ydieresis -30 KPX Aring C -40 KPX Aring Cacute -40 KPX Aring Ccaron -40 KPX Aring Ccedilla -40 KPX Aring G -50 KPX Aring Gbreve -50 KPX Aring Gcommaaccent -50 KPX Aring O -40 KPX Aring Oacute -40 KPX Aring Ocircumflex -40 KPX Aring Odieresis -40 KPX Aring Ograve -40 KPX Aring Ohungarumlaut -40 KPX Aring Omacron -40 KPX Aring Oslash -40 KPX Aring Otilde -40 KPX Aring Q -40 KPX Aring T -90 KPX Aring Tcaron -90 KPX Aring Tcommaaccent -90 KPX Aring U -50 KPX Aring Uacute -50 KPX Aring Ucircumflex -50 KPX Aring Udieresis -50 KPX Aring Ugrave -50 KPX Aring Uhungarumlaut -50 KPX Aring Umacron -50 KPX Aring Uogonek -50 KPX Aring Uring -50 KPX Aring V -80 KPX Aring W -60 KPX Aring Y -110 KPX Aring Yacute -110 KPX Aring Ydieresis -110 KPX Aring u -30 KPX Aring uacute -30 KPX Aring ucircumflex -30 KPX Aring udieresis -30 KPX Aring ugrave -30 KPX Aring uhungarumlaut -30 KPX Aring umacron -30 KPX Aring uogonek -30 KPX Aring uring -30 KPX Aring v -40 KPX Aring w -30 KPX Aring y -30 KPX Aring yacute -30 KPX Aring ydieresis -30 KPX Atilde C -40 KPX Atilde Cacute -40 KPX Atilde Ccaron -40 KPX Atilde Ccedilla -40 KPX Atilde G -50 KPX Atilde Gbreve -50 KPX Atilde Gcommaaccent -50 KPX Atilde O -40 KPX Atilde Oacute -40 KPX Atilde Ocircumflex -40 KPX Atilde Odieresis -40 KPX Atilde Ograve -40 KPX Atilde Ohungarumlaut -40 KPX Atilde Omacron -40 KPX Atilde Oslash -40 KPX Atilde Otilde -40 KPX Atilde Q -40 KPX Atilde T -90 KPX Atilde Tcaron -90 KPX Atilde Tcommaaccent -90 KPX Atilde U -50 KPX Atilde Uacute -50 KPX Atilde Ucircumflex -50 KPX Atilde Udieresis -50 KPX Atilde Ugrave -50 KPX Atilde Uhungarumlaut -50 KPX Atilde Umacron -50 KPX Atilde Uogonek -50 KPX Atilde Uring -50 KPX Atilde V -80 KPX Atilde W -60 KPX Atilde Y -110 KPX Atilde Yacute -110 KPX Atilde Ydieresis -110 KPX Atilde u -30 KPX Atilde uacute -30 KPX Atilde ucircumflex -30 KPX Atilde udieresis -30 KPX Atilde ugrave -30 KPX Atilde uhungarumlaut -30 KPX Atilde umacron -30 KPX Atilde uogonek -30 KPX Atilde uring -30 KPX Atilde v -40 KPX Atilde w -30 KPX Atilde y -30 KPX Atilde yacute -30 KPX Atilde ydieresis -30 KPX B A -30 KPX B Aacute -30 KPX B Abreve -30 KPX B Acircumflex -30 KPX B Adieresis -30 KPX B Agrave -30 KPX B Amacron -30 KPX B Aogonek -30 KPX B Aring -30 KPX B Atilde -30 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX D A -40 KPX D Aacute -40 KPX D Abreve -40 KPX D Acircumflex -40 KPX D Adieresis -40 KPX D Agrave -40 KPX D Amacron -40 KPX D Aogonek -40 KPX D Aring -40 KPX D Atilde -40 KPX D V -40 KPX D W -40 KPX D Y -70 KPX D Yacute -70 KPX D Ydieresis -70 KPX D comma -30 KPX D period -30 KPX Dcaron A -40 KPX Dcaron Aacute -40 KPX Dcaron Abreve -40 KPX Dcaron Acircumflex -40 KPX Dcaron Adieresis -40 KPX Dcaron Agrave -40 KPX Dcaron Amacron -40 KPX Dcaron Aogonek -40 KPX Dcaron Aring -40 KPX Dcaron Atilde -40 KPX Dcaron V -40 KPX Dcaron W -40 KPX Dcaron Y -70 KPX Dcaron Yacute -70 KPX Dcaron Ydieresis -70 KPX Dcaron comma -30 KPX Dcaron period -30 KPX Dcroat A -40 KPX Dcroat Aacute -40 KPX Dcroat Abreve -40 KPX Dcroat Acircumflex -40 KPX Dcroat Adieresis -40 KPX Dcroat Agrave -40 KPX Dcroat Amacron -40 KPX Dcroat Aogonek -40 KPX Dcroat Aring -40 KPX Dcroat Atilde -40 KPX Dcroat V -40 KPX Dcroat W -40 KPX Dcroat Y -70 KPX Dcroat Yacute -70 KPX Dcroat Ydieresis -70 KPX Dcroat comma -30 KPX Dcroat period -30 KPX F A -80 KPX F Aacute -80 KPX F Abreve -80 KPX F Acircumflex -80 KPX F Adieresis -80 KPX F Agrave -80 KPX F Amacron -80 KPX F Aogonek -80 KPX F Aring -80 KPX F Atilde -80 KPX F a -20 KPX F aacute -20 KPX F abreve -20 KPX F acircumflex -20 KPX F adieresis -20 KPX F agrave -20 KPX F amacron -20 KPX F aogonek -20 KPX F aring -20 KPX F atilde -20 KPX F comma -100 KPX F period -100 KPX J A -20 KPX J Aacute -20 KPX J Abreve -20 KPX J Acircumflex -20 KPX J Adieresis -20 KPX J Agrave -20 KPX J Amacron -20 KPX J Aogonek -20 KPX J Aring -20 KPX J Atilde -20 KPX J comma -20 KPX J period -20 KPX J u -20 KPX J uacute -20 KPX J ucircumflex -20 KPX J udieresis -20 KPX J ugrave -20 KPX J uhungarumlaut -20 KPX J umacron -20 KPX J uogonek -20 KPX J uring -20 KPX K O -30 KPX K Oacute -30 KPX K Ocircumflex -30 KPX K Odieresis -30 KPX K Ograve -30 KPX K Ohungarumlaut -30 KPX K Omacron -30 KPX K Oslash -30 KPX K Otilde -30 KPX K e -15 KPX K eacute -15 KPX K ecaron -15 KPX K ecircumflex -15 KPX K edieresis -15 KPX K edotaccent -15 KPX K egrave -15 KPX K emacron -15 KPX K eogonek -15 KPX K o -35 KPX K oacute -35 KPX K ocircumflex -35 KPX K odieresis -35 KPX K ograve -35 KPX K ohungarumlaut -35 KPX K omacron -35 KPX K oslash -35 KPX K otilde -35 KPX K u -30 KPX K uacute -30 KPX K ucircumflex -30 KPX K udieresis -30 KPX K ugrave -30 KPX K uhungarumlaut -30 KPX K umacron -30 KPX K uogonek -30 KPX K uring -30 KPX K y -40 KPX K yacute -40 KPX K ydieresis -40 KPX Kcommaaccent O -30 KPX Kcommaaccent Oacute -30 KPX Kcommaaccent Ocircumflex -30 KPX Kcommaaccent Odieresis -30 KPX Kcommaaccent Ograve -30 KPX Kcommaaccent Ohungarumlaut -30 KPX Kcommaaccent Omacron -30 KPX Kcommaaccent Oslash -30 KPX Kcommaaccent Otilde -30 KPX Kcommaaccent e -15 KPX Kcommaaccent eacute -15 KPX Kcommaaccent ecaron -15 KPX Kcommaaccent ecircumflex -15 KPX Kcommaaccent edieresis -15 KPX Kcommaaccent edotaccent -15 KPX Kcommaaccent egrave -15 KPX Kcommaaccent emacron -15 KPX Kcommaaccent eogonek -15 KPX Kcommaaccent o -35 KPX Kcommaaccent oacute -35 KPX Kcommaaccent ocircumflex -35 KPX Kcommaaccent odieresis -35 KPX Kcommaaccent ograve -35 KPX Kcommaaccent ohungarumlaut -35 KPX Kcommaaccent omacron -35 KPX Kcommaaccent oslash -35 KPX Kcommaaccent otilde -35 KPX Kcommaaccent u -30 KPX Kcommaaccent uacute -30 KPX Kcommaaccent ucircumflex -30 KPX Kcommaaccent udieresis -30 KPX Kcommaaccent ugrave -30 KPX Kcommaaccent uhungarumlaut -30 KPX Kcommaaccent umacron -30 KPX Kcommaaccent uogonek -30 KPX Kcommaaccent uring -30 KPX Kcommaaccent y -40 KPX Kcommaaccent yacute -40 KPX Kcommaaccent ydieresis -40 KPX L T -90 KPX L Tcaron -90 KPX L Tcommaaccent -90 KPX L V -110 KPX L W -80 KPX L Y -120 KPX L Yacute -120 KPX L Ydieresis -120 KPX L quotedblright -140 KPX L quoteright -140 KPX L y -30 KPX L yacute -30 KPX L ydieresis -30 KPX Lacute T -90 KPX Lacute Tcaron -90 KPX Lacute Tcommaaccent -90 KPX Lacute V -110 KPX Lacute W -80 KPX Lacute Y -120 KPX Lacute Yacute -120 KPX Lacute Ydieresis -120 KPX Lacute quotedblright -140 KPX Lacute quoteright -140 KPX Lacute y -30 KPX Lacute yacute -30 KPX Lacute ydieresis -30 KPX Lcommaaccent T -90 KPX Lcommaaccent Tcaron -90 KPX Lcommaaccent Tcommaaccent -90 KPX Lcommaaccent V -110 KPX Lcommaaccent W -80 KPX Lcommaaccent Y -120 KPX Lcommaaccent Yacute -120 KPX Lcommaaccent Ydieresis -120 KPX Lcommaaccent quotedblright -140 KPX Lcommaaccent quoteright -140 KPX Lcommaaccent y -30 KPX Lcommaaccent yacute -30 KPX Lcommaaccent ydieresis -30 KPX Lslash T -90 KPX Lslash Tcaron -90 KPX Lslash Tcommaaccent -90 KPX Lslash V -110 KPX Lslash W -80 KPX Lslash Y -120 KPX Lslash Yacute -120 KPX Lslash Ydieresis -120 KPX Lslash quotedblright -140 KPX Lslash quoteright -140 KPX Lslash y -30 KPX Lslash yacute -30 KPX Lslash ydieresis -30 KPX O A -50 KPX O Aacute -50 KPX O Abreve -50 KPX O Acircumflex -50 KPX O Adieresis -50 KPX O Agrave -50 KPX O Amacron -50 KPX O Aogonek -50 KPX O Aring -50 KPX O Atilde -50 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -50 KPX O X -50 KPX O Y -70 KPX O Yacute -70 KPX O Ydieresis -70 KPX O comma -40 KPX O period -40 KPX Oacute A -50 KPX Oacute Aacute -50 KPX Oacute Abreve -50 KPX Oacute Acircumflex -50 KPX Oacute Adieresis -50 KPX Oacute Agrave -50 KPX Oacute Amacron -50 KPX Oacute Aogonek -50 KPX Oacute Aring -50 KPX Oacute Atilde -50 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -50 KPX Oacute X -50 KPX Oacute Y -70 KPX Oacute Yacute -70 KPX Oacute Ydieresis -70 KPX Oacute comma -40 KPX Oacute period -40 KPX Ocircumflex A -50 KPX Ocircumflex Aacute -50 KPX Ocircumflex Abreve -50 KPX Ocircumflex Acircumflex -50 KPX Ocircumflex Adieresis -50 KPX Ocircumflex Agrave -50 KPX Ocircumflex Amacron -50 KPX Ocircumflex Aogonek -50 KPX Ocircumflex Aring -50 KPX Ocircumflex Atilde -50 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -50 KPX Ocircumflex X -50 KPX Ocircumflex Y -70 KPX Ocircumflex Yacute -70 KPX Ocircumflex Ydieresis -70 KPX Ocircumflex comma -40 KPX Ocircumflex period -40 KPX Odieresis A -50 KPX Odieresis Aacute -50 KPX Odieresis Abreve -50 KPX Odieresis Acircumflex -50 KPX Odieresis Adieresis -50 KPX Odieresis Agrave -50 KPX Odieresis Amacron -50 KPX Odieresis Aogonek -50 KPX Odieresis Aring -50 KPX Odieresis Atilde -50 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -50 KPX Odieresis X -50 KPX Odieresis Y -70 KPX Odieresis Yacute -70 KPX Odieresis Ydieresis -70 KPX Odieresis comma -40 KPX Odieresis period -40 KPX Ograve A -50 KPX Ograve Aacute -50 KPX Ograve Abreve -50 KPX Ograve Acircumflex -50 KPX Ograve Adieresis -50 KPX Ograve Agrave -50 KPX Ograve Amacron -50 KPX Ograve Aogonek -50 KPX Ograve Aring -50 KPX Ograve Atilde -50 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -50 KPX Ograve X -50 KPX Ograve Y -70 KPX Ograve Yacute -70 KPX Ograve Ydieresis -70 KPX Ograve comma -40 KPX Ograve period -40 KPX Ohungarumlaut A -50 KPX Ohungarumlaut Aacute -50 KPX Ohungarumlaut Abreve -50 KPX Ohungarumlaut Acircumflex -50 KPX Ohungarumlaut Adieresis -50 KPX Ohungarumlaut Agrave -50 KPX Ohungarumlaut Amacron -50 KPX Ohungarumlaut Aogonek -50 KPX Ohungarumlaut Aring -50 KPX Ohungarumlaut Atilde -50 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -50 KPX Ohungarumlaut X -50 KPX Ohungarumlaut Y -70 KPX Ohungarumlaut Yacute -70 KPX Ohungarumlaut Ydieresis -70 KPX Ohungarumlaut comma -40 KPX Ohungarumlaut period -40 KPX Omacron A -50 KPX Omacron Aacute -50 KPX Omacron Abreve -50 KPX Omacron Acircumflex -50 KPX Omacron Adieresis -50 KPX Omacron Agrave -50 KPX Omacron Amacron -50 KPX Omacron Aogonek -50 KPX Omacron Aring -50 KPX Omacron Atilde -50 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -50 KPX Omacron X -50 KPX Omacron Y -70 KPX Omacron Yacute -70 KPX Omacron Ydieresis -70 KPX Omacron comma -40 KPX Omacron period -40 KPX Oslash A -50 KPX Oslash Aacute -50 KPX Oslash Abreve -50 KPX Oslash Acircumflex -50 KPX Oslash Adieresis -50 KPX Oslash Agrave -50 KPX Oslash Amacron -50 KPX Oslash Aogonek -50 KPX Oslash Aring -50 KPX Oslash Atilde -50 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -50 KPX Oslash X -50 KPX Oslash Y -70 KPX Oslash Yacute -70 KPX Oslash Ydieresis -70 KPX Oslash comma -40 KPX Oslash period -40 KPX Otilde A -50 KPX Otilde Aacute -50 KPX Otilde Abreve -50 KPX Otilde Acircumflex -50 KPX Otilde Adieresis -50 KPX Otilde Agrave -50 KPX Otilde Amacron -50 KPX Otilde Aogonek -50 KPX Otilde Aring -50 KPX Otilde Atilde -50 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -50 KPX Otilde X -50 KPX Otilde Y -70 KPX Otilde Yacute -70 KPX Otilde Ydieresis -70 KPX Otilde comma -40 KPX Otilde period -40 KPX P A -100 KPX P Aacute -100 KPX P Abreve -100 KPX P Acircumflex -100 KPX P Adieresis -100 KPX P Agrave -100 KPX P Amacron -100 KPX P Aogonek -100 KPX P Aring -100 KPX P Atilde -100 KPX P a -30 KPX P aacute -30 KPX P abreve -30 KPX P acircumflex -30 KPX P adieresis -30 KPX P agrave -30 KPX P amacron -30 KPX P aogonek -30 KPX P aring -30 KPX P atilde -30 KPX P comma -120 KPX P e -30 KPX P eacute -30 KPX P ecaron -30 KPX P ecircumflex -30 KPX P edieresis -30 KPX P edotaccent -30 KPX P egrave -30 KPX P emacron -30 KPX P eogonek -30 KPX P o -40 KPX P oacute -40 KPX P ocircumflex -40 KPX P odieresis -40 KPX P ograve -40 KPX P ohungarumlaut -40 KPX P omacron -40 KPX P oslash -40 KPX P otilde -40 KPX P period -120 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX Q comma 20 KPX Q period 20 KPX R O -20 KPX R Oacute -20 KPX R Ocircumflex -20 KPX R Odieresis -20 KPX R Ograve -20 KPX R Ohungarumlaut -20 KPX R Omacron -20 KPX R Oslash -20 KPX R Otilde -20 KPX R T -20 KPX R Tcaron -20 KPX R Tcommaaccent -20 KPX R U -20 KPX R Uacute -20 KPX R Ucircumflex -20 KPX R Udieresis -20 KPX R Ugrave -20 KPX R Uhungarumlaut -20 KPX R Umacron -20 KPX R Uogonek -20 KPX R Uring -20 KPX R V -50 KPX R W -40 KPX R Y -50 KPX R Yacute -50 KPX R Ydieresis -50 KPX Racute O -20 KPX Racute Oacute -20 KPX Racute Ocircumflex -20 KPX Racute Odieresis -20 KPX Racute Ograve -20 KPX Racute Ohungarumlaut -20 KPX Racute Omacron -20 KPX Racute Oslash -20 KPX Racute Otilde -20 KPX Racute T -20 KPX Racute Tcaron -20 KPX Racute Tcommaaccent -20 KPX Racute U -20 KPX Racute Uacute -20 KPX Racute Ucircumflex -20 KPX Racute Udieresis -20 KPX Racute Ugrave -20 KPX Racute Uhungarumlaut -20 KPX Racute Umacron -20 KPX Racute Uogonek -20 KPX Racute Uring -20 KPX Racute V -50 KPX Racute W -40 KPX Racute Y -50 KPX Racute Yacute -50 KPX Racute Ydieresis -50 KPX Rcaron O -20 KPX Rcaron Oacute -20 KPX Rcaron Ocircumflex -20 KPX Rcaron Odieresis -20 KPX Rcaron Ograve -20 KPX Rcaron Ohungarumlaut -20 KPX Rcaron Omacron -20 KPX Rcaron Oslash -20 KPX Rcaron Otilde -20 KPX Rcaron T -20 KPX Rcaron Tcaron -20 KPX Rcaron Tcommaaccent -20 KPX Rcaron U -20 KPX Rcaron Uacute -20 KPX Rcaron Ucircumflex -20 KPX Rcaron Udieresis -20 KPX Rcaron Ugrave -20 KPX Rcaron Uhungarumlaut -20 KPX Rcaron Umacron -20 KPX Rcaron Uogonek -20 KPX Rcaron Uring -20 KPX Rcaron V -50 KPX Rcaron W -40 KPX Rcaron Y -50 KPX Rcaron Yacute -50 KPX Rcaron Ydieresis -50 KPX Rcommaaccent O -20 KPX Rcommaaccent Oacute -20 KPX Rcommaaccent Ocircumflex -20 KPX Rcommaaccent Odieresis -20 KPX Rcommaaccent Ograve -20 KPX Rcommaaccent Ohungarumlaut -20 KPX Rcommaaccent Omacron -20 KPX Rcommaaccent Oslash -20 KPX Rcommaaccent Otilde -20 KPX Rcommaaccent T -20 KPX Rcommaaccent Tcaron -20 KPX Rcommaaccent Tcommaaccent -20 KPX Rcommaaccent U -20 KPX Rcommaaccent Uacute -20 KPX Rcommaaccent Ucircumflex -20 KPX Rcommaaccent Udieresis -20 KPX Rcommaaccent Ugrave -20 KPX Rcommaaccent Uhungarumlaut -20 KPX Rcommaaccent Umacron -20 KPX Rcommaaccent Uogonek -20 KPX Rcommaaccent Uring -20 KPX Rcommaaccent V -50 KPX Rcommaaccent W -40 KPX Rcommaaccent Y -50 KPX Rcommaaccent Yacute -50 KPX Rcommaaccent Ydieresis -50 KPX T A -90 KPX T Aacute -90 KPX T Abreve -90 KPX T Acircumflex -90 KPX T Adieresis -90 KPX T Agrave -90 KPX T Amacron -90 KPX T Aogonek -90 KPX T Aring -90 KPX T Atilde -90 KPX T O -40 KPX T Oacute -40 KPX T Ocircumflex -40 KPX T Odieresis -40 KPX T Ograve -40 KPX T Ohungarumlaut -40 KPX T Omacron -40 KPX T Oslash -40 KPX T Otilde -40 KPX T a -80 KPX T aacute -80 KPX T abreve -80 KPX T acircumflex -80 KPX T adieresis -80 KPX T agrave -80 KPX T amacron -80 KPX T aogonek -80 KPX T aring -80 KPX T atilde -80 KPX T colon -40 KPX T comma -80 KPX T e -60 KPX T eacute -60 KPX T ecaron -60 KPX T ecircumflex -60 KPX T edieresis -60 KPX T edotaccent -60 KPX T egrave -60 KPX T emacron -60 KPX T eogonek -60 KPX T hyphen -120 KPX T o -80 KPX T oacute -80 KPX T ocircumflex -80 KPX T odieresis -80 KPX T ograve -80 KPX T ohungarumlaut -80 KPX T omacron -80 KPX T oslash -80 KPX T otilde -80 KPX T period -80 KPX T r -80 KPX T racute -80 KPX T rcommaaccent -80 KPX T semicolon -40 KPX T u -90 KPX T uacute -90 KPX T ucircumflex -90 KPX T udieresis -90 KPX T ugrave -90 KPX T uhungarumlaut -90 KPX T umacron -90 KPX T uogonek -90 KPX T uring -90 KPX T w -60 KPX T y -60 KPX T yacute -60 KPX T ydieresis -60 KPX Tcaron A -90 KPX Tcaron Aacute -90 KPX Tcaron Abreve -90 KPX Tcaron Acircumflex -90 KPX Tcaron Adieresis -90 KPX Tcaron Agrave -90 KPX Tcaron Amacron -90 KPX Tcaron Aogonek -90 KPX Tcaron Aring -90 KPX Tcaron Atilde -90 KPX Tcaron O -40 KPX Tcaron Oacute -40 KPX Tcaron Ocircumflex -40 KPX Tcaron Odieresis -40 KPX Tcaron Ograve -40 KPX Tcaron Ohungarumlaut -40 KPX Tcaron Omacron -40 KPX Tcaron Oslash -40 KPX Tcaron Otilde -40 KPX Tcaron a -80 KPX Tcaron aacute -80 KPX Tcaron abreve -80 KPX Tcaron acircumflex -80 KPX Tcaron adieresis -80 KPX Tcaron agrave -80 KPX Tcaron amacron -80 KPX Tcaron aogonek -80 KPX Tcaron aring -80 KPX Tcaron atilde -80 KPX Tcaron colon -40 KPX Tcaron comma -80 KPX Tcaron e -60 KPX Tcaron eacute -60 KPX Tcaron ecaron -60 KPX Tcaron ecircumflex -60 KPX Tcaron edieresis -60 KPX Tcaron edotaccent -60 KPX Tcaron egrave -60 KPX Tcaron emacron -60 KPX Tcaron eogonek -60 KPX Tcaron hyphen -120 KPX Tcaron o -80 KPX Tcaron oacute -80 KPX Tcaron ocircumflex -80 KPX Tcaron odieresis -80 KPX Tcaron ograve -80 KPX Tcaron ohungarumlaut -80 KPX Tcaron omacron -80 KPX Tcaron oslash -80 KPX Tcaron otilde -80 KPX Tcaron period -80 KPX Tcaron r -80 KPX Tcaron racute -80 KPX Tcaron rcommaaccent -80 KPX Tcaron semicolon -40 KPX Tcaron u -90 KPX Tcaron uacute -90 KPX Tcaron ucircumflex -90 KPX Tcaron udieresis -90 KPX Tcaron ugrave -90 KPX Tcaron uhungarumlaut -90 KPX Tcaron umacron -90 KPX Tcaron uogonek -90 KPX Tcaron uring -90 KPX Tcaron w -60 KPX Tcaron y -60 KPX Tcaron yacute -60 KPX Tcaron ydieresis -60 KPX Tcommaaccent A -90 KPX Tcommaaccent Aacute -90 KPX Tcommaaccent Abreve -90 KPX Tcommaaccent Acircumflex -90 KPX Tcommaaccent Adieresis -90 KPX Tcommaaccent Agrave -90 KPX Tcommaaccent Amacron -90 KPX Tcommaaccent Aogonek -90 KPX Tcommaaccent Aring -90 KPX Tcommaaccent Atilde -90 KPX Tcommaaccent O -40 KPX Tcommaaccent Oacute -40 KPX Tcommaaccent Ocircumflex -40 KPX Tcommaaccent Odieresis -40 KPX Tcommaaccent Ograve -40 KPX Tcommaaccent Ohungarumlaut -40 KPX Tcommaaccent Omacron -40 KPX Tcommaaccent Oslash -40 KPX Tcommaaccent Otilde -40 KPX Tcommaaccent a -80 KPX Tcommaaccent aacute -80 KPX Tcommaaccent abreve -80 KPX Tcommaaccent acircumflex -80 KPX Tcommaaccent adieresis -80 KPX Tcommaaccent agrave -80 KPX Tcommaaccent amacron -80 KPX Tcommaaccent aogonek -80 KPX Tcommaaccent aring -80 KPX Tcommaaccent atilde -80 KPX Tcommaaccent colon -40 KPX Tcommaaccent comma -80 KPX Tcommaaccent e -60 KPX Tcommaaccent eacute -60 KPX Tcommaaccent ecaron -60 KPX Tcommaaccent ecircumflex -60 KPX Tcommaaccent edieresis -60 KPX Tcommaaccent edotaccent -60 KPX Tcommaaccent egrave -60 KPX Tcommaaccent emacron -60 KPX Tcommaaccent eogonek -60 KPX Tcommaaccent hyphen -120 KPX Tcommaaccent o -80 KPX Tcommaaccent oacute -80 KPX Tcommaaccent ocircumflex -80 KPX Tcommaaccent odieresis -80 KPX Tcommaaccent ograve -80 KPX Tcommaaccent ohungarumlaut -80 KPX Tcommaaccent omacron -80 KPX Tcommaaccent oslash -80 KPX Tcommaaccent otilde -80 KPX Tcommaaccent period -80 KPX Tcommaaccent r -80 KPX Tcommaaccent racute -80 KPX Tcommaaccent rcommaaccent -80 KPX Tcommaaccent semicolon -40 KPX Tcommaaccent u -90 KPX Tcommaaccent uacute -90 KPX Tcommaaccent ucircumflex -90 KPX Tcommaaccent udieresis -90 KPX Tcommaaccent ugrave -90 KPX Tcommaaccent uhungarumlaut -90 KPX Tcommaaccent umacron -90 KPX Tcommaaccent uogonek -90 KPX Tcommaaccent uring -90 KPX Tcommaaccent w -60 KPX Tcommaaccent y -60 KPX Tcommaaccent yacute -60 KPX Tcommaaccent ydieresis -60 KPX U A -50 KPX U Aacute -50 KPX U Abreve -50 KPX U Acircumflex -50 KPX U Adieresis -50 KPX U Agrave -50 KPX U Amacron -50 KPX U Aogonek -50 KPX U Aring -50 KPX U Atilde -50 KPX U comma -30 KPX U period -30 KPX Uacute A -50 KPX Uacute Aacute -50 KPX Uacute Abreve -50 KPX Uacute Acircumflex -50 KPX Uacute Adieresis -50 KPX Uacute Agrave -50 KPX Uacute Amacron -50 KPX Uacute Aogonek -50 KPX Uacute Aring -50 KPX Uacute Atilde -50 KPX Uacute comma -30 KPX Uacute period -30 KPX Ucircumflex A -50 KPX Ucircumflex Aacute -50 KPX Ucircumflex Abreve -50 KPX Ucircumflex Acircumflex -50 KPX Ucircumflex Adieresis -50 KPX Ucircumflex Agrave -50 KPX Ucircumflex Amacron -50 KPX Ucircumflex Aogonek -50 KPX Ucircumflex Aring -50 KPX Ucircumflex Atilde -50 KPX Ucircumflex comma -30 KPX Ucircumflex period -30 KPX Udieresis A -50 KPX Udieresis Aacute -50 KPX Udieresis Abreve -50 KPX Udieresis Acircumflex -50 KPX Udieresis Adieresis -50 KPX Udieresis Agrave -50 KPX Udieresis Amacron -50 KPX Udieresis Aogonek -50 KPX Udieresis Aring -50 KPX Udieresis Atilde -50 KPX Udieresis comma -30 KPX Udieresis period -30 KPX Ugrave A -50 KPX Ugrave Aacute -50 KPX Ugrave Abreve -50 KPX Ugrave Acircumflex -50 KPX Ugrave Adieresis -50 KPX Ugrave Agrave -50 KPX Ugrave Amacron -50 KPX Ugrave Aogonek -50 KPX Ugrave Aring -50 KPX Ugrave Atilde -50 KPX Ugrave comma -30 KPX Ugrave period -30 KPX Uhungarumlaut A -50 KPX Uhungarumlaut Aacute -50 KPX Uhungarumlaut Abreve -50 KPX Uhungarumlaut Acircumflex -50 KPX Uhungarumlaut Adieresis -50 KPX Uhungarumlaut Agrave -50 KPX Uhungarumlaut Amacron -50 KPX Uhungarumlaut Aogonek -50 KPX Uhungarumlaut Aring -50 KPX Uhungarumlaut Atilde -50 KPX Uhungarumlaut comma -30 KPX Uhungarumlaut period -30 KPX Umacron A -50 KPX Umacron Aacute -50 KPX Umacron Abreve -50 KPX Umacron Acircumflex -50 KPX Umacron Adieresis -50 KPX Umacron Agrave -50 KPX Umacron Amacron -50 KPX Umacron Aogonek -50 KPX Umacron Aring -50 KPX Umacron Atilde -50 KPX Umacron comma -30 KPX Umacron period -30 KPX Uogonek A -50 KPX Uogonek Aacute -50 KPX Uogonek Abreve -50 KPX Uogonek Acircumflex -50 KPX Uogonek Adieresis -50 KPX Uogonek Agrave -50 KPX Uogonek Amacron -50 KPX Uogonek Aogonek -50 KPX Uogonek Aring -50 KPX Uogonek Atilde -50 KPX Uogonek comma -30 KPX Uogonek period -30 KPX Uring A -50 KPX Uring Aacute -50 KPX Uring Abreve -50 KPX Uring Acircumflex -50 KPX Uring Adieresis -50 KPX Uring Agrave -50 KPX Uring Amacron -50 KPX Uring Aogonek -50 KPX Uring Aring -50 KPX Uring Atilde -50 KPX Uring comma -30 KPX Uring period -30 KPX V A -80 KPX V Aacute -80 KPX V Abreve -80 KPX V Acircumflex -80 KPX V Adieresis -80 KPX V Agrave -80 KPX V Amacron -80 KPX V Aogonek -80 KPX V Aring -80 KPX V Atilde -80 KPX V G -50 KPX V Gbreve -50 KPX V Gcommaaccent -50 KPX V O -50 KPX V Oacute -50 KPX V Ocircumflex -50 KPX V Odieresis -50 KPX V Ograve -50 KPX V Ohungarumlaut -50 KPX V Omacron -50 KPX V Oslash -50 KPX V Otilde -50 KPX V a -60 KPX V aacute -60 KPX V abreve -60 KPX V acircumflex -60 KPX V adieresis -60 KPX V agrave -60 KPX V amacron -60 KPX V aogonek -60 KPX V aring -60 KPX V atilde -60 KPX V colon -40 KPX V comma -120 KPX V e -50 KPX V eacute -50 KPX V ecaron -50 KPX V ecircumflex -50 KPX V edieresis -50 KPX V edotaccent -50 KPX V egrave -50 KPX V emacron -50 KPX V eogonek -50 KPX V hyphen -80 KPX V o -90 KPX V oacute -90 KPX V ocircumflex -90 KPX V odieresis -90 KPX V ograve -90 KPX V ohungarumlaut -90 KPX V omacron -90 KPX V oslash -90 KPX V otilde -90 KPX V period -120 KPX V semicolon -40 KPX V u -60 KPX V uacute -60 KPX V ucircumflex -60 KPX V udieresis -60 KPX V ugrave -60 KPX V uhungarumlaut -60 KPX V umacron -60 KPX V uogonek -60 KPX V uring -60 KPX W A -60 KPX W Aacute -60 KPX W Abreve -60 KPX W Acircumflex -60 KPX W Adieresis -60 KPX W Agrave -60 KPX W Amacron -60 KPX W Aogonek -60 KPX W Aring -60 KPX W Atilde -60 KPX W O -20 KPX W Oacute -20 KPX W Ocircumflex -20 KPX W Odieresis -20 KPX W Ograve -20 KPX W Ohungarumlaut -20 KPX W Omacron -20 KPX W Oslash -20 KPX W Otilde -20 KPX W a -40 KPX W aacute -40 KPX W abreve -40 KPX W acircumflex -40 KPX W adieresis -40 KPX W agrave -40 KPX W amacron -40 KPX W aogonek -40 KPX W aring -40 KPX W atilde -40 KPX W colon -10 KPX W comma -80 KPX W e -35 KPX W eacute -35 KPX W ecaron -35 KPX W ecircumflex -35 KPX W edieresis -35 KPX W edotaccent -35 KPX W egrave -35 KPX W emacron -35 KPX W eogonek -35 KPX W hyphen -40 KPX W o -60 KPX W oacute -60 KPX W ocircumflex -60 KPX W odieresis -60 KPX W ograve -60 KPX W ohungarumlaut -60 KPX W omacron -60 KPX W oslash -60 KPX W otilde -60 KPX W period -80 KPX W semicolon -10 KPX W u -45 KPX W uacute -45 KPX W ucircumflex -45 KPX W udieresis -45 KPX W ugrave -45 KPX W uhungarumlaut -45 KPX W umacron -45 KPX W uogonek -45 KPX W uring -45 KPX W y -20 KPX W yacute -20 KPX W ydieresis -20 KPX Y A -110 KPX Y Aacute -110 KPX Y Abreve -110 KPX Y Acircumflex -110 KPX Y Adieresis -110 KPX Y Agrave -110 KPX Y Amacron -110 KPX Y Aogonek -110 KPX Y Aring -110 KPX Y Atilde -110 KPX Y O -70 KPX Y Oacute -70 KPX Y Ocircumflex -70 KPX Y Odieresis -70 KPX Y Ograve -70 KPX Y Ohungarumlaut -70 KPX Y Omacron -70 KPX Y Oslash -70 KPX Y Otilde -70 KPX Y a -90 KPX Y aacute -90 KPX Y abreve -90 KPX Y acircumflex -90 KPX Y adieresis -90 KPX Y agrave -90 KPX Y amacron -90 KPX Y aogonek -90 KPX Y aring -90 KPX Y atilde -90 KPX Y colon -50 KPX Y comma -100 KPX Y e -80 KPX Y eacute -80 KPX Y ecaron -80 KPX Y ecircumflex -80 KPX Y edieresis -80 KPX Y edotaccent -80 KPX Y egrave -80 KPX Y emacron -80 KPX Y eogonek -80 KPX Y o -100 KPX Y oacute -100 KPX Y ocircumflex -100 KPX Y odieresis -100 KPX Y ograve -100 KPX Y ohungarumlaut -100 KPX Y omacron -100 KPX Y oslash -100 KPX Y otilde -100 KPX Y period -100 KPX Y semicolon -50 KPX Y u -100 KPX Y uacute -100 KPX Y ucircumflex -100 KPX Y udieresis -100 KPX Y ugrave -100 KPX Y uhungarumlaut -100 KPX Y umacron -100 KPX Y uogonek -100 KPX Y uring -100 KPX Yacute A -110 KPX Yacute Aacute -110 KPX Yacute Abreve -110 KPX Yacute Acircumflex -110 KPX Yacute Adieresis -110 KPX Yacute Agrave -110 KPX Yacute Amacron -110 KPX Yacute Aogonek -110 KPX Yacute Aring -110 KPX Yacute Atilde -110 KPX Yacute O -70 KPX Yacute Oacute -70 KPX Yacute Ocircumflex -70 KPX Yacute Odieresis -70 KPX Yacute Ograve -70 KPX Yacute Ohungarumlaut -70 KPX Yacute Omacron -70 KPX Yacute Oslash -70 KPX Yacute Otilde -70 KPX Yacute a -90 KPX Yacute aacute -90 KPX Yacute abreve -90 KPX Yacute acircumflex -90 KPX Yacute adieresis -90 KPX Yacute agrave -90 KPX Yacute amacron -90 KPX Yacute aogonek -90 KPX Yacute aring -90 KPX Yacute atilde -90 KPX Yacute colon -50 KPX Yacute comma -100 KPX Yacute e -80 KPX Yacute eacute -80 KPX Yacute ecaron -80 KPX Yacute ecircumflex -80 KPX Yacute edieresis -80 KPX Yacute edotaccent -80 KPX Yacute egrave -80 KPX Yacute emacron -80 KPX Yacute eogonek -80 KPX Yacute o -100 KPX Yacute oacute -100 KPX Yacute ocircumflex -100 KPX Yacute odieresis -100 KPX Yacute ograve -100 KPX Yacute ohungarumlaut -100 KPX Yacute omacron -100 KPX Yacute oslash -100 KPX Yacute otilde -100 KPX Yacute period -100 KPX Yacute semicolon -50 KPX Yacute u -100 KPX Yacute uacute -100 KPX Yacute ucircumflex -100 KPX Yacute udieresis -100 KPX Yacute ugrave -100 KPX Yacute uhungarumlaut -100 KPX Yacute umacron -100 KPX Yacute uogonek -100 KPX Yacute uring -100 KPX Ydieresis A -110 KPX Ydieresis Aacute -110 KPX Ydieresis Abreve -110 KPX Ydieresis Acircumflex -110 KPX Ydieresis Adieresis -110 KPX Ydieresis Agrave -110 KPX Ydieresis Amacron -110 KPX Ydieresis Aogonek -110 KPX Ydieresis Aring -110 KPX Ydieresis Atilde -110 KPX Ydieresis O -70 KPX Ydieresis Oacute -70 KPX Ydieresis Ocircumflex -70 KPX Ydieresis Odieresis -70 KPX Ydieresis Ograve -70 KPX Ydieresis Ohungarumlaut -70 KPX Ydieresis Omacron -70 KPX Ydieresis Oslash -70 KPX Ydieresis Otilde -70 KPX Ydieresis a -90 KPX Ydieresis aacute -90 KPX Ydieresis abreve -90 KPX Ydieresis acircumflex -90 KPX Ydieresis adieresis -90 KPX Ydieresis agrave -90 KPX Ydieresis amacron -90 KPX Ydieresis aogonek -90 KPX Ydieresis aring -90 KPX Ydieresis atilde -90 KPX Ydieresis colon -50 KPX Ydieresis comma -100 KPX Ydieresis e -80 KPX Ydieresis eacute -80 KPX Ydieresis ecaron -80 KPX Ydieresis ecircumflex -80 KPX Ydieresis edieresis -80 KPX Ydieresis edotaccent -80 KPX Ydieresis egrave -80 KPX Ydieresis emacron -80 KPX Ydieresis eogonek -80 KPX Ydieresis o -100 KPX Ydieresis oacute -100 KPX Ydieresis ocircumflex -100 KPX Ydieresis odieresis -100 KPX Ydieresis ograve -100 KPX Ydieresis ohungarumlaut -100 KPX Ydieresis omacron -100 KPX Ydieresis oslash -100 KPX Ydieresis otilde -100 KPX Ydieresis period -100 KPX Ydieresis semicolon -50 KPX Ydieresis u -100 KPX Ydieresis uacute -100 KPX Ydieresis ucircumflex -100 KPX Ydieresis udieresis -100 KPX Ydieresis ugrave -100 KPX Ydieresis uhungarumlaut -100 KPX Ydieresis umacron -100 KPX Ydieresis uogonek -100 KPX Ydieresis uring -100 KPX a g -10 KPX a gbreve -10 KPX a gcommaaccent -10 KPX a v -15 KPX a w -15 KPX a y -20 KPX a yacute -20 KPX a ydieresis -20 KPX aacute g -10 KPX aacute gbreve -10 KPX aacute gcommaaccent -10 KPX aacute v -15 KPX aacute w -15 KPX aacute y -20 KPX aacute yacute -20 KPX aacute ydieresis -20 KPX abreve g -10 KPX abreve gbreve -10 KPX abreve gcommaaccent -10 KPX abreve v -15 KPX abreve w -15 KPX abreve y -20 KPX abreve yacute -20 KPX abreve ydieresis -20 KPX acircumflex g -10 KPX acircumflex gbreve -10 KPX acircumflex gcommaaccent -10 KPX acircumflex v -15 KPX acircumflex w -15 KPX acircumflex y -20 KPX acircumflex yacute -20 KPX acircumflex ydieresis -20 KPX adieresis g -10 KPX adieresis gbreve -10 KPX adieresis gcommaaccent -10 KPX adieresis v -15 KPX adieresis w -15 KPX adieresis y -20 KPX adieresis yacute -20 KPX adieresis ydieresis -20 KPX agrave g -10 KPX agrave gbreve -10 KPX agrave gcommaaccent -10 KPX agrave v -15 KPX agrave w -15 KPX agrave y -20 KPX agrave yacute -20 KPX agrave ydieresis -20 KPX amacron g -10 KPX amacron gbreve -10 KPX amacron gcommaaccent -10 KPX amacron v -15 KPX amacron w -15 KPX amacron y -20 KPX amacron yacute -20 KPX amacron ydieresis -20 KPX aogonek g -10 KPX aogonek gbreve -10 KPX aogonek gcommaaccent -10 KPX aogonek v -15 KPX aogonek w -15 KPX aogonek y -20 KPX aogonek yacute -20 KPX aogonek ydieresis -20 KPX aring g -10 KPX aring gbreve -10 KPX aring gcommaaccent -10 KPX aring v -15 KPX aring w -15 KPX aring y -20 KPX aring yacute -20 KPX aring ydieresis -20 KPX atilde g -10 KPX atilde gbreve -10 KPX atilde gcommaaccent -10 KPX atilde v -15 KPX atilde w -15 KPX atilde y -20 KPX atilde yacute -20 KPX atilde ydieresis -20 KPX b l -10 KPX b lacute -10 KPX b lcommaaccent -10 KPX b lslash -10 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX b v -20 KPX b y -20 KPX b yacute -20 KPX b ydieresis -20 KPX c h -10 KPX c k -20 KPX c kcommaaccent -20 KPX c l -20 KPX c lacute -20 KPX c lcommaaccent -20 KPX c lslash -20 KPX c y -10 KPX c yacute -10 KPX c ydieresis -10 KPX cacute h -10 KPX cacute k -20 KPX cacute kcommaaccent -20 KPX cacute l -20 KPX cacute lacute -20 KPX cacute lcommaaccent -20 KPX cacute lslash -20 KPX cacute y -10 KPX cacute yacute -10 KPX cacute ydieresis -10 KPX ccaron h -10 KPX ccaron k -20 KPX ccaron kcommaaccent -20 KPX ccaron l -20 KPX ccaron lacute -20 KPX ccaron lcommaaccent -20 KPX ccaron lslash -20 KPX ccaron y -10 KPX ccaron yacute -10 KPX ccaron ydieresis -10 KPX ccedilla h -10 KPX ccedilla k -20 KPX ccedilla kcommaaccent -20 KPX ccedilla l -20 KPX ccedilla lacute -20 KPX ccedilla lcommaaccent -20 KPX ccedilla lslash -20 KPX ccedilla y -10 KPX ccedilla yacute -10 KPX ccedilla ydieresis -10 KPX colon space -40 KPX comma quotedblright -120 KPX comma quoteright -120 KPX comma space -40 KPX d d -10 KPX d dcroat -10 KPX d v -15 KPX d w -15 KPX d y -15 KPX d yacute -15 KPX d ydieresis -15 KPX dcroat d -10 KPX dcroat dcroat -10 KPX dcroat v -15 KPX dcroat w -15 KPX dcroat y -15 KPX dcroat yacute -15 KPX dcroat ydieresis -15 KPX e comma 10 KPX e period 20 KPX e v -15 KPX e w -15 KPX e x -15 KPX e y -15 KPX e yacute -15 KPX e ydieresis -15 KPX eacute comma 10 KPX eacute period 20 KPX eacute v -15 KPX eacute w -15 KPX eacute x -15 KPX eacute y -15 KPX eacute yacute -15 KPX eacute ydieresis -15 KPX ecaron comma 10 KPX ecaron period 20 KPX ecaron v -15 KPX ecaron w -15 KPX ecaron x -15 KPX ecaron y -15 KPX ecaron yacute -15 KPX ecaron ydieresis -15 KPX ecircumflex comma 10 KPX ecircumflex period 20 KPX ecircumflex v -15 KPX ecircumflex w -15 KPX ecircumflex x -15 KPX ecircumflex y -15 KPX ecircumflex yacute -15 KPX ecircumflex ydieresis -15 KPX edieresis comma 10 KPX edieresis period 20 KPX edieresis v -15 KPX edieresis w -15 KPX edieresis x -15 KPX edieresis y -15 KPX edieresis yacute -15 KPX edieresis ydieresis -15 KPX edotaccent comma 10 KPX edotaccent period 20 KPX edotaccent v -15 KPX edotaccent w -15 KPX edotaccent x -15 KPX edotaccent y -15 KPX edotaccent yacute -15 KPX edotaccent ydieresis -15 KPX egrave comma 10 KPX egrave period 20 KPX egrave v -15 KPX egrave w -15 KPX egrave x -15 KPX egrave y -15 KPX egrave yacute -15 KPX egrave ydieresis -15 KPX emacron comma 10 KPX emacron period 20 KPX emacron v -15 KPX emacron w -15 KPX emacron x -15 KPX emacron y -15 KPX emacron yacute -15 KPX emacron ydieresis -15 KPX eogonek comma 10 KPX eogonek period 20 KPX eogonek v -15 KPX eogonek w -15 KPX eogonek x -15 KPX eogonek y -15 KPX eogonek yacute -15 KPX eogonek ydieresis -15 KPX f comma -10 KPX f e -10 KPX f eacute -10 KPX f ecaron -10 KPX f ecircumflex -10 KPX f edieresis -10 KPX f edotaccent -10 KPX f egrave -10 KPX f emacron -10 KPX f eogonek -10 KPX f o -20 KPX f oacute -20 KPX f ocircumflex -20 KPX f odieresis -20 KPX f ograve -20 KPX f ohungarumlaut -20 KPX f omacron -20 KPX f oslash -20 KPX f otilde -20 KPX f period -10 KPX f quotedblright 30 KPX f quoteright 30 KPX g e 10 KPX g eacute 10 KPX g ecaron 10 KPX g ecircumflex 10 KPX g edieresis 10 KPX g edotaccent 10 KPX g egrave 10 KPX g emacron 10 KPX g eogonek 10 KPX g g -10 KPX g gbreve -10 KPX g gcommaaccent -10 KPX gbreve e 10 KPX gbreve eacute 10 KPX gbreve ecaron 10 KPX gbreve ecircumflex 10 KPX gbreve edieresis 10 KPX gbreve edotaccent 10 KPX gbreve egrave 10 KPX gbreve emacron 10 KPX gbreve eogonek 10 KPX gbreve g -10 KPX gbreve gbreve -10 KPX gbreve gcommaaccent -10 KPX gcommaaccent e 10 KPX gcommaaccent eacute 10 KPX gcommaaccent ecaron 10 KPX gcommaaccent ecircumflex 10 KPX gcommaaccent edieresis 10 KPX gcommaaccent edotaccent 10 KPX gcommaaccent egrave 10 KPX gcommaaccent emacron 10 KPX gcommaaccent eogonek 10 KPX gcommaaccent g -10 KPX gcommaaccent gbreve -10 KPX gcommaaccent gcommaaccent -10 KPX h y -20 KPX h yacute -20 KPX h ydieresis -20 KPX k o -15 KPX k oacute -15 KPX k ocircumflex -15 KPX k odieresis -15 KPX k ograve -15 KPX k ohungarumlaut -15 KPX k omacron -15 KPX k oslash -15 KPX k otilde -15 KPX kcommaaccent o -15 KPX kcommaaccent oacute -15 KPX kcommaaccent ocircumflex -15 KPX kcommaaccent odieresis -15 KPX kcommaaccent ograve -15 KPX kcommaaccent ohungarumlaut -15 KPX kcommaaccent omacron -15 KPX kcommaaccent oslash -15 KPX kcommaaccent otilde -15 KPX l w -15 KPX l y -15 KPX l yacute -15 KPX l ydieresis -15 KPX lacute w -15 KPX lacute y -15 KPX lacute yacute -15 KPX lacute ydieresis -15 KPX lcommaaccent w -15 KPX lcommaaccent y -15 KPX lcommaaccent yacute -15 KPX lcommaaccent ydieresis -15 KPX lslash w -15 KPX lslash y -15 KPX lslash yacute -15 KPX lslash ydieresis -15 KPX m u -20 KPX m uacute -20 KPX m ucircumflex -20 KPX m udieresis -20 KPX m ugrave -20 KPX m uhungarumlaut -20 KPX m umacron -20 KPX m uogonek -20 KPX m uring -20 KPX m y -30 KPX m yacute -30 KPX m ydieresis -30 KPX n u -10 KPX n uacute -10 KPX n ucircumflex -10 KPX n udieresis -10 KPX n ugrave -10 KPX n uhungarumlaut -10 KPX n umacron -10 KPX n uogonek -10 KPX n uring -10 KPX n v -40 KPX n y -20 KPX n yacute -20 KPX n ydieresis -20 KPX nacute u -10 KPX nacute uacute -10 KPX nacute ucircumflex -10 KPX nacute udieresis -10 KPX nacute ugrave -10 KPX nacute uhungarumlaut -10 KPX nacute umacron -10 KPX nacute uogonek -10 KPX nacute uring -10 KPX nacute v -40 KPX nacute y -20 KPX nacute yacute -20 KPX nacute ydieresis -20 KPX ncaron u -10 KPX ncaron uacute -10 KPX ncaron ucircumflex -10 KPX ncaron udieresis -10 KPX ncaron ugrave -10 KPX ncaron uhungarumlaut -10 KPX ncaron umacron -10 KPX ncaron uogonek -10 KPX ncaron uring -10 KPX ncaron v -40 KPX ncaron y -20 KPX ncaron yacute -20 KPX ncaron ydieresis -20 KPX ncommaaccent u -10 KPX ncommaaccent uacute -10 KPX ncommaaccent ucircumflex -10 KPX ncommaaccent udieresis -10 KPX ncommaaccent ugrave -10 KPX ncommaaccent uhungarumlaut -10 KPX ncommaaccent umacron -10 KPX ncommaaccent uogonek -10 KPX ncommaaccent uring -10 KPX ncommaaccent v -40 KPX ncommaaccent y -20 KPX ncommaaccent yacute -20 KPX ncommaaccent ydieresis -20 KPX ntilde u -10 KPX ntilde uacute -10 KPX ntilde ucircumflex -10 KPX ntilde udieresis -10 KPX ntilde ugrave -10 KPX ntilde uhungarumlaut -10 KPX ntilde umacron -10 KPX ntilde uogonek -10 KPX ntilde uring -10 KPX ntilde v -40 KPX ntilde y -20 KPX ntilde yacute -20 KPX ntilde ydieresis -20 KPX o v -20 KPX o w -15 KPX o x -30 KPX o y -20 KPX o yacute -20 KPX o ydieresis -20 KPX oacute v -20 KPX oacute w -15 KPX oacute x -30 KPX oacute y -20 KPX oacute yacute -20 KPX oacute ydieresis -20 KPX ocircumflex v -20 KPX ocircumflex w -15 KPX ocircumflex x -30 KPX ocircumflex y -20 KPX ocircumflex yacute -20 KPX ocircumflex ydieresis -20 KPX odieresis v -20 KPX odieresis w -15 KPX odieresis x -30 KPX odieresis y -20 KPX odieresis yacute -20 KPX odieresis ydieresis -20 KPX ograve v -20 KPX ograve w -15 KPX ograve x -30 KPX ograve y -20 KPX ograve yacute -20 KPX ograve ydieresis -20 KPX ohungarumlaut v -20 KPX ohungarumlaut w -15 KPX ohungarumlaut x -30 KPX ohungarumlaut y -20 KPX ohungarumlaut yacute -20 KPX ohungarumlaut ydieresis -20 KPX omacron v -20 KPX omacron w -15 KPX omacron x -30 KPX omacron y -20 KPX omacron yacute -20 KPX omacron ydieresis -20 KPX oslash v -20 KPX oslash w -15 KPX oslash x -30 KPX oslash y -20 KPX oslash yacute -20 KPX oslash ydieresis -20 KPX otilde v -20 KPX otilde w -15 KPX otilde x -30 KPX otilde y -20 KPX otilde yacute -20 KPX otilde ydieresis -20 KPX p y -15 KPX p yacute -15 KPX p ydieresis -15 KPX period quotedblright -120 KPX period quoteright -120 KPX period space -40 KPX quotedblright space -80 KPX quoteleft quoteleft -46 KPX quoteright d -80 KPX quoteright dcroat -80 KPX quoteright l -20 KPX quoteright lacute -20 KPX quoteright lcommaaccent -20 KPX quoteright lslash -20 KPX quoteright quoteright -46 KPX quoteright r -40 KPX quoteright racute -40 KPX quoteright rcaron -40 KPX quoteright rcommaaccent -40 KPX quoteright s -60 KPX quoteright sacute -60 KPX quoteright scaron -60 KPX quoteright scedilla -60 KPX quoteright scommaaccent -60 KPX quoteright space -80 KPX quoteright v -20 KPX r c -20 KPX r cacute -20 KPX r ccaron -20 KPX r ccedilla -20 KPX r comma -60 KPX r d -20 KPX r dcroat -20 KPX r g -15 KPX r gbreve -15 KPX r gcommaaccent -15 KPX r hyphen -20 KPX r o -20 KPX r oacute -20 KPX r ocircumflex -20 KPX r odieresis -20 KPX r ograve -20 KPX r ohungarumlaut -20 KPX r omacron -20 KPX r oslash -20 KPX r otilde -20 KPX r period -60 KPX r q -20 KPX r s -15 KPX r sacute -15 KPX r scaron -15 KPX r scedilla -15 KPX r scommaaccent -15 KPX r t 20 KPX r tcommaaccent 20 KPX r v 10 KPX r y 10 KPX r yacute 10 KPX r ydieresis 10 KPX racute c -20 KPX racute cacute -20 KPX racute ccaron -20 KPX racute ccedilla -20 KPX racute comma -60 KPX racute d -20 KPX racute dcroat -20 KPX racute g -15 KPX racute gbreve -15 KPX racute gcommaaccent -15 KPX racute hyphen -20 KPX racute o -20 KPX racute oacute -20 KPX racute ocircumflex -20 KPX racute odieresis -20 KPX racute ograve -20 KPX racute ohungarumlaut -20 KPX racute omacron -20 KPX racute oslash -20 KPX racute otilde -20 KPX racute period -60 KPX racute q -20 KPX racute s -15 KPX racute sacute -15 KPX racute scaron -15 KPX racute scedilla -15 KPX racute scommaaccent -15 KPX racute t 20 KPX racute tcommaaccent 20 KPX racute v 10 KPX racute y 10 KPX racute yacute 10 KPX racute ydieresis 10 KPX rcaron c -20 KPX rcaron cacute -20 KPX rcaron ccaron -20 KPX rcaron ccedilla -20 KPX rcaron comma -60 KPX rcaron d -20 KPX rcaron dcroat -20 KPX rcaron g -15 KPX rcaron gbreve -15 KPX rcaron gcommaaccent -15 KPX rcaron hyphen -20 KPX rcaron o -20 KPX rcaron oacute -20 KPX rcaron ocircumflex -20 KPX rcaron odieresis -20 KPX rcaron ograve -20 KPX rcaron ohungarumlaut -20 KPX rcaron omacron -20 KPX rcaron oslash -20 KPX rcaron otilde -20 KPX rcaron period -60 KPX rcaron q -20 KPX rcaron s -15 KPX rcaron sacute -15 KPX rcaron scaron -15 KPX rcaron scedilla -15 KPX rcaron scommaaccent -15 KPX rcaron t 20 KPX rcaron tcommaaccent 20 KPX rcaron v 10 KPX rcaron y 10 KPX rcaron yacute 10 KPX rcaron ydieresis 10 KPX rcommaaccent c -20 KPX rcommaaccent cacute -20 KPX rcommaaccent ccaron -20 KPX rcommaaccent ccedilla -20 KPX rcommaaccent comma -60 KPX rcommaaccent d -20 KPX rcommaaccent dcroat -20 KPX rcommaaccent g -15 KPX rcommaaccent gbreve -15 KPX rcommaaccent gcommaaccent -15 KPX rcommaaccent hyphen -20 KPX rcommaaccent o -20 KPX rcommaaccent oacute -20 KPX rcommaaccent ocircumflex -20 KPX rcommaaccent odieresis -20 KPX rcommaaccent ograve -20 KPX rcommaaccent ohungarumlaut -20 KPX rcommaaccent omacron -20 KPX rcommaaccent oslash -20 KPX rcommaaccent otilde -20 KPX rcommaaccent period -60 KPX rcommaaccent q -20 KPX rcommaaccent s -15 KPX rcommaaccent sacute -15 KPX rcommaaccent scaron -15 KPX rcommaaccent scedilla -15 KPX rcommaaccent scommaaccent -15 KPX rcommaaccent t 20 KPX rcommaaccent tcommaaccent 20 KPX rcommaaccent v 10 KPX rcommaaccent y 10 KPX rcommaaccent yacute 10 KPX rcommaaccent ydieresis 10 KPX s w -15 KPX sacute w -15 KPX scaron w -15 KPX scedilla w -15 KPX scommaaccent w -15 KPX semicolon space -40 KPX space T -100 KPX space Tcaron -100 KPX space Tcommaaccent -100 KPX space V -80 KPX space W -80 KPX space Y -120 KPX space Yacute -120 KPX space Ydieresis -120 KPX space quotedblleft -80 KPX space quoteleft -60 KPX v a -20 KPX v aacute -20 KPX v abreve -20 KPX v acircumflex -20 KPX v adieresis -20 KPX v agrave -20 KPX v amacron -20 KPX v aogonek -20 KPX v aring -20 KPX v atilde -20 KPX v comma -80 KPX v o -30 KPX v oacute -30 KPX v ocircumflex -30 KPX v odieresis -30 KPX v ograve -30 KPX v ohungarumlaut -30 KPX v omacron -30 KPX v oslash -30 KPX v otilde -30 KPX v period -80 KPX w comma -40 KPX w o -20 KPX w oacute -20 KPX w ocircumflex -20 KPX w odieresis -20 KPX w ograve -20 KPX w ohungarumlaut -20 KPX w omacron -20 KPX w oslash -20 KPX w otilde -20 KPX w period -40 KPX x e -10 KPX x eacute -10 KPX x ecaron -10 KPX x ecircumflex -10 KPX x edieresis -10 KPX x edotaccent -10 KPX x egrave -10 KPX x emacron -10 KPX x eogonek -10 KPX y a -30 KPX y aacute -30 KPX y abreve -30 KPX y acircumflex -30 KPX y adieresis -30 KPX y agrave -30 KPX y amacron -30 KPX y aogonek -30 KPX y aring -30 KPX y atilde -30 KPX y comma -80 KPX y e -10 KPX y eacute -10 KPX y ecaron -10 KPX y ecircumflex -10 KPX y edieresis -10 KPX y edotaccent -10 KPX y egrave -10 KPX y emacron -10 KPX y eogonek -10 KPX y o -25 KPX y oacute -25 KPX y ocircumflex -25 KPX y odieresis -25 KPX y ograve -25 KPX y ohungarumlaut -25 KPX y omacron -25 KPX y oslash -25 KPX y otilde -25 KPX y period -80 KPX yacute a -30 KPX yacute aacute -30 KPX yacute abreve -30 KPX yacute acircumflex -30 KPX yacute adieresis -30 KPX yacute agrave -30 KPX yacute amacron -30 KPX yacute aogonek -30 KPX yacute aring -30 KPX yacute atilde -30 KPX yacute comma -80 KPX yacute e -10 KPX yacute eacute -10 KPX yacute ecaron -10 KPX yacute ecircumflex -10 KPX yacute edieresis -10 KPX yacute edotaccent -10 KPX yacute egrave -10 KPX yacute emacron -10 KPX yacute eogonek -10 KPX yacute o -25 KPX yacute oacute -25 KPX yacute ocircumflex -25 KPX yacute odieresis -25 KPX yacute ograve -25 KPX yacute ohungarumlaut -25 KPX yacute omacron -25 KPX yacute oslash -25 KPX yacute otilde -25 KPX yacute period -80 KPX ydieresis a -30 KPX ydieresis aacute -30 KPX ydieresis abreve -30 KPX ydieresis acircumflex -30 KPX ydieresis adieresis -30 KPX ydieresis agrave -30 KPX ydieresis amacron -30 KPX ydieresis aogonek -30 KPX ydieresis aring -30 KPX ydieresis atilde -30 KPX ydieresis comma -80 KPX ydieresis e -10 KPX ydieresis eacute -10 KPX ydieresis ecaron -10 KPX ydieresis ecircumflex -10 KPX ydieresis edieresis -10 KPX ydieresis edotaccent -10 KPX ydieresis egrave -10 KPX ydieresis emacron -10 KPX ydieresis eogonek -10 KPX ydieresis o -25 KPX ydieresis oacute -25 KPX ydieresis ocircumflex -25 KPX ydieresis odieresis -25 KPX ydieresis ograve -25 KPX ydieresis ohungarumlaut -25 KPX ydieresis omacron -25 KPX ydieresis oslash -25 KPX ydieresis otilde -25 KPX ydieresis period -80 KPX z e 10 KPX z eacute 10 KPX z ecaron 10 KPX z ecircumflex 10 KPX z edieresis 10 KPX z edotaccent 10 KPX z egrave 10 KPX z emacron 10 KPX z eogonek 10 KPX zacute e 10 KPX zacute eacute 10 KPX zacute ecaron 10 KPX zacute ecircumflex 10 KPX zacute edieresis 10 KPX zacute edotaccent 10 KPX zacute egrave 10 KPX zacute emacron 10 KPX zacute eogonek 10 KPX zcaron e 10 KPX zcaron eacute 10 KPX zcaron ecaron 10 KPX zcaron ecircumflex 10 KPX zcaron edieresis 10 KPX zcaron edotaccent 10 KPX zcaron egrave 10 KPX zcaron emacron 10 KPX zcaron eogonek 10 KPX zdotaccent e 10 KPX zdotaccent eacute 10 KPX zdotaccent ecaron 10 KPX zdotaccent ecircumflex 10 KPX zdotaccent edieresis 10 KPX zdotaccent edotaccent 10 KPX zdotaccent egrave 10 KPX zdotaccent emacron 10 KPX zdotaccent eogonek 10 EndKernPairs EndKernData EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Helvetica-Oblique.afm000066400000000000000000002272031320103431700305330ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 12:44:31 1997 Comment UniqueID 43055 Comment VMusage 14960 69346 FontName Helvetica-Oblique FullName Helvetica Oblique FamilyName Helvetica Weight Medium ItalicAngle -12 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -170 -225 1116 931 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme AdobeStandardEncoding CapHeight 718 XHeight 523 Ascender 718 Descender -207 StdHW 76 StdVW 88 StartCharMetrics 315 C 32 ; WX 278 ; N space ; B 0 0 0 0 ; C 33 ; WX 278 ; N exclam ; B 90 0 340 718 ; C 34 ; WX 355 ; N quotedbl ; B 168 463 438 718 ; C 35 ; WX 556 ; N numbersign ; B 73 0 631 688 ; C 36 ; WX 556 ; N dollar ; B 69 -115 617 775 ; C 37 ; WX 889 ; N percent ; B 147 -19 889 703 ; C 38 ; WX 667 ; N ampersand ; B 77 -15 647 718 ; C 39 ; WX 222 ; N quoteright ; B 151 463 310 718 ; C 40 ; WX 333 ; N parenleft ; B 108 -207 454 733 ; C 41 ; WX 333 ; N parenright ; B -9 -207 337 733 ; C 42 ; WX 389 ; N asterisk ; B 165 431 475 718 ; C 43 ; WX 584 ; N plus ; B 85 0 606 505 ; C 44 ; WX 278 ; N comma ; B 56 -147 214 106 ; C 45 ; WX 333 ; N hyphen ; B 93 232 357 322 ; C 46 ; WX 278 ; N period ; B 87 0 214 106 ; C 47 ; WX 278 ; N slash ; B -21 -19 452 737 ; C 48 ; WX 556 ; N zero ; B 93 -19 608 703 ; C 49 ; WX 556 ; N one ; B 207 0 508 703 ; C 50 ; WX 556 ; N two ; B 26 0 617 703 ; C 51 ; WX 556 ; N three ; B 75 -19 610 703 ; C 52 ; WX 556 ; N four ; B 61 0 576 703 ; C 53 ; WX 556 ; N five ; B 68 -19 621 688 ; C 54 ; WX 556 ; N six ; B 91 -19 615 703 ; C 55 ; WX 556 ; N seven ; B 137 0 669 688 ; C 56 ; WX 556 ; N eight ; B 74 -19 607 703 ; C 57 ; WX 556 ; N nine ; B 82 -19 609 703 ; C 58 ; WX 278 ; N colon ; B 87 0 301 516 ; C 59 ; WX 278 ; N semicolon ; B 56 -147 301 516 ; C 60 ; WX 584 ; N less ; B 94 11 641 495 ; C 61 ; WX 584 ; N equal ; B 63 115 628 390 ; C 62 ; WX 584 ; N greater ; B 50 11 597 495 ; C 63 ; WX 556 ; N question ; B 161 0 610 727 ; C 64 ; WX 1015 ; N at ; B 215 -19 965 737 ; C 65 ; WX 667 ; N A ; B 14 0 654 718 ; C 66 ; WX 667 ; N B ; B 74 0 712 718 ; C 67 ; WX 722 ; N C ; B 108 -19 782 737 ; C 68 ; WX 722 ; N D ; B 81 0 764 718 ; C 69 ; WX 667 ; N E ; B 86 0 762 718 ; C 70 ; WX 611 ; N F ; B 86 0 736 718 ; C 71 ; WX 778 ; N G ; B 111 -19 799 737 ; C 72 ; WX 722 ; N H ; B 77 0 799 718 ; C 73 ; WX 278 ; N I ; B 91 0 341 718 ; C 74 ; WX 500 ; N J ; B 47 -19 581 718 ; C 75 ; WX 667 ; N K ; B 76 0 808 718 ; C 76 ; WX 556 ; N L ; B 76 0 555 718 ; C 77 ; WX 833 ; N M ; B 73 0 914 718 ; C 78 ; WX 722 ; N N ; B 76 0 799 718 ; C 79 ; WX 778 ; N O ; B 105 -19 826 737 ; C 80 ; WX 667 ; N P ; B 86 0 737 718 ; C 81 ; WX 778 ; N Q ; B 105 -56 826 737 ; C 82 ; WX 722 ; N R ; B 88 0 773 718 ; C 83 ; WX 667 ; N S ; B 90 -19 713 737 ; C 84 ; WX 611 ; N T ; B 148 0 750 718 ; C 85 ; WX 722 ; N U ; B 123 -19 797 718 ; C 86 ; WX 667 ; N V ; B 173 0 800 718 ; C 87 ; WX 944 ; N W ; B 169 0 1081 718 ; C 88 ; WX 667 ; N X ; B 19 0 790 718 ; C 89 ; WX 667 ; N Y ; B 167 0 806 718 ; C 90 ; WX 611 ; N Z ; B 23 0 741 718 ; C 91 ; WX 278 ; N bracketleft ; B 21 -196 403 722 ; C 92 ; WX 278 ; N backslash ; B 140 -19 291 737 ; C 93 ; WX 278 ; N bracketright ; B -14 -196 368 722 ; C 94 ; WX 469 ; N asciicircum ; B 42 264 539 688 ; C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ; C 96 ; WX 222 ; N quoteleft ; B 165 470 323 725 ; C 97 ; WX 556 ; N a ; B 61 -15 559 538 ; C 98 ; WX 556 ; N b ; B 58 -15 584 718 ; C 99 ; WX 500 ; N c ; B 74 -15 553 538 ; C 100 ; WX 556 ; N d ; B 84 -15 652 718 ; C 101 ; WX 556 ; N e ; B 84 -15 578 538 ; C 102 ; WX 278 ; N f ; B 86 0 416 728 ; L i fi ; L l fl ; C 103 ; WX 556 ; N g ; B 42 -220 610 538 ; C 104 ; WX 556 ; N h ; B 65 0 573 718 ; C 105 ; WX 222 ; N i ; B 67 0 308 718 ; C 106 ; WX 222 ; N j ; B -60 -210 308 718 ; C 107 ; WX 500 ; N k ; B 67 0 600 718 ; C 108 ; WX 222 ; N l ; B 67 0 308 718 ; C 109 ; WX 833 ; N m ; B 65 0 852 538 ; C 110 ; WX 556 ; N n ; B 65 0 573 538 ; C 111 ; WX 556 ; N o ; B 83 -14 585 538 ; C 112 ; WX 556 ; N p ; B 14 -207 584 538 ; C 113 ; WX 556 ; N q ; B 84 -207 605 538 ; C 114 ; WX 333 ; N r ; B 77 0 446 538 ; C 115 ; WX 500 ; N s ; B 63 -15 529 538 ; C 116 ; WX 278 ; N t ; B 102 -7 368 669 ; C 117 ; WX 556 ; N u ; B 94 -15 600 523 ; C 118 ; WX 500 ; N v ; B 119 0 603 523 ; C 119 ; WX 722 ; N w ; B 125 0 820 523 ; C 120 ; WX 500 ; N x ; B 11 0 594 523 ; C 121 ; WX 500 ; N y ; B 15 -214 600 523 ; C 122 ; WX 500 ; N z ; B 31 0 571 523 ; C 123 ; WX 334 ; N braceleft ; B 92 -196 445 722 ; C 124 ; WX 260 ; N bar ; B 46 -225 332 775 ; C 125 ; WX 334 ; N braceright ; B 0 -196 354 722 ; C 126 ; WX 584 ; N asciitilde ; B 111 180 580 326 ; C 161 ; WX 333 ; N exclamdown ; B 77 -195 326 523 ; C 162 ; WX 556 ; N cent ; B 95 -115 584 623 ; C 163 ; WX 556 ; N sterling ; B 49 -16 634 718 ; C 164 ; WX 167 ; N fraction ; B -170 -19 482 703 ; C 165 ; WX 556 ; N yen ; B 81 0 699 688 ; C 166 ; WX 556 ; N florin ; B -52 -207 654 737 ; C 167 ; WX 556 ; N section ; B 76 -191 584 737 ; C 168 ; WX 556 ; N currency ; B 60 99 646 603 ; C 169 ; WX 191 ; N quotesingle ; B 157 463 285 718 ; C 170 ; WX 333 ; N quotedblleft ; B 138 470 461 725 ; C 171 ; WX 556 ; N guillemotleft ; B 146 108 554 446 ; C 172 ; WX 333 ; N guilsinglleft ; B 137 108 340 446 ; C 173 ; WX 333 ; N guilsinglright ; B 111 108 314 446 ; C 174 ; WX 500 ; N fi ; B 86 0 587 728 ; C 175 ; WX 500 ; N fl ; B 86 0 585 728 ; C 177 ; WX 556 ; N endash ; B 51 240 623 313 ; C 178 ; WX 556 ; N dagger ; B 135 -159 622 718 ; C 179 ; WX 556 ; N daggerdbl ; B 52 -159 623 718 ; C 180 ; WX 278 ; N periodcentered ; B 129 190 257 315 ; C 182 ; WX 537 ; N paragraph ; B 126 -173 650 718 ; C 183 ; WX 350 ; N bullet ; B 91 202 413 517 ; C 184 ; WX 222 ; N quotesinglbase ; B 21 -149 180 106 ; C 185 ; WX 333 ; N quotedblbase ; B -6 -149 318 106 ; C 186 ; WX 333 ; N quotedblright ; B 124 463 448 718 ; C 187 ; WX 556 ; N guillemotright ; B 120 108 528 446 ; C 188 ; WX 1000 ; N ellipsis ; B 115 0 908 106 ; C 189 ; WX 1000 ; N perthousand ; B 88 -19 1029 703 ; C 191 ; WX 611 ; N questiondown ; B 85 -201 534 525 ; C 193 ; WX 333 ; N grave ; B 170 593 337 734 ; C 194 ; WX 333 ; N acute ; B 248 593 475 734 ; C 195 ; WX 333 ; N circumflex ; B 147 593 438 734 ; C 196 ; WX 333 ; N tilde ; B 125 606 490 722 ; C 197 ; WX 333 ; N macron ; B 143 627 468 684 ; C 198 ; WX 333 ; N breve ; B 167 595 476 731 ; C 199 ; WX 333 ; N dotaccent ; B 249 604 362 706 ; C 200 ; WX 333 ; N dieresis ; B 168 604 443 706 ; C 202 ; WX 333 ; N ring ; B 214 572 402 756 ; C 203 ; WX 333 ; N cedilla ; B 2 -225 232 0 ; C 205 ; WX 333 ; N hungarumlaut ; B 157 593 565 734 ; C 206 ; WX 333 ; N ogonek ; B 43 -225 249 0 ; C 207 ; WX 333 ; N caron ; B 177 593 468 734 ; C 208 ; WX 1000 ; N emdash ; B 51 240 1067 313 ; C 225 ; WX 1000 ; N AE ; B 8 0 1097 718 ; C 227 ; WX 370 ; N ordfeminine ; B 127 405 449 737 ; C 232 ; WX 556 ; N Lslash ; B 41 0 555 718 ; C 233 ; WX 778 ; N Oslash ; B 43 -19 890 737 ; C 234 ; WX 1000 ; N OE ; B 98 -19 1116 737 ; C 235 ; WX 365 ; N ordmasculine ; B 141 405 468 737 ; C 241 ; WX 889 ; N ae ; B 61 -15 909 538 ; C 245 ; WX 278 ; N dotlessi ; B 95 0 294 523 ; C 248 ; WX 222 ; N lslash ; B 41 0 347 718 ; C 249 ; WX 611 ; N oslash ; B 29 -22 647 545 ; C 250 ; WX 944 ; N oe ; B 83 -15 964 538 ; C 251 ; WX 611 ; N germandbls ; B 67 -15 658 728 ; C -1 ; WX 278 ; N Idieresis ; B 91 0 458 901 ; C -1 ; WX 556 ; N eacute ; B 84 -15 587 734 ; C -1 ; WX 556 ; N abreve ; B 61 -15 578 731 ; C -1 ; WX 556 ; N uhungarumlaut ; B 94 -15 677 734 ; C -1 ; WX 556 ; N ecaron ; B 84 -15 580 734 ; C -1 ; WX 667 ; N Ydieresis ; B 167 0 806 901 ; C -1 ; WX 584 ; N divide ; B 85 -19 606 524 ; C -1 ; WX 667 ; N Yacute ; B 167 0 806 929 ; C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ; C -1 ; WX 556 ; N aacute ; B 61 -15 587 734 ; C -1 ; WX 722 ; N Ucircumflex ; B 123 -19 797 929 ; C -1 ; WX 500 ; N yacute ; B 15 -214 600 734 ; C -1 ; WX 500 ; N scommaaccent ; B 63 -225 529 538 ; C -1 ; WX 556 ; N ecircumflex ; B 84 -15 578 734 ; C -1 ; WX 722 ; N Uring ; B 123 -19 797 931 ; C -1 ; WX 722 ; N Udieresis ; B 123 -19 797 901 ; C -1 ; WX 556 ; N aogonek ; B 61 -220 559 538 ; C -1 ; WX 722 ; N Uacute ; B 123 -19 797 929 ; C -1 ; WX 556 ; N uogonek ; B 94 -225 600 523 ; C -1 ; WX 667 ; N Edieresis ; B 86 0 762 901 ; C -1 ; WX 722 ; N Dcroat ; B 69 0 764 718 ; C -1 ; WX 250 ; N commaaccent ; B 39 -225 172 -40 ; C -1 ; WX 737 ; N copyright ; B 54 -19 837 737 ; C -1 ; WX 667 ; N Emacron ; B 86 0 762 879 ; C -1 ; WX 500 ; N ccaron ; B 74 -15 553 734 ; C -1 ; WX 556 ; N aring ; B 61 -15 559 756 ; C -1 ; WX 722 ; N Ncommaaccent ; B 76 -225 799 718 ; C -1 ; WX 222 ; N lacute ; B 67 0 461 929 ; C -1 ; WX 556 ; N agrave ; B 61 -15 559 734 ; C -1 ; WX 611 ; N Tcommaaccent ; B 148 -225 750 718 ; C -1 ; WX 722 ; N Cacute ; B 108 -19 782 929 ; C -1 ; WX 556 ; N atilde ; B 61 -15 592 722 ; C -1 ; WX 667 ; N Edotaccent ; B 86 0 762 901 ; C -1 ; WX 500 ; N scaron ; B 63 -15 552 734 ; C -1 ; WX 500 ; N scedilla ; B 63 -225 529 538 ; C -1 ; WX 278 ; N iacute ; B 95 0 448 734 ; C -1 ; WX 471 ; N lozenge ; B 88 0 540 728 ; C -1 ; WX 722 ; N Rcaron ; B 88 0 773 929 ; C -1 ; WX 778 ; N Gcommaaccent ; B 111 -225 799 737 ; C -1 ; WX 556 ; N ucircumflex ; B 94 -15 600 734 ; C -1 ; WX 556 ; N acircumflex ; B 61 -15 559 734 ; C -1 ; WX 667 ; N Amacron ; B 14 0 677 879 ; C -1 ; WX 333 ; N rcaron ; B 77 0 508 734 ; C -1 ; WX 500 ; N ccedilla ; B 74 -225 553 538 ; C -1 ; WX 611 ; N Zdotaccent ; B 23 0 741 901 ; C -1 ; WX 667 ; N Thorn ; B 86 0 712 718 ; C -1 ; WX 778 ; N Omacron ; B 105 -19 826 879 ; C -1 ; WX 722 ; N Racute ; B 88 0 773 929 ; C -1 ; WX 667 ; N Sacute ; B 90 -19 713 929 ; C -1 ; WX 643 ; N dcaron ; B 84 -15 808 718 ; C -1 ; WX 722 ; N Umacron ; B 123 -19 797 879 ; C -1 ; WX 556 ; N uring ; B 94 -15 600 756 ; C -1 ; WX 333 ; N threesuperior ; B 90 270 436 703 ; C -1 ; WX 778 ; N Ograve ; B 105 -19 826 929 ; C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ; C -1 ; WX 667 ; N Abreve ; B 14 0 685 926 ; C -1 ; WX 584 ; N multiply ; B 50 0 642 506 ; C -1 ; WX 556 ; N uacute ; B 94 -15 600 734 ; C -1 ; WX 611 ; N Tcaron ; B 148 0 750 929 ; C -1 ; WX 476 ; N partialdiff ; B 41 -38 550 714 ; C -1 ; WX 500 ; N ydieresis ; B 15 -214 600 706 ; C -1 ; WX 722 ; N Nacute ; B 76 0 799 929 ; C -1 ; WX 278 ; N icircumflex ; B 95 0 411 734 ; C -1 ; WX 667 ; N Ecircumflex ; B 86 0 762 929 ; C -1 ; WX 556 ; N adieresis ; B 61 -15 559 706 ; C -1 ; WX 556 ; N edieresis ; B 84 -15 578 706 ; C -1 ; WX 500 ; N cacute ; B 74 -15 559 734 ; C -1 ; WX 556 ; N nacute ; B 65 0 587 734 ; C -1 ; WX 556 ; N umacron ; B 94 -15 600 684 ; C -1 ; WX 722 ; N Ncaron ; B 76 0 799 929 ; C -1 ; WX 278 ; N Iacute ; B 91 0 489 929 ; C -1 ; WX 584 ; N plusminus ; B 39 0 618 506 ; C -1 ; WX 260 ; N brokenbar ; B 62 -150 316 700 ; C -1 ; WX 737 ; N registered ; B 54 -19 837 737 ; C -1 ; WX 778 ; N Gbreve ; B 111 -19 799 926 ; C -1 ; WX 278 ; N Idotaccent ; B 91 0 377 901 ; C -1 ; WX 600 ; N summation ; B 15 -10 671 706 ; C -1 ; WX 667 ; N Egrave ; B 86 0 762 929 ; C -1 ; WX 333 ; N racute ; B 77 0 475 734 ; C -1 ; WX 556 ; N omacron ; B 83 -14 585 684 ; C -1 ; WX 611 ; N Zacute ; B 23 0 741 929 ; C -1 ; WX 611 ; N Zcaron ; B 23 0 741 929 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 620 674 ; C -1 ; WX 722 ; N Eth ; B 69 0 764 718 ; C -1 ; WX 722 ; N Ccedilla ; B 108 -225 782 737 ; C -1 ; WX 222 ; N lcommaaccent ; B 25 -225 308 718 ; C -1 ; WX 317 ; N tcaron ; B 102 -7 501 808 ; C -1 ; WX 556 ; N eogonek ; B 84 -225 578 538 ; C -1 ; WX 722 ; N Uogonek ; B 123 -225 797 718 ; C -1 ; WX 667 ; N Aacute ; B 14 0 683 929 ; C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ; C -1 ; WX 556 ; N egrave ; B 84 -15 578 734 ; C -1 ; WX 500 ; N zacute ; B 31 0 571 734 ; C -1 ; WX 222 ; N iogonek ; B -61 -225 308 718 ; C -1 ; WX 778 ; N Oacute ; B 105 -19 826 929 ; C -1 ; WX 556 ; N oacute ; B 83 -14 587 734 ; C -1 ; WX 556 ; N amacron ; B 61 -15 580 684 ; C -1 ; WX 500 ; N sacute ; B 63 -15 559 734 ; C -1 ; WX 278 ; N idieresis ; B 95 0 416 706 ; C -1 ; WX 778 ; N Ocircumflex ; B 105 -19 826 929 ; C -1 ; WX 722 ; N Ugrave ; B 123 -19 797 929 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C -1 ; WX 556 ; N thorn ; B 14 -207 584 718 ; C -1 ; WX 333 ; N twosuperior ; B 64 281 449 703 ; C -1 ; WX 778 ; N Odieresis ; B 105 -19 826 901 ; C -1 ; WX 556 ; N mu ; B 24 -207 600 523 ; C -1 ; WX 278 ; N igrave ; B 95 0 310 734 ; C -1 ; WX 556 ; N ohungarumlaut ; B 83 -14 677 734 ; C -1 ; WX 667 ; N Eogonek ; B 86 -220 762 718 ; C -1 ; WX 556 ; N dcroat ; B 84 -15 689 718 ; C -1 ; WX 834 ; N threequarters ; B 130 -19 861 703 ; C -1 ; WX 667 ; N Scedilla ; B 90 -225 713 737 ; C -1 ; WX 299 ; N lcaron ; B 67 0 464 718 ; C -1 ; WX 667 ; N Kcommaaccent ; B 76 -225 808 718 ; C -1 ; WX 556 ; N Lacute ; B 76 0 555 929 ; C -1 ; WX 1000 ; N trademark ; B 186 306 1056 718 ; C -1 ; WX 556 ; N edotaccent ; B 84 -15 578 706 ; C -1 ; WX 278 ; N Igrave ; B 91 0 351 929 ; C -1 ; WX 278 ; N Imacron ; B 91 0 483 879 ; C -1 ; WX 556 ; N Lcaron ; B 76 0 570 718 ; C -1 ; WX 834 ; N onehalf ; B 114 -19 839 703 ; C -1 ; WX 549 ; N lessequal ; B 26 0 666 674 ; C -1 ; WX 556 ; N ocircumflex ; B 83 -14 585 734 ; C -1 ; WX 556 ; N ntilde ; B 65 0 592 722 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 123 -19 801 929 ; C -1 ; WX 667 ; N Eacute ; B 86 0 762 929 ; C -1 ; WX 556 ; N emacron ; B 84 -15 580 684 ; C -1 ; WX 556 ; N gbreve ; B 42 -220 610 731 ; C -1 ; WX 834 ; N onequarter ; B 150 -19 802 703 ; C -1 ; WX 667 ; N Scaron ; B 90 -19 713 929 ; C -1 ; WX 667 ; N Scommaaccent ; B 90 -225 713 737 ; C -1 ; WX 778 ; N Ohungarumlaut ; B 105 -19 829 929 ; C -1 ; WX 400 ; N degree ; B 169 411 468 703 ; C -1 ; WX 556 ; N ograve ; B 83 -14 585 734 ; C -1 ; WX 722 ; N Ccaron ; B 108 -19 782 929 ; C -1 ; WX 556 ; N ugrave ; B 94 -15 600 734 ; C -1 ; WX 453 ; N radical ; B 79 -80 617 762 ; C -1 ; WX 722 ; N Dcaron ; B 81 0 764 929 ; C -1 ; WX 333 ; N rcommaaccent ; B 30 -225 446 538 ; C -1 ; WX 722 ; N Ntilde ; B 76 0 799 917 ; C -1 ; WX 556 ; N otilde ; B 83 -14 602 722 ; C -1 ; WX 722 ; N Rcommaaccent ; B 88 -225 773 718 ; C -1 ; WX 556 ; N Lcommaaccent ; B 76 -225 555 718 ; C -1 ; WX 667 ; N Atilde ; B 14 0 699 917 ; C -1 ; WX 667 ; N Aogonek ; B 14 -225 654 718 ; C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ; C -1 ; WX 778 ; N Otilde ; B 105 -19 826 917 ; C -1 ; WX 500 ; N zdotaccent ; B 31 0 571 706 ; C -1 ; WX 667 ; N Ecaron ; B 86 0 762 929 ; C -1 ; WX 278 ; N Iogonek ; B -33 -225 341 718 ; C -1 ; WX 500 ; N kcommaaccent ; B 67 -225 600 718 ; C -1 ; WX 584 ; N minus ; B 85 216 606 289 ; C -1 ; WX 278 ; N Icircumflex ; B 91 0 452 929 ; C -1 ; WX 556 ; N ncaron ; B 65 0 580 734 ; C -1 ; WX 278 ; N tcommaaccent ; B 63 -225 368 669 ; C -1 ; WX 584 ; N logicalnot ; B 106 108 628 390 ; C -1 ; WX 556 ; N odieresis ; B 83 -14 585 706 ; C -1 ; WX 556 ; N udieresis ; B 94 -15 600 706 ; C -1 ; WX 549 ; N notequal ; B 34 -35 623 551 ; C -1 ; WX 556 ; N gcommaaccent ; B 42 -220 610 822 ; C -1 ; WX 556 ; N eth ; B 81 -15 617 737 ; C -1 ; WX 500 ; N zcaron ; B 31 0 571 734 ; C -1 ; WX 556 ; N ncommaaccent ; B 65 -225 573 538 ; C -1 ; WX 333 ; N onesuperior ; B 166 281 371 703 ; C -1 ; WX 278 ; N imacron ; B 95 0 417 684 ; C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2705 KPX A C -30 KPX A Cacute -30 KPX A Ccaron -30 KPX A Ccedilla -30 KPX A G -30 KPX A Gbreve -30 KPX A Gcommaaccent -30 KPX A O -30 KPX A Oacute -30 KPX A Ocircumflex -30 KPX A Odieresis -30 KPX A Ograve -30 KPX A Ohungarumlaut -30 KPX A Omacron -30 KPX A Oslash -30 KPX A Otilde -30 KPX A Q -30 KPX A T -120 KPX A Tcaron -120 KPX A Tcommaaccent -120 KPX A U -50 KPX A Uacute -50 KPX A Ucircumflex -50 KPX A Udieresis -50 KPX A Ugrave -50 KPX A Uhungarumlaut -50 KPX A Umacron -50 KPX A Uogonek -50 KPX A Uring -50 KPX A V -70 KPX A W -50 KPX A Y -100 KPX A Yacute -100 KPX A Ydieresis -100 KPX A u -30 KPX A uacute -30 KPX A ucircumflex -30 KPX A udieresis -30 KPX A ugrave -30 KPX A uhungarumlaut -30 KPX A umacron -30 KPX A uogonek -30 KPX A uring -30 KPX A v -40 KPX A w -40 KPX A y -40 KPX A yacute -40 KPX A ydieresis -40 KPX Aacute C -30 KPX Aacute Cacute -30 KPX Aacute Ccaron -30 KPX Aacute Ccedilla -30 KPX Aacute G -30 KPX Aacute Gbreve -30 KPX Aacute Gcommaaccent -30 KPX Aacute O -30 KPX Aacute Oacute -30 KPX Aacute Ocircumflex -30 KPX Aacute Odieresis -30 KPX Aacute Ograve -30 KPX Aacute Ohungarumlaut -30 KPX Aacute Omacron -30 KPX Aacute Oslash -30 KPX Aacute Otilde -30 KPX Aacute Q -30 KPX Aacute T -120 KPX Aacute Tcaron -120 KPX Aacute Tcommaaccent -120 KPX Aacute U -50 KPX Aacute Uacute -50 KPX Aacute Ucircumflex -50 KPX Aacute Udieresis -50 KPX Aacute Ugrave -50 KPX Aacute Uhungarumlaut -50 KPX Aacute Umacron -50 KPX Aacute Uogonek -50 KPX Aacute Uring -50 KPX Aacute V -70 KPX Aacute W -50 KPX Aacute Y -100 KPX Aacute Yacute -100 KPX Aacute Ydieresis -100 KPX Aacute u -30 KPX Aacute uacute -30 KPX Aacute ucircumflex -30 KPX Aacute udieresis -30 KPX Aacute ugrave -30 KPX Aacute uhungarumlaut -30 KPX Aacute umacron -30 KPX Aacute uogonek -30 KPX Aacute uring -30 KPX Aacute v -40 KPX Aacute w -40 KPX Aacute y -40 KPX Aacute yacute -40 KPX Aacute ydieresis -40 KPX Abreve C -30 KPX Abreve Cacute -30 KPX Abreve Ccaron -30 KPX Abreve Ccedilla -30 KPX Abreve G -30 KPX Abreve Gbreve -30 KPX Abreve Gcommaaccent -30 KPX Abreve O -30 KPX Abreve Oacute -30 KPX Abreve Ocircumflex -30 KPX Abreve Odieresis -30 KPX Abreve Ograve -30 KPX Abreve Ohungarumlaut -30 KPX Abreve Omacron -30 KPX Abreve Oslash -30 KPX Abreve Otilde -30 KPX Abreve Q -30 KPX Abreve T -120 KPX Abreve Tcaron -120 KPX Abreve Tcommaaccent -120 KPX Abreve U -50 KPX Abreve Uacute -50 KPX Abreve Ucircumflex -50 KPX Abreve Udieresis -50 KPX Abreve Ugrave -50 KPX Abreve Uhungarumlaut -50 KPX Abreve Umacron -50 KPX Abreve Uogonek -50 KPX Abreve Uring -50 KPX Abreve V -70 KPX Abreve W -50 KPX Abreve Y -100 KPX Abreve Yacute -100 KPX Abreve Ydieresis -100 KPX Abreve u -30 KPX Abreve uacute -30 KPX Abreve ucircumflex -30 KPX Abreve udieresis -30 KPX Abreve ugrave -30 KPX Abreve uhungarumlaut -30 KPX Abreve umacron -30 KPX Abreve uogonek -30 KPX Abreve uring -30 KPX Abreve v -40 KPX Abreve w -40 KPX Abreve y -40 KPX Abreve yacute -40 KPX Abreve ydieresis -40 KPX Acircumflex C -30 KPX Acircumflex Cacute -30 KPX Acircumflex Ccaron -30 KPX Acircumflex Ccedilla -30 KPX Acircumflex G -30 KPX Acircumflex Gbreve -30 KPX Acircumflex Gcommaaccent -30 KPX Acircumflex O -30 KPX Acircumflex Oacute -30 KPX Acircumflex Ocircumflex -30 KPX Acircumflex Odieresis -30 KPX Acircumflex Ograve -30 KPX Acircumflex Ohungarumlaut -30 KPX Acircumflex Omacron -30 KPX Acircumflex Oslash -30 KPX Acircumflex Otilde -30 KPX Acircumflex Q -30 KPX Acircumflex T -120 KPX Acircumflex Tcaron -120 KPX Acircumflex Tcommaaccent -120 KPX Acircumflex U -50 KPX Acircumflex Uacute -50 KPX Acircumflex Ucircumflex -50 KPX Acircumflex Udieresis -50 KPX Acircumflex Ugrave -50 KPX Acircumflex Uhungarumlaut -50 KPX Acircumflex Umacron -50 KPX Acircumflex Uogonek -50 KPX Acircumflex Uring -50 KPX Acircumflex V -70 KPX Acircumflex W -50 KPX Acircumflex Y -100 KPX Acircumflex Yacute -100 KPX Acircumflex Ydieresis -100 KPX Acircumflex u -30 KPX Acircumflex uacute -30 KPX Acircumflex ucircumflex -30 KPX Acircumflex udieresis -30 KPX Acircumflex ugrave -30 KPX Acircumflex uhungarumlaut -30 KPX Acircumflex umacron -30 KPX Acircumflex uogonek -30 KPX Acircumflex uring -30 KPX Acircumflex v -40 KPX Acircumflex w -40 KPX Acircumflex y -40 KPX Acircumflex yacute -40 KPX Acircumflex ydieresis -40 KPX Adieresis C -30 KPX Adieresis Cacute -30 KPX Adieresis Ccaron -30 KPX Adieresis Ccedilla -30 KPX Adieresis G -30 KPX Adieresis Gbreve -30 KPX Adieresis Gcommaaccent -30 KPX Adieresis O -30 KPX Adieresis Oacute -30 KPX Adieresis Ocircumflex -30 KPX Adieresis Odieresis -30 KPX Adieresis Ograve -30 KPX Adieresis Ohungarumlaut -30 KPX Adieresis Omacron -30 KPX Adieresis Oslash -30 KPX Adieresis Otilde -30 KPX Adieresis Q -30 KPX Adieresis T -120 KPX Adieresis Tcaron -120 KPX Adieresis Tcommaaccent -120 KPX Adieresis U -50 KPX Adieresis Uacute -50 KPX Adieresis Ucircumflex -50 KPX Adieresis Udieresis -50 KPX Adieresis Ugrave -50 KPX Adieresis Uhungarumlaut -50 KPX Adieresis Umacron -50 KPX Adieresis Uogonek -50 KPX Adieresis Uring -50 KPX Adieresis V -70 KPX Adieresis W -50 KPX Adieresis Y -100 KPX Adieresis Yacute -100 KPX Adieresis Ydieresis -100 KPX Adieresis u -30 KPX Adieresis uacute -30 KPX Adieresis ucircumflex -30 KPX Adieresis udieresis -30 KPX Adieresis ugrave -30 KPX Adieresis uhungarumlaut -30 KPX Adieresis umacron -30 KPX Adieresis uogonek -30 KPX Adieresis uring -30 KPX Adieresis v -40 KPX Adieresis w -40 KPX Adieresis y -40 KPX Adieresis yacute -40 KPX Adieresis ydieresis -40 KPX Agrave C -30 KPX Agrave Cacute -30 KPX Agrave Ccaron -30 KPX Agrave Ccedilla -30 KPX Agrave G -30 KPX Agrave Gbreve -30 KPX Agrave Gcommaaccent -30 KPX Agrave O -30 KPX Agrave Oacute -30 KPX Agrave Ocircumflex -30 KPX Agrave Odieresis -30 KPX Agrave Ograve -30 KPX Agrave Ohungarumlaut -30 KPX Agrave Omacron -30 KPX Agrave Oslash -30 KPX Agrave Otilde -30 KPX Agrave Q -30 KPX Agrave T -120 KPX Agrave Tcaron -120 KPX Agrave Tcommaaccent -120 KPX Agrave U -50 KPX Agrave Uacute -50 KPX Agrave Ucircumflex -50 KPX Agrave Udieresis -50 KPX Agrave Ugrave -50 KPX Agrave Uhungarumlaut -50 KPX Agrave Umacron -50 KPX Agrave Uogonek -50 KPX Agrave Uring -50 KPX Agrave V -70 KPX Agrave W -50 KPX Agrave Y -100 KPX Agrave Yacute -100 KPX Agrave Ydieresis -100 KPX Agrave u -30 KPX Agrave uacute -30 KPX Agrave ucircumflex -30 KPX Agrave udieresis -30 KPX Agrave ugrave -30 KPX Agrave uhungarumlaut -30 KPX Agrave umacron -30 KPX Agrave uogonek -30 KPX Agrave uring -30 KPX Agrave v -40 KPX Agrave w -40 KPX Agrave y -40 KPX Agrave yacute -40 KPX Agrave ydieresis -40 KPX Amacron C -30 KPX Amacron Cacute -30 KPX Amacron Ccaron -30 KPX Amacron Ccedilla -30 KPX Amacron G -30 KPX Amacron Gbreve -30 KPX Amacron Gcommaaccent -30 KPX Amacron O -30 KPX Amacron Oacute -30 KPX Amacron Ocircumflex -30 KPX Amacron Odieresis -30 KPX Amacron Ograve -30 KPX Amacron Ohungarumlaut -30 KPX Amacron Omacron -30 KPX Amacron Oslash -30 KPX Amacron Otilde -30 KPX Amacron Q -30 KPX Amacron T -120 KPX Amacron Tcaron -120 KPX Amacron Tcommaaccent -120 KPX Amacron U -50 KPX Amacron Uacute -50 KPX Amacron Ucircumflex -50 KPX Amacron Udieresis -50 KPX Amacron Ugrave -50 KPX Amacron Uhungarumlaut -50 KPX Amacron Umacron -50 KPX Amacron Uogonek -50 KPX Amacron Uring -50 KPX Amacron V -70 KPX Amacron W -50 KPX Amacron Y -100 KPX Amacron Yacute -100 KPX Amacron Ydieresis -100 KPX Amacron u -30 KPX Amacron uacute -30 KPX Amacron ucircumflex -30 KPX Amacron udieresis -30 KPX Amacron ugrave -30 KPX Amacron uhungarumlaut -30 KPX Amacron umacron -30 KPX Amacron uogonek -30 KPX Amacron uring -30 KPX Amacron v -40 KPX Amacron w -40 KPX Amacron y -40 KPX Amacron yacute -40 KPX Amacron ydieresis -40 KPX Aogonek C -30 KPX Aogonek Cacute -30 KPX Aogonek Ccaron -30 KPX Aogonek Ccedilla -30 KPX Aogonek G -30 KPX Aogonek Gbreve -30 KPX Aogonek Gcommaaccent -30 KPX Aogonek O -30 KPX Aogonek Oacute -30 KPX Aogonek Ocircumflex -30 KPX Aogonek Odieresis -30 KPX Aogonek Ograve -30 KPX Aogonek Ohungarumlaut -30 KPX Aogonek Omacron -30 KPX Aogonek Oslash -30 KPX Aogonek Otilde -30 KPX Aogonek Q -30 KPX Aogonek T -120 KPX Aogonek Tcaron -120 KPX Aogonek Tcommaaccent -120 KPX Aogonek U -50 KPX Aogonek Uacute -50 KPX Aogonek Ucircumflex -50 KPX Aogonek Udieresis -50 KPX Aogonek Ugrave -50 KPX Aogonek Uhungarumlaut -50 KPX Aogonek Umacron -50 KPX Aogonek Uogonek -50 KPX Aogonek Uring -50 KPX Aogonek V -70 KPX Aogonek W -50 KPX Aogonek Y -100 KPX Aogonek Yacute -100 KPX Aogonek Ydieresis -100 KPX Aogonek u -30 KPX Aogonek uacute -30 KPX Aogonek ucircumflex -30 KPX Aogonek udieresis -30 KPX Aogonek ugrave -30 KPX Aogonek uhungarumlaut -30 KPX Aogonek umacron -30 KPX Aogonek uogonek -30 KPX Aogonek uring -30 KPX Aogonek v -40 KPX Aogonek w -40 KPX Aogonek y -40 KPX Aogonek yacute -40 KPX Aogonek ydieresis -40 KPX Aring C -30 KPX Aring Cacute -30 KPX Aring Ccaron -30 KPX Aring Ccedilla -30 KPX Aring G -30 KPX Aring Gbreve -30 KPX Aring Gcommaaccent -30 KPX Aring O -30 KPX Aring Oacute -30 KPX Aring Ocircumflex -30 KPX Aring Odieresis -30 KPX Aring Ograve -30 KPX Aring Ohungarumlaut -30 KPX Aring Omacron -30 KPX Aring Oslash -30 KPX Aring Otilde -30 KPX Aring Q -30 KPX Aring T -120 KPX Aring Tcaron -120 KPX Aring Tcommaaccent -120 KPX Aring U -50 KPX Aring Uacute -50 KPX Aring Ucircumflex -50 KPX Aring Udieresis -50 KPX Aring Ugrave -50 KPX Aring Uhungarumlaut -50 KPX Aring Umacron -50 KPX Aring Uogonek -50 KPX Aring Uring -50 KPX Aring V -70 KPX Aring W -50 KPX Aring Y -100 KPX Aring Yacute -100 KPX Aring Ydieresis -100 KPX Aring u -30 KPX Aring uacute -30 KPX Aring ucircumflex -30 KPX Aring udieresis -30 KPX Aring ugrave -30 KPX Aring uhungarumlaut -30 KPX Aring umacron -30 KPX Aring uogonek -30 KPX Aring uring -30 KPX Aring v -40 KPX Aring w -40 KPX Aring y -40 KPX Aring yacute -40 KPX Aring ydieresis -40 KPX Atilde C -30 KPX Atilde Cacute -30 KPX Atilde Ccaron -30 KPX Atilde Ccedilla -30 KPX Atilde G -30 KPX Atilde Gbreve -30 KPX Atilde Gcommaaccent -30 KPX Atilde O -30 KPX Atilde Oacute -30 KPX Atilde Ocircumflex -30 KPX Atilde Odieresis -30 KPX Atilde Ograve -30 KPX Atilde Ohungarumlaut -30 KPX Atilde Omacron -30 KPX Atilde Oslash -30 KPX Atilde Otilde -30 KPX Atilde Q -30 KPX Atilde T -120 KPX Atilde Tcaron -120 KPX Atilde Tcommaaccent -120 KPX Atilde U -50 KPX Atilde Uacute -50 KPX Atilde Ucircumflex -50 KPX Atilde Udieresis -50 KPX Atilde Ugrave -50 KPX Atilde Uhungarumlaut -50 KPX Atilde Umacron -50 KPX Atilde Uogonek -50 KPX Atilde Uring -50 KPX Atilde V -70 KPX Atilde W -50 KPX Atilde Y -100 KPX Atilde Yacute -100 KPX Atilde Ydieresis -100 KPX Atilde u -30 KPX Atilde uacute -30 KPX Atilde ucircumflex -30 KPX Atilde udieresis -30 KPX Atilde ugrave -30 KPX Atilde uhungarumlaut -30 KPX Atilde umacron -30 KPX Atilde uogonek -30 KPX Atilde uring -30 KPX Atilde v -40 KPX Atilde w -40 KPX Atilde y -40 KPX Atilde yacute -40 KPX Atilde ydieresis -40 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX B comma -20 KPX B period -20 KPX C comma -30 KPX C period -30 KPX Cacute comma -30 KPX Cacute period -30 KPX Ccaron comma -30 KPX Ccaron period -30 KPX Ccedilla comma -30 KPX Ccedilla period -30 KPX D A -40 KPX D Aacute -40 KPX D Abreve -40 KPX D Acircumflex -40 KPX D Adieresis -40 KPX D Agrave -40 KPX D Amacron -40 KPX D Aogonek -40 KPX D Aring -40 KPX D Atilde -40 KPX D V -70 KPX D W -40 KPX D Y -90 KPX D Yacute -90 KPX D Ydieresis -90 KPX D comma -70 KPX D period -70 KPX Dcaron A -40 KPX Dcaron Aacute -40 KPX Dcaron Abreve -40 KPX Dcaron Acircumflex -40 KPX Dcaron Adieresis -40 KPX Dcaron Agrave -40 KPX Dcaron Amacron -40 KPX Dcaron Aogonek -40 KPX Dcaron Aring -40 KPX Dcaron Atilde -40 KPX Dcaron V -70 KPX Dcaron W -40 KPX Dcaron Y -90 KPX Dcaron Yacute -90 KPX Dcaron Ydieresis -90 KPX Dcaron comma -70 KPX Dcaron period -70 KPX Dcroat A -40 KPX Dcroat Aacute -40 KPX Dcroat Abreve -40 KPX Dcroat Acircumflex -40 KPX Dcroat Adieresis -40 KPX Dcroat Agrave -40 KPX Dcroat Amacron -40 KPX Dcroat Aogonek -40 KPX Dcroat Aring -40 KPX Dcroat Atilde -40 KPX Dcroat V -70 KPX Dcroat W -40 KPX Dcroat Y -90 KPX Dcroat Yacute -90 KPX Dcroat Ydieresis -90 KPX Dcroat comma -70 KPX Dcroat period -70 KPX F A -80 KPX F Aacute -80 KPX F Abreve -80 KPX F Acircumflex -80 KPX F Adieresis -80 KPX F Agrave -80 KPX F Amacron -80 KPX F Aogonek -80 KPX F Aring -80 KPX F Atilde -80 KPX F a -50 KPX F aacute -50 KPX F abreve -50 KPX F acircumflex -50 KPX F adieresis -50 KPX F agrave -50 KPX F amacron -50 KPX F aogonek -50 KPX F aring -50 KPX F atilde -50 KPX F comma -150 KPX F e -30 KPX F eacute -30 KPX F ecaron -30 KPX F ecircumflex -30 KPX F edieresis -30 KPX F edotaccent -30 KPX F egrave -30 KPX F emacron -30 KPX F eogonek -30 KPX F o -30 KPX F oacute -30 KPX F ocircumflex -30 KPX F odieresis -30 KPX F ograve -30 KPX F ohungarumlaut -30 KPX F omacron -30 KPX F oslash -30 KPX F otilde -30 KPX F period -150 KPX F r -45 KPX F racute -45 KPX F rcaron -45 KPX F rcommaaccent -45 KPX J A -20 KPX J Aacute -20 KPX J Abreve -20 KPX J Acircumflex -20 KPX J Adieresis -20 KPX J Agrave -20 KPX J Amacron -20 KPX J Aogonek -20 KPX J Aring -20 KPX J Atilde -20 KPX J a -20 KPX J aacute -20 KPX J abreve -20 KPX J acircumflex -20 KPX J adieresis -20 KPX J agrave -20 KPX J amacron -20 KPX J aogonek -20 KPX J aring -20 KPX J atilde -20 KPX J comma -30 KPX J period -30 KPX J u -20 KPX J uacute -20 KPX J ucircumflex -20 KPX J udieresis -20 KPX J ugrave -20 KPX J uhungarumlaut -20 KPX J umacron -20 KPX J uogonek -20 KPX J uring -20 KPX K O -50 KPX K Oacute -50 KPX K Ocircumflex -50 KPX K Odieresis -50 KPX K Ograve -50 KPX K Ohungarumlaut -50 KPX K Omacron -50 KPX K Oslash -50 KPX K Otilde -50 KPX K e -40 KPX K eacute -40 KPX K ecaron -40 KPX K ecircumflex -40 KPX K edieresis -40 KPX K edotaccent -40 KPX K egrave -40 KPX K emacron -40 KPX K eogonek -40 KPX K o -40 KPX K oacute -40 KPX K ocircumflex -40 KPX K odieresis -40 KPX K ograve -40 KPX K ohungarumlaut -40 KPX K omacron -40 KPX K oslash -40 KPX K otilde -40 KPX K u -30 KPX K uacute -30 KPX K ucircumflex -30 KPX K udieresis -30 KPX K ugrave -30 KPX K uhungarumlaut -30 KPX K umacron -30 KPX K uogonek -30 KPX K uring -30 KPX K y -50 KPX K yacute -50 KPX K ydieresis -50 KPX Kcommaaccent O -50 KPX Kcommaaccent Oacute -50 KPX Kcommaaccent Ocircumflex -50 KPX Kcommaaccent Odieresis -50 KPX Kcommaaccent Ograve -50 KPX Kcommaaccent Ohungarumlaut -50 KPX Kcommaaccent Omacron -50 KPX Kcommaaccent Oslash -50 KPX Kcommaaccent Otilde -50 KPX Kcommaaccent e -40 KPX Kcommaaccent eacute -40 KPX Kcommaaccent ecaron -40 KPX Kcommaaccent ecircumflex -40 KPX Kcommaaccent edieresis -40 KPX Kcommaaccent edotaccent -40 KPX Kcommaaccent egrave -40 KPX Kcommaaccent emacron -40 KPX Kcommaaccent eogonek -40 KPX Kcommaaccent o -40 KPX Kcommaaccent oacute -40 KPX Kcommaaccent ocircumflex -40 KPX Kcommaaccent odieresis -40 KPX Kcommaaccent ograve -40 KPX Kcommaaccent ohungarumlaut -40 KPX Kcommaaccent omacron -40 KPX Kcommaaccent oslash -40 KPX Kcommaaccent otilde -40 KPX Kcommaaccent u -30 KPX Kcommaaccent uacute -30 KPX Kcommaaccent ucircumflex -30 KPX Kcommaaccent udieresis -30 KPX Kcommaaccent ugrave -30 KPX Kcommaaccent uhungarumlaut -30 KPX Kcommaaccent umacron -30 KPX Kcommaaccent uogonek -30 KPX Kcommaaccent uring -30 KPX Kcommaaccent y -50 KPX Kcommaaccent yacute -50 KPX Kcommaaccent ydieresis -50 KPX L T -110 KPX L Tcaron -110 KPX L Tcommaaccent -110 KPX L V -110 KPX L W -70 KPX L Y -140 KPX L Yacute -140 KPX L Ydieresis -140 KPX L quotedblright -140 KPX L quoteright -160 KPX L y -30 KPX L yacute -30 KPX L ydieresis -30 KPX Lacute T -110 KPX Lacute Tcaron -110 KPX Lacute Tcommaaccent -110 KPX Lacute V -110 KPX Lacute W -70 KPX Lacute Y -140 KPX Lacute Yacute -140 KPX Lacute Ydieresis -140 KPX Lacute quotedblright -140 KPX Lacute quoteright -160 KPX Lacute y -30 KPX Lacute yacute -30 KPX Lacute ydieresis -30 KPX Lcaron T -110 KPX Lcaron Tcaron -110 KPX Lcaron Tcommaaccent -110 KPX Lcaron V -110 KPX Lcaron W -70 KPX Lcaron Y -140 KPX Lcaron Yacute -140 KPX Lcaron Ydieresis -140 KPX Lcaron quotedblright -140 KPX Lcaron quoteright -160 KPX Lcaron y -30 KPX Lcaron yacute -30 KPX Lcaron ydieresis -30 KPX Lcommaaccent T -110 KPX Lcommaaccent Tcaron -110 KPX Lcommaaccent Tcommaaccent -110 KPX Lcommaaccent V -110 KPX Lcommaaccent W -70 KPX Lcommaaccent Y -140 KPX Lcommaaccent Yacute -140 KPX Lcommaaccent Ydieresis -140 KPX Lcommaaccent quotedblright -140 KPX Lcommaaccent quoteright -160 KPX Lcommaaccent y -30 KPX Lcommaaccent yacute -30 KPX Lcommaaccent ydieresis -30 KPX Lslash T -110 KPX Lslash Tcaron -110 KPX Lslash Tcommaaccent -110 KPX Lslash V -110 KPX Lslash W -70 KPX Lslash Y -140 KPX Lslash Yacute -140 KPX Lslash Ydieresis -140 KPX Lslash quotedblright -140 KPX Lslash quoteright -160 KPX Lslash y -30 KPX Lslash yacute -30 KPX Lslash ydieresis -30 KPX O A -20 KPX O Aacute -20 KPX O Abreve -20 KPX O Acircumflex -20 KPX O Adieresis -20 KPX O Agrave -20 KPX O Amacron -20 KPX O Aogonek -20 KPX O Aring -20 KPX O Atilde -20 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -30 KPX O X -60 KPX O Y -70 KPX O Yacute -70 KPX O Ydieresis -70 KPX O comma -40 KPX O period -40 KPX Oacute A -20 KPX Oacute Aacute -20 KPX Oacute Abreve -20 KPX Oacute Acircumflex -20 KPX Oacute Adieresis -20 KPX Oacute Agrave -20 KPX Oacute Amacron -20 KPX Oacute Aogonek -20 KPX Oacute Aring -20 KPX Oacute Atilde -20 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -30 KPX Oacute X -60 KPX Oacute Y -70 KPX Oacute Yacute -70 KPX Oacute Ydieresis -70 KPX Oacute comma -40 KPX Oacute period -40 KPX Ocircumflex A -20 KPX Ocircumflex Aacute -20 KPX Ocircumflex Abreve -20 KPX Ocircumflex Acircumflex -20 KPX Ocircumflex Adieresis -20 KPX Ocircumflex Agrave -20 KPX Ocircumflex Amacron -20 KPX Ocircumflex Aogonek -20 KPX Ocircumflex Aring -20 KPX Ocircumflex Atilde -20 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -30 KPX Ocircumflex X -60 KPX Ocircumflex Y -70 KPX Ocircumflex Yacute -70 KPX Ocircumflex Ydieresis -70 KPX Ocircumflex comma -40 KPX Ocircumflex period -40 KPX Odieresis A -20 KPX Odieresis Aacute -20 KPX Odieresis Abreve -20 KPX Odieresis Acircumflex -20 KPX Odieresis Adieresis -20 KPX Odieresis Agrave -20 KPX Odieresis Amacron -20 KPX Odieresis Aogonek -20 KPX Odieresis Aring -20 KPX Odieresis Atilde -20 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -30 KPX Odieresis X -60 KPX Odieresis Y -70 KPX Odieresis Yacute -70 KPX Odieresis Ydieresis -70 KPX Odieresis comma -40 KPX Odieresis period -40 KPX Ograve A -20 KPX Ograve Aacute -20 KPX Ograve Abreve -20 KPX Ograve Acircumflex -20 KPX Ograve Adieresis -20 KPX Ograve Agrave -20 KPX Ograve Amacron -20 KPX Ograve Aogonek -20 KPX Ograve Aring -20 KPX Ograve Atilde -20 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -30 KPX Ograve X -60 KPX Ograve Y -70 KPX Ograve Yacute -70 KPX Ograve Ydieresis -70 KPX Ograve comma -40 KPX Ograve period -40 KPX Ohungarumlaut A -20 KPX Ohungarumlaut Aacute -20 KPX Ohungarumlaut Abreve -20 KPX Ohungarumlaut Acircumflex -20 KPX Ohungarumlaut Adieresis -20 KPX Ohungarumlaut Agrave -20 KPX Ohungarumlaut Amacron -20 KPX Ohungarumlaut Aogonek -20 KPX Ohungarumlaut Aring -20 KPX Ohungarumlaut Atilde -20 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -30 KPX Ohungarumlaut X -60 KPX Ohungarumlaut Y -70 KPX Ohungarumlaut Yacute -70 KPX Ohungarumlaut Ydieresis -70 KPX Ohungarumlaut comma -40 KPX Ohungarumlaut period -40 KPX Omacron A -20 KPX Omacron Aacute -20 KPX Omacron Abreve -20 KPX Omacron Acircumflex -20 KPX Omacron Adieresis -20 KPX Omacron Agrave -20 KPX Omacron Amacron -20 KPX Omacron Aogonek -20 KPX Omacron Aring -20 KPX Omacron Atilde -20 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -30 KPX Omacron X -60 KPX Omacron Y -70 KPX Omacron Yacute -70 KPX Omacron Ydieresis -70 KPX Omacron comma -40 KPX Omacron period -40 KPX Oslash A -20 KPX Oslash Aacute -20 KPX Oslash Abreve -20 KPX Oslash Acircumflex -20 KPX Oslash Adieresis -20 KPX Oslash Agrave -20 KPX Oslash Amacron -20 KPX Oslash Aogonek -20 KPX Oslash Aring -20 KPX Oslash Atilde -20 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -30 KPX Oslash X -60 KPX Oslash Y -70 KPX Oslash Yacute -70 KPX Oslash Ydieresis -70 KPX Oslash comma -40 KPX Oslash period -40 KPX Otilde A -20 KPX Otilde Aacute -20 KPX Otilde Abreve -20 KPX Otilde Acircumflex -20 KPX Otilde Adieresis -20 KPX Otilde Agrave -20 KPX Otilde Amacron -20 KPX Otilde Aogonek -20 KPX Otilde Aring -20 KPX Otilde Atilde -20 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -30 KPX Otilde X -60 KPX Otilde Y -70 KPX Otilde Yacute -70 KPX Otilde Ydieresis -70 KPX Otilde comma -40 KPX Otilde period -40 KPX P A -120 KPX P Aacute -120 KPX P Abreve -120 KPX P Acircumflex -120 KPX P Adieresis -120 KPX P Agrave -120 KPX P Amacron -120 KPX P Aogonek -120 KPX P Aring -120 KPX P Atilde -120 KPX P a -40 KPX P aacute -40 KPX P abreve -40 KPX P acircumflex -40 KPX P adieresis -40 KPX P agrave -40 KPX P amacron -40 KPX P aogonek -40 KPX P aring -40 KPX P atilde -40 KPX P comma -180 KPX P e -50 KPX P eacute -50 KPX P ecaron -50 KPX P ecircumflex -50 KPX P edieresis -50 KPX P edotaccent -50 KPX P egrave -50 KPX P emacron -50 KPX P eogonek -50 KPX P o -50 KPX P oacute -50 KPX P ocircumflex -50 KPX P odieresis -50 KPX P ograve -50 KPX P ohungarumlaut -50 KPX P omacron -50 KPX P oslash -50 KPX P otilde -50 KPX P period -180 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX R O -20 KPX R Oacute -20 KPX R Ocircumflex -20 KPX R Odieresis -20 KPX R Ograve -20 KPX R Ohungarumlaut -20 KPX R Omacron -20 KPX R Oslash -20 KPX R Otilde -20 KPX R T -30 KPX R Tcaron -30 KPX R Tcommaaccent -30 KPX R U -40 KPX R Uacute -40 KPX R Ucircumflex -40 KPX R Udieresis -40 KPX R Ugrave -40 KPX R Uhungarumlaut -40 KPX R Umacron -40 KPX R Uogonek -40 KPX R Uring -40 KPX R V -50 KPX R W -30 KPX R Y -50 KPX R Yacute -50 KPX R Ydieresis -50 KPX Racute O -20 KPX Racute Oacute -20 KPX Racute Ocircumflex -20 KPX Racute Odieresis -20 KPX Racute Ograve -20 KPX Racute Ohungarumlaut -20 KPX Racute Omacron -20 KPX Racute Oslash -20 KPX Racute Otilde -20 KPX Racute T -30 KPX Racute Tcaron -30 KPX Racute Tcommaaccent -30 KPX Racute U -40 KPX Racute Uacute -40 KPX Racute Ucircumflex -40 KPX Racute Udieresis -40 KPX Racute Ugrave -40 KPX Racute Uhungarumlaut -40 KPX Racute Umacron -40 KPX Racute Uogonek -40 KPX Racute Uring -40 KPX Racute V -50 KPX Racute W -30 KPX Racute Y -50 KPX Racute Yacute -50 KPX Racute Ydieresis -50 KPX Rcaron O -20 KPX Rcaron Oacute -20 KPX Rcaron Ocircumflex -20 KPX Rcaron Odieresis -20 KPX Rcaron Ograve -20 KPX Rcaron Ohungarumlaut -20 KPX Rcaron Omacron -20 KPX Rcaron Oslash -20 KPX Rcaron Otilde -20 KPX Rcaron T -30 KPX Rcaron Tcaron -30 KPX Rcaron Tcommaaccent -30 KPX Rcaron U -40 KPX Rcaron Uacute -40 KPX Rcaron Ucircumflex -40 KPX Rcaron Udieresis -40 KPX Rcaron Ugrave -40 KPX Rcaron Uhungarumlaut -40 KPX Rcaron Umacron -40 KPX Rcaron Uogonek -40 KPX Rcaron Uring -40 KPX Rcaron V -50 KPX Rcaron W -30 KPX Rcaron Y -50 KPX Rcaron Yacute -50 KPX Rcaron Ydieresis -50 KPX Rcommaaccent O -20 KPX Rcommaaccent Oacute -20 KPX Rcommaaccent Ocircumflex -20 KPX Rcommaaccent Odieresis -20 KPX Rcommaaccent Ograve -20 KPX Rcommaaccent Ohungarumlaut -20 KPX Rcommaaccent Omacron -20 KPX Rcommaaccent Oslash -20 KPX Rcommaaccent Otilde -20 KPX Rcommaaccent T -30 KPX Rcommaaccent Tcaron -30 KPX Rcommaaccent Tcommaaccent -30 KPX Rcommaaccent U -40 KPX Rcommaaccent Uacute -40 KPX Rcommaaccent Ucircumflex -40 KPX Rcommaaccent Udieresis -40 KPX Rcommaaccent Ugrave -40 KPX Rcommaaccent Uhungarumlaut -40 KPX Rcommaaccent Umacron -40 KPX Rcommaaccent Uogonek -40 KPX Rcommaaccent Uring -40 KPX Rcommaaccent V -50 KPX Rcommaaccent W -30 KPX Rcommaaccent Y -50 KPX Rcommaaccent Yacute -50 KPX Rcommaaccent Ydieresis -50 KPX S comma -20 KPX S period -20 KPX Sacute comma -20 KPX Sacute period -20 KPX Scaron comma -20 KPX Scaron period -20 KPX Scedilla comma -20 KPX Scedilla period -20 KPX Scommaaccent comma -20 KPX Scommaaccent period -20 KPX T A -120 KPX T Aacute -120 KPX T Abreve -120 KPX T Acircumflex -120 KPX T Adieresis -120 KPX T Agrave -120 KPX T Amacron -120 KPX T Aogonek -120 KPX T Aring -120 KPX T Atilde -120 KPX T O -40 KPX T Oacute -40 KPX T Ocircumflex -40 KPX T Odieresis -40 KPX T Ograve -40 KPX T Ohungarumlaut -40 KPX T Omacron -40 KPX T Oslash -40 KPX T Otilde -40 KPX T a -120 KPX T aacute -120 KPX T abreve -60 KPX T acircumflex -120 KPX T adieresis -120 KPX T agrave -120 KPX T amacron -60 KPX T aogonek -120 KPX T aring -120 KPX T atilde -60 KPX T colon -20 KPX T comma -120 KPX T e -120 KPX T eacute -120 KPX T ecaron -120 KPX T ecircumflex -120 KPX T edieresis -120 KPX T edotaccent -120 KPX T egrave -60 KPX T emacron -60 KPX T eogonek -120 KPX T hyphen -140 KPX T o -120 KPX T oacute -120 KPX T ocircumflex -120 KPX T odieresis -120 KPX T ograve -120 KPX T ohungarumlaut -120 KPX T omacron -60 KPX T oslash -120 KPX T otilde -60 KPX T period -120 KPX T r -120 KPX T racute -120 KPX T rcaron -120 KPX T rcommaaccent -120 KPX T semicolon -20 KPX T u -120 KPX T uacute -120 KPX T ucircumflex -120 KPX T udieresis -120 KPX T ugrave -120 KPX T uhungarumlaut -120 KPX T umacron -60 KPX T uogonek -120 KPX T uring -120 KPX T w -120 KPX T y -120 KPX T yacute -120 KPX T ydieresis -60 KPX Tcaron A -120 KPX Tcaron Aacute -120 KPX Tcaron Abreve -120 KPX Tcaron Acircumflex -120 KPX Tcaron Adieresis -120 KPX Tcaron Agrave -120 KPX Tcaron Amacron -120 KPX Tcaron Aogonek -120 KPX Tcaron Aring -120 KPX Tcaron Atilde -120 KPX Tcaron O -40 KPX Tcaron Oacute -40 KPX Tcaron Ocircumflex -40 KPX Tcaron Odieresis -40 KPX Tcaron Ograve -40 KPX Tcaron Ohungarumlaut -40 KPX Tcaron Omacron -40 KPX Tcaron Oslash -40 KPX Tcaron Otilde -40 KPX Tcaron a -120 KPX Tcaron aacute -120 KPX Tcaron abreve -60 KPX Tcaron acircumflex -120 KPX Tcaron adieresis -120 KPX Tcaron agrave -120 KPX Tcaron amacron -60 KPX Tcaron aogonek -120 KPX Tcaron aring -120 KPX Tcaron atilde -60 KPX Tcaron colon -20 KPX Tcaron comma -120 KPX Tcaron e -120 KPX Tcaron eacute -120 KPX Tcaron ecaron -120 KPX Tcaron ecircumflex -120 KPX Tcaron edieresis -120 KPX Tcaron edotaccent -120 KPX Tcaron egrave -60 KPX Tcaron emacron -60 KPX Tcaron eogonek -120 KPX Tcaron hyphen -140 KPX Tcaron o -120 KPX Tcaron oacute -120 KPX Tcaron ocircumflex -120 KPX Tcaron odieresis -120 KPX Tcaron ograve -120 KPX Tcaron ohungarumlaut -120 KPX Tcaron omacron -60 KPX Tcaron oslash -120 KPX Tcaron otilde -60 KPX Tcaron period -120 KPX Tcaron r -120 KPX Tcaron racute -120 KPX Tcaron rcaron -120 KPX Tcaron rcommaaccent -120 KPX Tcaron semicolon -20 KPX Tcaron u -120 KPX Tcaron uacute -120 KPX Tcaron ucircumflex -120 KPX Tcaron udieresis -120 KPX Tcaron ugrave -120 KPX Tcaron uhungarumlaut -120 KPX Tcaron umacron -60 KPX Tcaron uogonek -120 KPX Tcaron uring -120 KPX Tcaron w -120 KPX Tcaron y -120 KPX Tcaron yacute -120 KPX Tcaron ydieresis -60 KPX Tcommaaccent A -120 KPX Tcommaaccent Aacute -120 KPX Tcommaaccent Abreve -120 KPX Tcommaaccent Acircumflex -120 KPX Tcommaaccent Adieresis -120 KPX Tcommaaccent Agrave -120 KPX Tcommaaccent Amacron -120 KPX Tcommaaccent Aogonek -120 KPX Tcommaaccent Aring -120 KPX Tcommaaccent Atilde -120 KPX Tcommaaccent O -40 KPX Tcommaaccent Oacute -40 KPX Tcommaaccent Ocircumflex -40 KPX Tcommaaccent Odieresis -40 KPX Tcommaaccent Ograve -40 KPX Tcommaaccent Ohungarumlaut -40 KPX Tcommaaccent Omacron -40 KPX Tcommaaccent Oslash -40 KPX Tcommaaccent Otilde -40 KPX Tcommaaccent a -120 KPX Tcommaaccent aacute -120 KPX Tcommaaccent abreve -60 KPX Tcommaaccent acircumflex -120 KPX Tcommaaccent adieresis -120 KPX Tcommaaccent agrave -120 KPX Tcommaaccent amacron -60 KPX Tcommaaccent aogonek -120 KPX Tcommaaccent aring -120 KPX Tcommaaccent atilde -60 KPX Tcommaaccent colon -20 KPX Tcommaaccent comma -120 KPX Tcommaaccent e -120 KPX Tcommaaccent eacute -120 KPX Tcommaaccent ecaron -120 KPX Tcommaaccent ecircumflex -120 KPX Tcommaaccent edieresis -120 KPX Tcommaaccent edotaccent -120 KPX Tcommaaccent egrave -60 KPX Tcommaaccent emacron -60 KPX Tcommaaccent eogonek -120 KPX Tcommaaccent hyphen -140 KPX Tcommaaccent o -120 KPX Tcommaaccent oacute -120 KPX Tcommaaccent ocircumflex -120 KPX Tcommaaccent odieresis -120 KPX Tcommaaccent ograve -120 KPX Tcommaaccent ohungarumlaut -120 KPX Tcommaaccent omacron -60 KPX Tcommaaccent oslash -120 KPX Tcommaaccent otilde -60 KPX Tcommaaccent period -120 KPX Tcommaaccent r -120 KPX Tcommaaccent racute -120 KPX Tcommaaccent rcaron -120 KPX Tcommaaccent rcommaaccent -120 KPX Tcommaaccent semicolon -20 KPX Tcommaaccent u -120 KPX Tcommaaccent uacute -120 KPX Tcommaaccent ucircumflex -120 KPX Tcommaaccent udieresis -120 KPX Tcommaaccent ugrave -120 KPX Tcommaaccent uhungarumlaut -120 KPX Tcommaaccent umacron -60 KPX Tcommaaccent uogonek -120 KPX Tcommaaccent uring -120 KPX Tcommaaccent w -120 KPX Tcommaaccent y -120 KPX Tcommaaccent yacute -120 KPX Tcommaaccent ydieresis -60 KPX U A -40 KPX U Aacute -40 KPX U Abreve -40 KPX U Acircumflex -40 KPX U Adieresis -40 KPX U Agrave -40 KPX U Amacron -40 KPX U Aogonek -40 KPX U Aring -40 KPX U Atilde -40 KPX U comma -40 KPX U period -40 KPX Uacute A -40 KPX Uacute Aacute -40 KPX Uacute Abreve -40 KPX Uacute Acircumflex -40 KPX Uacute Adieresis -40 KPX Uacute Agrave -40 KPX Uacute Amacron -40 KPX Uacute Aogonek -40 KPX Uacute Aring -40 KPX Uacute Atilde -40 KPX Uacute comma -40 KPX Uacute period -40 KPX Ucircumflex A -40 KPX Ucircumflex Aacute -40 KPX Ucircumflex Abreve -40 KPX Ucircumflex Acircumflex -40 KPX Ucircumflex Adieresis -40 KPX Ucircumflex Agrave -40 KPX Ucircumflex Amacron -40 KPX Ucircumflex Aogonek -40 KPX Ucircumflex Aring -40 KPX Ucircumflex Atilde -40 KPX Ucircumflex comma -40 KPX Ucircumflex period -40 KPX Udieresis A -40 KPX Udieresis Aacute -40 KPX Udieresis Abreve -40 KPX Udieresis Acircumflex -40 KPX Udieresis Adieresis -40 KPX Udieresis Agrave -40 KPX Udieresis Amacron -40 KPX Udieresis Aogonek -40 KPX Udieresis Aring -40 KPX Udieresis Atilde -40 KPX Udieresis comma -40 KPX Udieresis period -40 KPX Ugrave A -40 KPX Ugrave Aacute -40 KPX Ugrave Abreve -40 KPX Ugrave Acircumflex -40 KPX Ugrave Adieresis -40 KPX Ugrave Agrave -40 KPX Ugrave Amacron -40 KPX Ugrave Aogonek -40 KPX Ugrave Aring -40 KPX Ugrave Atilde -40 KPX Ugrave comma -40 KPX Ugrave period -40 KPX Uhungarumlaut A -40 KPX Uhungarumlaut Aacute -40 KPX Uhungarumlaut Abreve -40 KPX Uhungarumlaut Acircumflex -40 KPX Uhungarumlaut Adieresis -40 KPX Uhungarumlaut Agrave -40 KPX Uhungarumlaut Amacron -40 KPX Uhungarumlaut Aogonek -40 KPX Uhungarumlaut Aring -40 KPX Uhungarumlaut Atilde -40 KPX Uhungarumlaut comma -40 KPX Uhungarumlaut period -40 KPX Umacron A -40 KPX Umacron Aacute -40 KPX Umacron Abreve -40 KPX Umacron Acircumflex -40 KPX Umacron Adieresis -40 KPX Umacron Agrave -40 KPX Umacron Amacron -40 KPX Umacron Aogonek -40 KPX Umacron Aring -40 KPX Umacron Atilde -40 KPX Umacron comma -40 KPX Umacron period -40 KPX Uogonek A -40 KPX Uogonek Aacute -40 KPX Uogonek Abreve -40 KPX Uogonek Acircumflex -40 KPX Uogonek Adieresis -40 KPX Uogonek Agrave -40 KPX Uogonek Amacron -40 KPX Uogonek Aogonek -40 KPX Uogonek Aring -40 KPX Uogonek Atilde -40 KPX Uogonek comma -40 KPX Uogonek period -40 KPX Uring A -40 KPX Uring Aacute -40 KPX Uring Abreve -40 KPX Uring Acircumflex -40 KPX Uring Adieresis -40 KPX Uring Agrave -40 KPX Uring Amacron -40 KPX Uring Aogonek -40 KPX Uring Aring -40 KPX Uring Atilde -40 KPX Uring comma -40 KPX Uring period -40 KPX V A -80 KPX V Aacute -80 KPX V Abreve -80 KPX V Acircumflex -80 KPX V Adieresis -80 KPX V Agrave -80 KPX V Amacron -80 KPX V Aogonek -80 KPX V Aring -80 KPX V Atilde -80 KPX V G -40 KPX V Gbreve -40 KPX V Gcommaaccent -40 KPX V O -40 KPX V Oacute -40 KPX V Ocircumflex -40 KPX V Odieresis -40 KPX V Ograve -40 KPX V Ohungarumlaut -40 KPX V Omacron -40 KPX V Oslash -40 KPX V Otilde -40 KPX V a -70 KPX V aacute -70 KPX V abreve -70 KPX V acircumflex -70 KPX V adieresis -70 KPX V agrave -70 KPX V amacron -70 KPX V aogonek -70 KPX V aring -70 KPX V atilde -70 KPX V colon -40 KPX V comma -125 KPX V e -80 KPX V eacute -80 KPX V ecaron -80 KPX V ecircumflex -80 KPX V edieresis -80 KPX V edotaccent -80 KPX V egrave -80 KPX V emacron -80 KPX V eogonek -80 KPX V hyphen -80 KPX V o -80 KPX V oacute -80 KPX V ocircumflex -80 KPX V odieresis -80 KPX V ograve -80 KPX V ohungarumlaut -80 KPX V omacron -80 KPX V oslash -80 KPX V otilde -80 KPX V period -125 KPX V semicolon -40 KPX V u -70 KPX V uacute -70 KPX V ucircumflex -70 KPX V udieresis -70 KPX V ugrave -70 KPX V uhungarumlaut -70 KPX V umacron -70 KPX V uogonek -70 KPX V uring -70 KPX W A -50 KPX W Aacute -50 KPX W Abreve -50 KPX W Acircumflex -50 KPX W Adieresis -50 KPX W Agrave -50 KPX W Amacron -50 KPX W Aogonek -50 KPX W Aring -50 KPX W Atilde -50 KPX W O -20 KPX W Oacute -20 KPX W Ocircumflex -20 KPX W Odieresis -20 KPX W Ograve -20 KPX W Ohungarumlaut -20 KPX W Omacron -20 KPX W Oslash -20 KPX W Otilde -20 KPX W a -40 KPX W aacute -40 KPX W abreve -40 KPX W acircumflex -40 KPX W adieresis -40 KPX W agrave -40 KPX W amacron -40 KPX W aogonek -40 KPX W aring -40 KPX W atilde -40 KPX W comma -80 KPX W e -30 KPX W eacute -30 KPX W ecaron -30 KPX W ecircumflex -30 KPX W edieresis -30 KPX W edotaccent -30 KPX W egrave -30 KPX W emacron -30 KPX W eogonek -30 KPX W hyphen -40 KPX W o -30 KPX W oacute -30 KPX W ocircumflex -30 KPX W odieresis -30 KPX W ograve -30 KPX W ohungarumlaut -30 KPX W omacron -30 KPX W oslash -30 KPX W otilde -30 KPX W period -80 KPX W u -30 KPX W uacute -30 KPX W ucircumflex -30 KPX W udieresis -30 KPX W ugrave -30 KPX W uhungarumlaut -30 KPX W umacron -30 KPX W uogonek -30 KPX W uring -30 KPX W y -20 KPX W yacute -20 KPX W ydieresis -20 KPX Y A -110 KPX Y Aacute -110 KPX Y Abreve -110 KPX Y Acircumflex -110 KPX Y Adieresis -110 KPX Y Agrave -110 KPX Y Amacron -110 KPX Y Aogonek -110 KPX Y Aring -110 KPX Y Atilde -110 KPX Y O -85 KPX Y Oacute -85 KPX Y Ocircumflex -85 KPX Y Odieresis -85 KPX Y Ograve -85 KPX Y Ohungarumlaut -85 KPX Y Omacron -85 KPX Y Oslash -85 KPX Y Otilde -85 KPX Y a -140 KPX Y aacute -140 KPX Y abreve -70 KPX Y acircumflex -140 KPX Y adieresis -140 KPX Y agrave -140 KPX Y amacron -70 KPX Y aogonek -140 KPX Y aring -140 KPX Y atilde -140 KPX Y colon -60 KPX Y comma -140 KPX Y e -140 KPX Y eacute -140 KPX Y ecaron -140 KPX Y ecircumflex -140 KPX Y edieresis -140 KPX Y edotaccent -140 KPX Y egrave -140 KPX Y emacron -70 KPX Y eogonek -140 KPX Y hyphen -140 KPX Y i -20 KPX Y iacute -20 KPX Y iogonek -20 KPX Y o -140 KPX Y oacute -140 KPX Y ocircumflex -140 KPX Y odieresis -140 KPX Y ograve -140 KPX Y ohungarumlaut -140 KPX Y omacron -140 KPX Y oslash -140 KPX Y otilde -140 KPX Y period -140 KPX Y semicolon -60 KPX Y u -110 KPX Y uacute -110 KPX Y ucircumflex -110 KPX Y udieresis -110 KPX Y ugrave -110 KPX Y uhungarumlaut -110 KPX Y umacron -110 KPX Y uogonek -110 KPX Y uring -110 KPX Yacute A -110 KPX Yacute Aacute -110 KPX Yacute Abreve -110 KPX Yacute Acircumflex -110 KPX Yacute Adieresis -110 KPX Yacute Agrave -110 KPX Yacute Amacron -110 KPX Yacute Aogonek -110 KPX Yacute Aring -110 KPX Yacute Atilde -110 KPX Yacute O -85 KPX Yacute Oacute -85 KPX Yacute Ocircumflex -85 KPX Yacute Odieresis -85 KPX Yacute Ograve -85 KPX Yacute Ohungarumlaut -85 KPX Yacute Omacron -85 KPX Yacute Oslash -85 KPX Yacute Otilde -85 KPX Yacute a -140 KPX Yacute aacute -140 KPX Yacute abreve -70 KPX Yacute acircumflex -140 KPX Yacute adieresis -140 KPX Yacute agrave -140 KPX Yacute amacron -70 KPX Yacute aogonek -140 KPX Yacute aring -140 KPX Yacute atilde -70 KPX Yacute colon -60 KPX Yacute comma -140 KPX Yacute e -140 KPX Yacute eacute -140 KPX Yacute ecaron -140 KPX Yacute ecircumflex -140 KPX Yacute edieresis -140 KPX Yacute edotaccent -140 KPX Yacute egrave -140 KPX Yacute emacron -70 KPX Yacute eogonek -140 KPX Yacute hyphen -140 KPX Yacute i -20 KPX Yacute iacute -20 KPX Yacute iogonek -20 KPX Yacute o -140 KPX Yacute oacute -140 KPX Yacute ocircumflex -140 KPX Yacute odieresis -140 KPX Yacute ograve -140 KPX Yacute ohungarumlaut -140 KPX Yacute omacron -70 KPX Yacute oslash -140 KPX Yacute otilde -140 KPX Yacute period -140 KPX Yacute semicolon -60 KPX Yacute u -110 KPX Yacute uacute -110 KPX Yacute ucircumflex -110 KPX Yacute udieresis -110 KPX Yacute ugrave -110 KPX Yacute uhungarumlaut -110 KPX Yacute umacron -110 KPX Yacute uogonek -110 KPX Yacute uring -110 KPX Ydieresis A -110 KPX Ydieresis Aacute -110 KPX Ydieresis Abreve -110 KPX Ydieresis Acircumflex -110 KPX Ydieresis Adieresis -110 KPX Ydieresis Agrave -110 KPX Ydieresis Amacron -110 KPX Ydieresis Aogonek -110 KPX Ydieresis Aring -110 KPX Ydieresis Atilde -110 KPX Ydieresis O -85 KPX Ydieresis Oacute -85 KPX Ydieresis Ocircumflex -85 KPX Ydieresis Odieresis -85 KPX Ydieresis Ograve -85 KPX Ydieresis Ohungarumlaut -85 KPX Ydieresis Omacron -85 KPX Ydieresis Oslash -85 KPX Ydieresis Otilde -85 KPX Ydieresis a -140 KPX Ydieresis aacute -140 KPX Ydieresis abreve -70 KPX Ydieresis acircumflex -140 KPX Ydieresis adieresis -140 KPX Ydieresis agrave -140 KPX Ydieresis amacron -70 KPX Ydieresis aogonek -140 KPX Ydieresis aring -140 KPX Ydieresis atilde -70 KPX Ydieresis colon -60 KPX Ydieresis comma -140 KPX Ydieresis e -140 KPX Ydieresis eacute -140 KPX Ydieresis ecaron -140 KPX Ydieresis ecircumflex -140 KPX Ydieresis edieresis -140 KPX Ydieresis edotaccent -140 KPX Ydieresis egrave -140 KPX Ydieresis emacron -70 KPX Ydieresis eogonek -140 KPX Ydieresis hyphen -140 KPX Ydieresis i -20 KPX Ydieresis iacute -20 KPX Ydieresis iogonek -20 KPX Ydieresis o -140 KPX Ydieresis oacute -140 KPX Ydieresis ocircumflex -140 KPX Ydieresis odieresis -140 KPX Ydieresis ograve -140 KPX Ydieresis ohungarumlaut -140 KPX Ydieresis omacron -140 KPX Ydieresis oslash -140 KPX Ydieresis otilde -140 KPX Ydieresis period -140 KPX Ydieresis semicolon -60 KPX Ydieresis u -110 KPX Ydieresis uacute -110 KPX Ydieresis ucircumflex -110 KPX Ydieresis udieresis -110 KPX Ydieresis ugrave -110 KPX Ydieresis uhungarumlaut -110 KPX Ydieresis umacron -110 KPX Ydieresis uogonek -110 KPX Ydieresis uring -110 KPX a v -20 KPX a w -20 KPX a y -30 KPX a yacute -30 KPX a ydieresis -30 KPX aacute v -20 KPX aacute w -20 KPX aacute y -30 KPX aacute yacute -30 KPX aacute ydieresis -30 KPX abreve v -20 KPX abreve w -20 KPX abreve y -30 KPX abreve yacute -30 KPX abreve ydieresis -30 KPX acircumflex v -20 KPX acircumflex w -20 KPX acircumflex y -30 KPX acircumflex yacute -30 KPX acircumflex ydieresis -30 KPX adieresis v -20 KPX adieresis w -20 KPX adieresis y -30 KPX adieresis yacute -30 KPX adieresis ydieresis -30 KPX agrave v -20 KPX agrave w -20 KPX agrave y -30 KPX agrave yacute -30 KPX agrave ydieresis -30 KPX amacron v -20 KPX amacron w -20 KPX amacron y -30 KPX amacron yacute -30 KPX amacron ydieresis -30 KPX aogonek v -20 KPX aogonek w -20 KPX aogonek y -30 KPX aogonek yacute -30 KPX aogonek ydieresis -30 KPX aring v -20 KPX aring w -20 KPX aring y -30 KPX aring yacute -30 KPX aring ydieresis -30 KPX atilde v -20 KPX atilde w -20 KPX atilde y -30 KPX atilde yacute -30 KPX atilde ydieresis -30 KPX b b -10 KPX b comma -40 KPX b l -20 KPX b lacute -20 KPX b lcommaaccent -20 KPX b lslash -20 KPX b period -40 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX b v -20 KPX b y -20 KPX b yacute -20 KPX b ydieresis -20 KPX c comma -15 KPX c k -20 KPX c kcommaaccent -20 KPX cacute comma -15 KPX cacute k -20 KPX cacute kcommaaccent -20 KPX ccaron comma -15 KPX ccaron k -20 KPX ccaron kcommaaccent -20 KPX ccedilla comma -15 KPX ccedilla k -20 KPX ccedilla kcommaaccent -20 KPX colon space -50 KPX comma quotedblright -100 KPX comma quoteright -100 KPX e comma -15 KPX e period -15 KPX e v -30 KPX e w -20 KPX e x -30 KPX e y -20 KPX e yacute -20 KPX e ydieresis -20 KPX eacute comma -15 KPX eacute period -15 KPX eacute v -30 KPX eacute w -20 KPX eacute x -30 KPX eacute y -20 KPX eacute yacute -20 KPX eacute ydieresis -20 KPX ecaron comma -15 KPX ecaron period -15 KPX ecaron v -30 KPX ecaron w -20 KPX ecaron x -30 KPX ecaron y -20 KPX ecaron yacute -20 KPX ecaron ydieresis -20 KPX ecircumflex comma -15 KPX ecircumflex period -15 KPX ecircumflex v -30 KPX ecircumflex w -20 KPX ecircumflex x -30 KPX ecircumflex y -20 KPX ecircumflex yacute -20 KPX ecircumflex ydieresis -20 KPX edieresis comma -15 KPX edieresis period -15 KPX edieresis v -30 KPX edieresis w -20 KPX edieresis x -30 KPX edieresis y -20 KPX edieresis yacute -20 KPX edieresis ydieresis -20 KPX edotaccent comma -15 KPX edotaccent period -15 KPX edotaccent v -30 KPX edotaccent w -20 KPX edotaccent x -30 KPX edotaccent y -20 KPX edotaccent yacute -20 KPX edotaccent ydieresis -20 KPX egrave comma -15 KPX egrave period -15 KPX egrave v -30 KPX egrave w -20 KPX egrave x -30 KPX egrave y -20 KPX egrave yacute -20 KPX egrave ydieresis -20 KPX emacron comma -15 KPX emacron period -15 KPX emacron v -30 KPX emacron w -20 KPX emacron x -30 KPX emacron y -20 KPX emacron yacute -20 KPX emacron ydieresis -20 KPX eogonek comma -15 KPX eogonek period -15 KPX eogonek v -30 KPX eogonek w -20 KPX eogonek x -30 KPX eogonek y -20 KPX eogonek yacute -20 KPX eogonek ydieresis -20 KPX f a -30 KPX f aacute -30 KPX f abreve -30 KPX f acircumflex -30 KPX f adieresis -30 KPX f agrave -30 KPX f amacron -30 KPX f aogonek -30 KPX f aring -30 KPX f atilde -30 KPX f comma -30 KPX f dotlessi -28 KPX f e -30 KPX f eacute -30 KPX f ecaron -30 KPX f ecircumflex -30 KPX f edieresis -30 KPX f edotaccent -30 KPX f egrave -30 KPX f emacron -30 KPX f eogonek -30 KPX f o -30 KPX f oacute -30 KPX f ocircumflex -30 KPX f odieresis -30 KPX f ograve -30 KPX f ohungarumlaut -30 KPX f omacron -30 KPX f oslash -30 KPX f otilde -30 KPX f period -30 KPX f quotedblright 60 KPX f quoteright 50 KPX g r -10 KPX g racute -10 KPX g rcaron -10 KPX g rcommaaccent -10 KPX gbreve r -10 KPX gbreve racute -10 KPX gbreve rcaron -10 KPX gbreve rcommaaccent -10 KPX gcommaaccent r -10 KPX gcommaaccent racute -10 KPX gcommaaccent rcaron -10 KPX gcommaaccent rcommaaccent -10 KPX h y -30 KPX h yacute -30 KPX h ydieresis -30 KPX k e -20 KPX k eacute -20 KPX k ecaron -20 KPX k ecircumflex -20 KPX k edieresis -20 KPX k edotaccent -20 KPX k egrave -20 KPX k emacron -20 KPX k eogonek -20 KPX k o -20 KPX k oacute -20 KPX k ocircumflex -20 KPX k odieresis -20 KPX k ograve -20 KPX k ohungarumlaut -20 KPX k omacron -20 KPX k oslash -20 KPX k otilde -20 KPX kcommaaccent e -20 KPX kcommaaccent eacute -20 KPX kcommaaccent ecaron -20 KPX kcommaaccent ecircumflex -20 KPX kcommaaccent edieresis -20 KPX kcommaaccent edotaccent -20 KPX kcommaaccent egrave -20 KPX kcommaaccent emacron -20 KPX kcommaaccent eogonek -20 KPX kcommaaccent o -20 KPX kcommaaccent oacute -20 KPX kcommaaccent ocircumflex -20 KPX kcommaaccent odieresis -20 KPX kcommaaccent ograve -20 KPX kcommaaccent ohungarumlaut -20 KPX kcommaaccent omacron -20 KPX kcommaaccent oslash -20 KPX kcommaaccent otilde -20 KPX m u -10 KPX m uacute -10 KPX m ucircumflex -10 KPX m udieresis -10 KPX m ugrave -10 KPX m uhungarumlaut -10 KPX m umacron -10 KPX m uogonek -10 KPX m uring -10 KPX m y -15 KPX m yacute -15 KPX m ydieresis -15 KPX n u -10 KPX n uacute -10 KPX n ucircumflex -10 KPX n udieresis -10 KPX n ugrave -10 KPX n uhungarumlaut -10 KPX n umacron -10 KPX n uogonek -10 KPX n uring -10 KPX n v -20 KPX n y -15 KPX n yacute -15 KPX n ydieresis -15 KPX nacute u -10 KPX nacute uacute -10 KPX nacute ucircumflex -10 KPX nacute udieresis -10 KPX nacute ugrave -10 KPX nacute uhungarumlaut -10 KPX nacute umacron -10 KPX nacute uogonek -10 KPX nacute uring -10 KPX nacute v -20 KPX nacute y -15 KPX nacute yacute -15 KPX nacute ydieresis -15 KPX ncaron u -10 KPX ncaron uacute -10 KPX ncaron ucircumflex -10 KPX ncaron udieresis -10 KPX ncaron ugrave -10 KPX ncaron uhungarumlaut -10 KPX ncaron umacron -10 KPX ncaron uogonek -10 KPX ncaron uring -10 KPX ncaron v -20 KPX ncaron y -15 KPX ncaron yacute -15 KPX ncaron ydieresis -15 KPX ncommaaccent u -10 KPX ncommaaccent uacute -10 KPX ncommaaccent ucircumflex -10 KPX ncommaaccent udieresis -10 KPX ncommaaccent ugrave -10 KPX ncommaaccent uhungarumlaut -10 KPX ncommaaccent umacron -10 KPX ncommaaccent uogonek -10 KPX ncommaaccent uring -10 KPX ncommaaccent v -20 KPX ncommaaccent y -15 KPX ncommaaccent yacute -15 KPX ncommaaccent ydieresis -15 KPX ntilde u -10 KPX ntilde uacute -10 KPX ntilde ucircumflex -10 KPX ntilde udieresis -10 KPX ntilde ugrave -10 KPX ntilde uhungarumlaut -10 KPX ntilde umacron -10 KPX ntilde uogonek -10 KPX ntilde uring -10 KPX ntilde v -20 KPX ntilde y -15 KPX ntilde yacute -15 KPX ntilde ydieresis -15 KPX o comma -40 KPX o period -40 KPX o v -15 KPX o w -15 KPX o x -30 KPX o y -30 KPX o yacute -30 KPX o ydieresis -30 KPX oacute comma -40 KPX oacute period -40 KPX oacute v -15 KPX oacute w -15 KPX oacute x -30 KPX oacute y -30 KPX oacute yacute -30 KPX oacute ydieresis -30 KPX ocircumflex comma -40 KPX ocircumflex period -40 KPX ocircumflex v -15 KPX ocircumflex w -15 KPX ocircumflex x -30 KPX ocircumflex y -30 KPX ocircumflex yacute -30 KPX ocircumflex ydieresis -30 KPX odieresis comma -40 KPX odieresis period -40 KPX odieresis v -15 KPX odieresis w -15 KPX odieresis x -30 KPX odieresis y -30 KPX odieresis yacute -30 KPX odieresis ydieresis -30 KPX ograve comma -40 KPX ograve period -40 KPX ograve v -15 KPX ograve w -15 KPX ograve x -30 KPX ograve y -30 KPX ograve yacute -30 KPX ograve ydieresis -30 KPX ohungarumlaut comma -40 KPX ohungarumlaut period -40 KPX ohungarumlaut v -15 KPX ohungarumlaut w -15 KPX ohungarumlaut x -30 KPX ohungarumlaut y -30 KPX ohungarumlaut yacute -30 KPX ohungarumlaut ydieresis -30 KPX omacron comma -40 KPX omacron period -40 KPX omacron v -15 KPX omacron w -15 KPX omacron x -30 KPX omacron y -30 KPX omacron yacute -30 KPX omacron ydieresis -30 KPX oslash a -55 KPX oslash aacute -55 KPX oslash abreve -55 KPX oslash acircumflex -55 KPX oslash adieresis -55 KPX oslash agrave -55 KPX oslash amacron -55 KPX oslash aogonek -55 KPX oslash aring -55 KPX oslash atilde -55 KPX oslash b -55 KPX oslash c -55 KPX oslash cacute -55 KPX oslash ccaron -55 KPX oslash ccedilla -55 KPX oslash comma -95 KPX oslash d -55 KPX oslash dcroat -55 KPX oslash e -55 KPX oslash eacute -55 KPX oslash ecaron -55 KPX oslash ecircumflex -55 KPX oslash edieresis -55 KPX oslash edotaccent -55 KPX oslash egrave -55 KPX oslash emacron -55 KPX oslash eogonek -55 KPX oslash f -55 KPX oslash g -55 KPX oslash gbreve -55 KPX oslash gcommaaccent -55 KPX oslash h -55 KPX oslash i -55 KPX oslash iacute -55 KPX oslash icircumflex -55 KPX oslash idieresis -55 KPX oslash igrave -55 KPX oslash imacron -55 KPX oslash iogonek -55 KPX oslash j -55 KPX oslash k -55 KPX oslash kcommaaccent -55 KPX oslash l -55 KPX oslash lacute -55 KPX oslash lcommaaccent -55 KPX oslash lslash -55 KPX oslash m -55 KPX oslash n -55 KPX oslash nacute -55 KPX oslash ncaron -55 KPX oslash ncommaaccent -55 KPX oslash ntilde -55 KPX oslash o -55 KPX oslash oacute -55 KPX oslash ocircumflex -55 KPX oslash odieresis -55 KPX oslash ograve -55 KPX oslash ohungarumlaut -55 KPX oslash omacron -55 KPX oslash oslash -55 KPX oslash otilde -55 KPX oslash p -55 KPX oslash period -95 KPX oslash q -55 KPX oslash r -55 KPX oslash racute -55 KPX oslash rcaron -55 KPX oslash rcommaaccent -55 KPX oslash s -55 KPX oslash sacute -55 KPX oslash scaron -55 KPX oslash scedilla -55 KPX oslash scommaaccent -55 KPX oslash t -55 KPX oslash tcommaaccent -55 KPX oslash u -55 KPX oslash uacute -55 KPX oslash ucircumflex -55 KPX oslash udieresis -55 KPX oslash ugrave -55 KPX oslash uhungarumlaut -55 KPX oslash umacron -55 KPX oslash uogonek -55 KPX oslash uring -55 KPX oslash v -70 KPX oslash w -70 KPX oslash x -85 KPX oslash y -70 KPX oslash yacute -70 KPX oslash ydieresis -70 KPX oslash z -55 KPX oslash zacute -55 KPX oslash zcaron -55 KPX oslash zdotaccent -55 KPX otilde comma -40 KPX otilde period -40 KPX otilde v -15 KPX otilde w -15 KPX otilde x -30 KPX otilde y -30 KPX otilde yacute -30 KPX otilde ydieresis -30 KPX p comma -35 KPX p period -35 KPX p y -30 KPX p yacute -30 KPX p ydieresis -30 KPX period quotedblright -100 KPX period quoteright -100 KPX period space -60 KPX quotedblright space -40 KPX quoteleft quoteleft -57 KPX quoteright d -50 KPX quoteright dcroat -50 KPX quoteright quoteright -57 KPX quoteright r -50 KPX quoteright racute -50 KPX quoteright rcaron -50 KPX quoteright rcommaaccent -50 KPX quoteright s -50 KPX quoteright sacute -50 KPX quoteright scaron -50 KPX quoteright scedilla -50 KPX quoteright scommaaccent -50 KPX quoteright space -70 KPX r a -10 KPX r aacute -10 KPX r abreve -10 KPX r acircumflex -10 KPX r adieresis -10 KPX r agrave -10 KPX r amacron -10 KPX r aogonek -10 KPX r aring -10 KPX r atilde -10 KPX r colon 30 KPX r comma -50 KPX r i 15 KPX r iacute 15 KPX r icircumflex 15 KPX r idieresis 15 KPX r igrave 15 KPX r imacron 15 KPX r iogonek 15 KPX r k 15 KPX r kcommaaccent 15 KPX r l 15 KPX r lacute 15 KPX r lcommaaccent 15 KPX r lslash 15 KPX r m 25 KPX r n 25 KPX r nacute 25 KPX r ncaron 25 KPX r ncommaaccent 25 KPX r ntilde 25 KPX r p 30 KPX r period -50 KPX r semicolon 30 KPX r t 40 KPX r tcommaaccent 40 KPX r u 15 KPX r uacute 15 KPX r ucircumflex 15 KPX r udieresis 15 KPX r ugrave 15 KPX r uhungarumlaut 15 KPX r umacron 15 KPX r uogonek 15 KPX r uring 15 KPX r v 30 KPX r y 30 KPX r yacute 30 KPX r ydieresis 30 KPX racute a -10 KPX racute aacute -10 KPX racute abreve -10 KPX racute acircumflex -10 KPX racute adieresis -10 KPX racute agrave -10 KPX racute amacron -10 KPX racute aogonek -10 KPX racute aring -10 KPX racute atilde -10 KPX racute colon 30 KPX racute comma -50 KPX racute i 15 KPX racute iacute 15 KPX racute icircumflex 15 KPX racute idieresis 15 KPX racute igrave 15 KPX racute imacron 15 KPX racute iogonek 15 KPX racute k 15 KPX racute kcommaaccent 15 KPX racute l 15 KPX racute lacute 15 KPX racute lcommaaccent 15 KPX racute lslash 15 KPX racute m 25 KPX racute n 25 KPX racute nacute 25 KPX racute ncaron 25 KPX racute ncommaaccent 25 KPX racute ntilde 25 KPX racute p 30 KPX racute period -50 KPX racute semicolon 30 KPX racute t 40 KPX racute tcommaaccent 40 KPX racute u 15 KPX racute uacute 15 KPX racute ucircumflex 15 KPX racute udieresis 15 KPX racute ugrave 15 KPX racute uhungarumlaut 15 KPX racute umacron 15 KPX racute uogonek 15 KPX racute uring 15 KPX racute v 30 KPX racute y 30 KPX racute yacute 30 KPX racute ydieresis 30 KPX rcaron a -10 KPX rcaron aacute -10 KPX rcaron abreve -10 KPX rcaron acircumflex -10 KPX rcaron adieresis -10 KPX rcaron agrave -10 KPX rcaron amacron -10 KPX rcaron aogonek -10 KPX rcaron aring -10 KPX rcaron atilde -10 KPX rcaron colon 30 KPX rcaron comma -50 KPX rcaron i 15 KPX rcaron iacute 15 KPX rcaron icircumflex 15 KPX rcaron idieresis 15 KPX rcaron igrave 15 KPX rcaron imacron 15 KPX rcaron iogonek 15 KPX rcaron k 15 KPX rcaron kcommaaccent 15 KPX rcaron l 15 KPX rcaron lacute 15 KPX rcaron lcommaaccent 15 KPX rcaron lslash 15 KPX rcaron m 25 KPX rcaron n 25 KPX rcaron nacute 25 KPX rcaron ncaron 25 KPX rcaron ncommaaccent 25 KPX rcaron ntilde 25 KPX rcaron p 30 KPX rcaron period -50 KPX rcaron semicolon 30 KPX rcaron t 40 KPX rcaron tcommaaccent 40 KPX rcaron u 15 KPX rcaron uacute 15 KPX rcaron ucircumflex 15 KPX rcaron udieresis 15 KPX rcaron ugrave 15 KPX rcaron uhungarumlaut 15 KPX rcaron umacron 15 KPX rcaron uogonek 15 KPX rcaron uring 15 KPX rcaron v 30 KPX rcaron y 30 KPX rcaron yacute 30 KPX rcaron ydieresis 30 KPX rcommaaccent a -10 KPX rcommaaccent aacute -10 KPX rcommaaccent abreve -10 KPX rcommaaccent acircumflex -10 KPX rcommaaccent adieresis -10 KPX rcommaaccent agrave -10 KPX rcommaaccent amacron -10 KPX rcommaaccent aogonek -10 KPX rcommaaccent aring -10 KPX rcommaaccent atilde -10 KPX rcommaaccent colon 30 KPX rcommaaccent comma -50 KPX rcommaaccent i 15 KPX rcommaaccent iacute 15 KPX rcommaaccent icircumflex 15 KPX rcommaaccent idieresis 15 KPX rcommaaccent igrave 15 KPX rcommaaccent imacron 15 KPX rcommaaccent iogonek 15 KPX rcommaaccent k 15 KPX rcommaaccent kcommaaccent 15 KPX rcommaaccent l 15 KPX rcommaaccent lacute 15 KPX rcommaaccent lcommaaccent 15 KPX rcommaaccent lslash 15 KPX rcommaaccent m 25 KPX rcommaaccent n 25 KPX rcommaaccent nacute 25 KPX rcommaaccent ncaron 25 KPX rcommaaccent ncommaaccent 25 KPX rcommaaccent ntilde 25 KPX rcommaaccent p 30 KPX rcommaaccent period -50 KPX rcommaaccent semicolon 30 KPX rcommaaccent t 40 KPX rcommaaccent tcommaaccent 40 KPX rcommaaccent u 15 KPX rcommaaccent uacute 15 KPX rcommaaccent ucircumflex 15 KPX rcommaaccent udieresis 15 KPX rcommaaccent ugrave 15 KPX rcommaaccent uhungarumlaut 15 KPX rcommaaccent umacron 15 KPX rcommaaccent uogonek 15 KPX rcommaaccent uring 15 KPX rcommaaccent v 30 KPX rcommaaccent y 30 KPX rcommaaccent yacute 30 KPX rcommaaccent ydieresis 30 KPX s comma -15 KPX s period -15 KPX s w -30 KPX sacute comma -15 KPX sacute period -15 KPX sacute w -30 KPX scaron comma -15 KPX scaron period -15 KPX scaron w -30 KPX scedilla comma -15 KPX scedilla period -15 KPX scedilla w -30 KPX scommaaccent comma -15 KPX scommaaccent period -15 KPX scommaaccent w -30 KPX semicolon space -50 KPX space T -50 KPX space Tcaron -50 KPX space Tcommaaccent -50 KPX space V -50 KPX space W -40 KPX space Y -90 KPX space Yacute -90 KPX space Ydieresis -90 KPX space quotedblleft -30 KPX space quoteleft -60 KPX v a -25 KPX v aacute -25 KPX v abreve -25 KPX v acircumflex -25 KPX v adieresis -25 KPX v agrave -25 KPX v amacron -25 KPX v aogonek -25 KPX v aring -25 KPX v atilde -25 KPX v comma -80 KPX v e -25 KPX v eacute -25 KPX v ecaron -25 KPX v ecircumflex -25 KPX v edieresis -25 KPX v edotaccent -25 KPX v egrave -25 KPX v emacron -25 KPX v eogonek -25 KPX v o -25 KPX v oacute -25 KPX v ocircumflex -25 KPX v odieresis -25 KPX v ograve -25 KPX v ohungarumlaut -25 KPX v omacron -25 KPX v oslash -25 KPX v otilde -25 KPX v period -80 KPX w a -15 KPX w aacute -15 KPX w abreve -15 KPX w acircumflex -15 KPX w adieresis -15 KPX w agrave -15 KPX w amacron -15 KPX w aogonek -15 KPX w aring -15 KPX w atilde -15 KPX w comma -60 KPX w e -10 KPX w eacute -10 KPX w ecaron -10 KPX w ecircumflex -10 KPX w edieresis -10 KPX w edotaccent -10 KPX w egrave -10 KPX w emacron -10 KPX w eogonek -10 KPX w o -10 KPX w oacute -10 KPX w ocircumflex -10 KPX w odieresis -10 KPX w ograve -10 KPX w ohungarumlaut -10 KPX w omacron -10 KPX w oslash -10 KPX w otilde -10 KPX w period -60 KPX x e -30 KPX x eacute -30 KPX x ecaron -30 KPX x ecircumflex -30 KPX x edieresis -30 KPX x edotaccent -30 KPX x egrave -30 KPX x emacron -30 KPX x eogonek -30 KPX y a -20 KPX y aacute -20 KPX y abreve -20 KPX y acircumflex -20 KPX y adieresis -20 KPX y agrave -20 KPX y amacron -20 KPX y aogonek -20 KPX y aring -20 KPX y atilde -20 KPX y comma -100 KPX y e -20 KPX y eacute -20 KPX y ecaron -20 KPX y ecircumflex -20 KPX y edieresis -20 KPX y edotaccent -20 KPX y egrave -20 KPX y emacron -20 KPX y eogonek -20 KPX y o -20 KPX y oacute -20 KPX y ocircumflex -20 KPX y odieresis -20 KPX y ograve -20 KPX y ohungarumlaut -20 KPX y omacron -20 KPX y oslash -20 KPX y otilde -20 KPX y period -100 KPX yacute a -20 KPX yacute aacute -20 KPX yacute abreve -20 KPX yacute acircumflex -20 KPX yacute adieresis -20 KPX yacute agrave -20 KPX yacute amacron -20 KPX yacute aogonek -20 KPX yacute aring -20 KPX yacute atilde -20 KPX yacute comma -100 KPX yacute e -20 KPX yacute eacute -20 KPX yacute ecaron -20 KPX yacute ecircumflex -20 KPX yacute edieresis -20 KPX yacute edotaccent -20 KPX yacute egrave -20 KPX yacute emacron -20 KPX yacute eogonek -20 KPX yacute o -20 KPX yacute oacute -20 KPX yacute ocircumflex -20 KPX yacute odieresis -20 KPX yacute ograve -20 KPX yacute ohungarumlaut -20 KPX yacute omacron -20 KPX yacute oslash -20 KPX yacute otilde -20 KPX yacute period -100 KPX ydieresis a -20 KPX ydieresis aacute -20 KPX ydieresis abreve -20 KPX ydieresis acircumflex -20 KPX ydieresis adieresis -20 KPX ydieresis agrave -20 KPX ydieresis amacron -20 KPX ydieresis aogonek -20 KPX ydieresis aring -20 KPX ydieresis atilde -20 KPX ydieresis comma -100 KPX ydieresis e -20 KPX ydieresis eacute -20 KPX ydieresis ecaron -20 KPX ydieresis ecircumflex -20 KPX ydieresis edieresis -20 KPX ydieresis edotaccent -20 KPX ydieresis egrave -20 KPX ydieresis emacron -20 KPX ydieresis eogonek -20 KPX ydieresis o -20 KPX ydieresis oacute -20 KPX ydieresis ocircumflex -20 KPX ydieresis odieresis -20 KPX ydieresis ograve -20 KPX ydieresis ohungarumlaut -20 KPX ydieresis omacron -20 KPX ydieresis oslash -20 KPX ydieresis otilde -20 KPX ydieresis period -100 KPX z e -15 KPX z eacute -15 KPX z ecaron -15 KPX z ecircumflex -15 KPX z edieresis -15 KPX z edotaccent -15 KPX z egrave -15 KPX z emacron -15 KPX z eogonek -15 KPX z o -15 KPX z oacute -15 KPX z ocircumflex -15 KPX z odieresis -15 KPX z ograve -15 KPX z ohungarumlaut -15 KPX z omacron -15 KPX z oslash -15 KPX z otilde -15 KPX zacute e -15 KPX zacute eacute -15 KPX zacute ecaron -15 KPX zacute ecircumflex -15 KPX zacute edieresis -15 KPX zacute edotaccent -15 KPX zacute egrave -15 KPX zacute emacron -15 KPX zacute eogonek -15 KPX zacute o -15 KPX zacute oacute -15 KPX zacute ocircumflex -15 KPX zacute odieresis -15 KPX zacute ograve -15 KPX zacute ohungarumlaut -15 KPX zacute omacron -15 KPX zacute oslash -15 KPX zacute otilde -15 KPX zcaron e -15 KPX zcaron eacute -15 KPX zcaron ecaron -15 KPX zcaron ecircumflex -15 KPX zcaron edieresis -15 KPX zcaron edotaccent -15 KPX zcaron egrave -15 KPX zcaron emacron -15 KPX zcaron eogonek -15 KPX zcaron o -15 KPX zcaron oacute -15 KPX zcaron ocircumflex -15 KPX zcaron odieresis -15 KPX zcaron ograve -15 KPX zcaron ohungarumlaut -15 KPX zcaron omacron -15 KPX zcaron oslash -15 KPX zcaron otilde -15 KPX zdotaccent e -15 KPX zdotaccent eacute -15 KPX zdotaccent ecaron -15 KPX zdotaccent ecircumflex -15 KPX zdotaccent edieresis -15 KPX zdotaccent edotaccent -15 KPX zdotaccent egrave -15 KPX zdotaccent emacron -15 KPX zdotaccent eogonek -15 KPX zdotaccent o -15 KPX zdotaccent oacute -15 KPX zdotaccent ocircumflex -15 KPX zdotaccent odieresis -15 KPX zdotaccent ograve -15 KPX zdotaccent ohungarumlaut -15 KPX zdotaccent omacron -15 KPX zdotaccent oslash -15 KPX zdotaccent otilde -15 EndKernPairs EndKernData EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Helvetica.afm000066400000000000000000002270371320103431700271420ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 12:38:23 1997 Comment UniqueID 43054 Comment VMusage 37069 48094 FontName Helvetica FullName Helvetica FamilyName Helvetica Weight Medium ItalicAngle 0 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -166 -225 1000 931 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme AdobeStandardEncoding CapHeight 718 XHeight 523 Ascender 718 Descender -207 StdHW 76 StdVW 88 StartCharMetrics 315 C 32 ; WX 278 ; N space ; B 0 0 0 0 ; C 33 ; WX 278 ; N exclam ; B 90 0 187 718 ; C 34 ; WX 355 ; N quotedbl ; B 70 463 285 718 ; C 35 ; WX 556 ; N numbersign ; B 28 0 529 688 ; C 36 ; WX 556 ; N dollar ; B 32 -115 520 775 ; C 37 ; WX 889 ; N percent ; B 39 -19 850 703 ; C 38 ; WX 667 ; N ampersand ; B 44 -15 645 718 ; C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; C 40 ; WX 333 ; N parenleft ; B 68 -207 299 733 ; C 41 ; WX 333 ; N parenright ; B 34 -207 265 733 ; C 42 ; WX 389 ; N asterisk ; B 39 431 349 718 ; C 43 ; WX 584 ; N plus ; B 39 0 545 505 ; C 44 ; WX 278 ; N comma ; B 87 -147 191 106 ; C 45 ; WX 333 ; N hyphen ; B 44 232 289 322 ; C 46 ; WX 278 ; N period ; B 87 0 191 106 ; C 47 ; WX 278 ; N slash ; B -17 -19 295 737 ; C 48 ; WX 556 ; N zero ; B 37 -19 519 703 ; C 49 ; WX 556 ; N one ; B 101 0 359 703 ; C 50 ; WX 556 ; N two ; B 26 0 507 703 ; C 51 ; WX 556 ; N three ; B 34 -19 522 703 ; C 52 ; WX 556 ; N four ; B 25 0 523 703 ; C 53 ; WX 556 ; N five ; B 32 -19 514 688 ; C 54 ; WX 556 ; N six ; B 38 -19 518 703 ; C 55 ; WX 556 ; N seven ; B 37 0 523 688 ; C 56 ; WX 556 ; N eight ; B 38 -19 517 703 ; C 57 ; WX 556 ; N nine ; B 42 -19 514 703 ; C 58 ; WX 278 ; N colon ; B 87 0 191 516 ; C 59 ; WX 278 ; N semicolon ; B 87 -147 191 516 ; C 60 ; WX 584 ; N less ; B 48 11 536 495 ; C 61 ; WX 584 ; N equal ; B 39 115 545 390 ; C 62 ; WX 584 ; N greater ; B 48 11 536 495 ; C 63 ; WX 556 ; N question ; B 56 0 492 727 ; C 64 ; WX 1015 ; N at ; B 147 -19 868 737 ; C 65 ; WX 667 ; N A ; B 14 0 654 718 ; C 66 ; WX 667 ; N B ; B 74 0 627 718 ; C 67 ; WX 722 ; N C ; B 44 -19 681 737 ; C 68 ; WX 722 ; N D ; B 81 0 674 718 ; C 69 ; WX 667 ; N E ; B 86 0 616 718 ; C 70 ; WX 611 ; N F ; B 86 0 583 718 ; C 71 ; WX 778 ; N G ; B 48 -19 704 737 ; C 72 ; WX 722 ; N H ; B 77 0 646 718 ; C 73 ; WX 278 ; N I ; B 91 0 188 718 ; C 74 ; WX 500 ; N J ; B 17 -19 428 718 ; C 75 ; WX 667 ; N K ; B 76 0 663 718 ; C 76 ; WX 556 ; N L ; B 76 0 537 718 ; C 77 ; WX 833 ; N M ; B 73 0 761 718 ; C 78 ; WX 722 ; N N ; B 76 0 646 718 ; C 79 ; WX 778 ; N O ; B 39 -19 739 737 ; C 80 ; WX 667 ; N P ; B 86 0 622 718 ; C 81 ; WX 778 ; N Q ; B 39 -56 739 737 ; C 82 ; WX 722 ; N R ; B 88 0 684 718 ; C 83 ; WX 667 ; N S ; B 49 -19 620 737 ; C 84 ; WX 611 ; N T ; B 14 0 597 718 ; C 85 ; WX 722 ; N U ; B 79 -19 644 718 ; C 86 ; WX 667 ; N V ; B 20 0 647 718 ; C 87 ; WX 944 ; N W ; B 16 0 928 718 ; C 88 ; WX 667 ; N X ; B 19 0 648 718 ; C 89 ; WX 667 ; N Y ; B 14 0 653 718 ; C 90 ; WX 611 ; N Z ; B 23 0 588 718 ; C 91 ; WX 278 ; N bracketleft ; B 63 -196 250 722 ; C 92 ; WX 278 ; N backslash ; B -17 -19 295 737 ; C 93 ; WX 278 ; N bracketright ; B 28 -196 215 722 ; C 94 ; WX 469 ; N asciicircum ; B -14 264 483 688 ; C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ; C 96 ; WX 222 ; N quoteleft ; B 65 470 169 725 ; C 97 ; WX 556 ; N a ; B 36 -15 530 538 ; C 98 ; WX 556 ; N b ; B 58 -15 517 718 ; C 99 ; WX 500 ; N c ; B 30 -15 477 538 ; C 100 ; WX 556 ; N d ; B 35 -15 499 718 ; C 101 ; WX 556 ; N e ; B 40 -15 516 538 ; C 102 ; WX 278 ; N f ; B 14 0 262 728 ; L i fi ; L l fl ; C 103 ; WX 556 ; N g ; B 40 -220 499 538 ; C 104 ; WX 556 ; N h ; B 65 0 491 718 ; C 105 ; WX 222 ; N i ; B 67 0 155 718 ; C 106 ; WX 222 ; N j ; B -16 -210 155 718 ; C 107 ; WX 500 ; N k ; B 67 0 501 718 ; C 108 ; WX 222 ; N l ; B 67 0 155 718 ; C 109 ; WX 833 ; N m ; B 65 0 769 538 ; C 110 ; WX 556 ; N n ; B 65 0 491 538 ; C 111 ; WX 556 ; N o ; B 35 -14 521 538 ; C 112 ; WX 556 ; N p ; B 58 -207 517 538 ; C 113 ; WX 556 ; N q ; B 35 -207 494 538 ; C 114 ; WX 333 ; N r ; B 77 0 332 538 ; C 115 ; WX 500 ; N s ; B 32 -15 464 538 ; C 116 ; WX 278 ; N t ; B 14 -7 257 669 ; C 117 ; WX 556 ; N u ; B 68 -15 489 523 ; C 118 ; WX 500 ; N v ; B 8 0 492 523 ; C 119 ; WX 722 ; N w ; B 14 0 709 523 ; C 120 ; WX 500 ; N x ; B 11 0 490 523 ; C 121 ; WX 500 ; N y ; B 11 -214 489 523 ; C 122 ; WX 500 ; N z ; B 31 0 469 523 ; C 123 ; WX 334 ; N braceleft ; B 42 -196 292 722 ; C 124 ; WX 260 ; N bar ; B 94 -225 167 775 ; C 125 ; WX 334 ; N braceright ; B 42 -196 292 722 ; C 126 ; WX 584 ; N asciitilde ; B 61 180 523 326 ; C 161 ; WX 333 ; N exclamdown ; B 118 -195 215 523 ; C 162 ; WX 556 ; N cent ; B 51 -115 513 623 ; C 163 ; WX 556 ; N sterling ; B 33 -16 539 718 ; C 164 ; WX 167 ; N fraction ; B -166 -19 333 703 ; C 165 ; WX 556 ; N yen ; B 3 0 553 688 ; C 166 ; WX 556 ; N florin ; B -11 -207 501 737 ; C 167 ; WX 556 ; N section ; B 43 -191 512 737 ; C 168 ; WX 556 ; N currency ; B 28 99 528 603 ; C 169 ; WX 191 ; N quotesingle ; B 59 463 132 718 ; C 170 ; WX 333 ; N quotedblleft ; B 38 470 307 725 ; C 171 ; WX 556 ; N guillemotleft ; B 97 108 459 446 ; C 172 ; WX 333 ; N guilsinglleft ; B 88 108 245 446 ; C 173 ; WX 333 ; N guilsinglright ; B 88 108 245 446 ; C 174 ; WX 500 ; N fi ; B 14 0 434 728 ; C 175 ; WX 500 ; N fl ; B 14 0 432 728 ; C 177 ; WX 556 ; N endash ; B 0 240 556 313 ; C 178 ; WX 556 ; N dagger ; B 43 -159 514 718 ; C 179 ; WX 556 ; N daggerdbl ; B 43 -159 514 718 ; C 180 ; WX 278 ; N periodcentered ; B 77 190 202 315 ; C 182 ; WX 537 ; N paragraph ; B 18 -173 497 718 ; C 183 ; WX 350 ; N bullet ; B 18 202 333 517 ; C 184 ; WX 222 ; N quotesinglbase ; B 53 -149 157 106 ; C 185 ; WX 333 ; N quotedblbase ; B 26 -149 295 106 ; C 186 ; WX 333 ; N quotedblright ; B 26 463 295 718 ; C 187 ; WX 556 ; N guillemotright ; B 97 108 459 446 ; C 188 ; WX 1000 ; N ellipsis ; B 115 0 885 106 ; C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 703 ; C 191 ; WX 611 ; N questiondown ; B 91 -201 527 525 ; C 193 ; WX 333 ; N grave ; B 14 593 211 734 ; C 194 ; WX 333 ; N acute ; B 122 593 319 734 ; C 195 ; WX 333 ; N circumflex ; B 21 593 312 734 ; C 196 ; WX 333 ; N tilde ; B -4 606 337 722 ; C 197 ; WX 333 ; N macron ; B 10 627 323 684 ; C 198 ; WX 333 ; N breve ; B 13 595 321 731 ; C 199 ; WX 333 ; N dotaccent ; B 121 604 212 706 ; C 200 ; WX 333 ; N dieresis ; B 40 604 293 706 ; C 202 ; WX 333 ; N ring ; B 75 572 259 756 ; C 203 ; WX 333 ; N cedilla ; B 45 -225 259 0 ; C 205 ; WX 333 ; N hungarumlaut ; B 31 593 409 734 ; C 206 ; WX 333 ; N ogonek ; B 73 -225 287 0 ; C 207 ; WX 333 ; N caron ; B 21 593 312 734 ; C 208 ; WX 1000 ; N emdash ; B 0 240 1000 313 ; C 225 ; WX 1000 ; N AE ; B 8 0 951 718 ; C 227 ; WX 370 ; N ordfeminine ; B 24 405 346 737 ; C 232 ; WX 556 ; N Lslash ; B -20 0 537 718 ; C 233 ; WX 778 ; N Oslash ; B 39 -19 740 737 ; C 234 ; WX 1000 ; N OE ; B 36 -19 965 737 ; C 235 ; WX 365 ; N ordmasculine ; B 25 405 341 737 ; C 241 ; WX 889 ; N ae ; B 36 -15 847 538 ; C 245 ; WX 278 ; N dotlessi ; B 95 0 183 523 ; C 248 ; WX 222 ; N lslash ; B -20 0 242 718 ; C 249 ; WX 611 ; N oslash ; B 28 -22 537 545 ; C 250 ; WX 944 ; N oe ; B 35 -15 902 538 ; C 251 ; WX 611 ; N germandbls ; B 67 -15 571 728 ; C -1 ; WX 278 ; N Idieresis ; B 13 0 266 901 ; C -1 ; WX 556 ; N eacute ; B 40 -15 516 734 ; C -1 ; WX 556 ; N abreve ; B 36 -15 530 731 ; C -1 ; WX 556 ; N uhungarumlaut ; B 68 -15 521 734 ; C -1 ; WX 556 ; N ecaron ; B 40 -15 516 734 ; C -1 ; WX 667 ; N Ydieresis ; B 14 0 653 901 ; C -1 ; WX 584 ; N divide ; B 39 -19 545 524 ; C -1 ; WX 667 ; N Yacute ; B 14 0 653 929 ; C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ; C -1 ; WX 556 ; N aacute ; B 36 -15 530 734 ; C -1 ; WX 722 ; N Ucircumflex ; B 79 -19 644 929 ; C -1 ; WX 500 ; N yacute ; B 11 -214 489 734 ; C -1 ; WX 500 ; N scommaaccent ; B 32 -225 464 538 ; C -1 ; WX 556 ; N ecircumflex ; B 40 -15 516 734 ; C -1 ; WX 722 ; N Uring ; B 79 -19 644 931 ; C -1 ; WX 722 ; N Udieresis ; B 79 -19 644 901 ; C -1 ; WX 556 ; N aogonek ; B 36 -220 547 538 ; C -1 ; WX 722 ; N Uacute ; B 79 -19 644 929 ; C -1 ; WX 556 ; N uogonek ; B 68 -225 519 523 ; C -1 ; WX 667 ; N Edieresis ; B 86 0 616 901 ; C -1 ; WX 722 ; N Dcroat ; B 0 0 674 718 ; C -1 ; WX 250 ; N commaaccent ; B 87 -225 181 -40 ; C -1 ; WX 737 ; N copyright ; B -14 -19 752 737 ; C -1 ; WX 667 ; N Emacron ; B 86 0 616 879 ; C -1 ; WX 500 ; N ccaron ; B 30 -15 477 734 ; C -1 ; WX 556 ; N aring ; B 36 -15 530 756 ; C -1 ; WX 722 ; N Ncommaaccent ; B 76 -225 646 718 ; C -1 ; WX 222 ; N lacute ; B 67 0 264 929 ; C -1 ; WX 556 ; N agrave ; B 36 -15 530 734 ; C -1 ; WX 611 ; N Tcommaaccent ; B 14 -225 597 718 ; C -1 ; WX 722 ; N Cacute ; B 44 -19 681 929 ; C -1 ; WX 556 ; N atilde ; B 36 -15 530 722 ; C -1 ; WX 667 ; N Edotaccent ; B 86 0 616 901 ; C -1 ; WX 500 ; N scaron ; B 32 -15 464 734 ; C -1 ; WX 500 ; N scedilla ; B 32 -225 464 538 ; C -1 ; WX 278 ; N iacute ; B 95 0 292 734 ; C -1 ; WX 471 ; N lozenge ; B 10 0 462 728 ; C -1 ; WX 722 ; N Rcaron ; B 88 0 684 929 ; C -1 ; WX 778 ; N Gcommaaccent ; B 48 -225 704 737 ; C -1 ; WX 556 ; N ucircumflex ; B 68 -15 489 734 ; C -1 ; WX 556 ; N acircumflex ; B 36 -15 530 734 ; C -1 ; WX 667 ; N Amacron ; B 14 0 654 879 ; C -1 ; WX 333 ; N rcaron ; B 61 0 352 734 ; C -1 ; WX 500 ; N ccedilla ; B 30 -225 477 538 ; C -1 ; WX 611 ; N Zdotaccent ; B 23 0 588 901 ; C -1 ; WX 667 ; N Thorn ; B 86 0 622 718 ; C -1 ; WX 778 ; N Omacron ; B 39 -19 739 879 ; C -1 ; WX 722 ; N Racute ; B 88 0 684 929 ; C -1 ; WX 667 ; N Sacute ; B 49 -19 620 929 ; C -1 ; WX 643 ; N dcaron ; B 35 -15 655 718 ; C -1 ; WX 722 ; N Umacron ; B 79 -19 644 879 ; C -1 ; WX 556 ; N uring ; B 68 -15 489 756 ; C -1 ; WX 333 ; N threesuperior ; B 5 270 325 703 ; C -1 ; WX 778 ; N Ograve ; B 39 -19 739 929 ; C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ; C -1 ; WX 667 ; N Abreve ; B 14 0 654 926 ; C -1 ; WX 584 ; N multiply ; B 39 0 545 506 ; C -1 ; WX 556 ; N uacute ; B 68 -15 489 734 ; C -1 ; WX 611 ; N Tcaron ; B 14 0 597 929 ; C -1 ; WX 476 ; N partialdiff ; B 13 -38 463 714 ; C -1 ; WX 500 ; N ydieresis ; B 11 -214 489 706 ; C -1 ; WX 722 ; N Nacute ; B 76 0 646 929 ; C -1 ; WX 278 ; N icircumflex ; B -6 0 285 734 ; C -1 ; WX 667 ; N Ecircumflex ; B 86 0 616 929 ; C -1 ; WX 556 ; N adieresis ; B 36 -15 530 706 ; C -1 ; WX 556 ; N edieresis ; B 40 -15 516 706 ; C -1 ; WX 500 ; N cacute ; B 30 -15 477 734 ; C -1 ; WX 556 ; N nacute ; B 65 0 491 734 ; C -1 ; WX 556 ; N umacron ; B 68 -15 489 684 ; C -1 ; WX 722 ; N Ncaron ; B 76 0 646 929 ; C -1 ; WX 278 ; N Iacute ; B 91 0 292 929 ; C -1 ; WX 584 ; N plusminus ; B 39 0 545 506 ; C -1 ; WX 260 ; N brokenbar ; B 94 -150 167 700 ; C -1 ; WX 737 ; N registered ; B -14 -19 752 737 ; C -1 ; WX 778 ; N Gbreve ; B 48 -19 704 926 ; C -1 ; WX 278 ; N Idotaccent ; B 91 0 188 901 ; C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ; C -1 ; WX 667 ; N Egrave ; B 86 0 616 929 ; C -1 ; WX 333 ; N racute ; B 77 0 332 734 ; C -1 ; WX 556 ; N omacron ; B 35 -14 521 684 ; C -1 ; WX 611 ; N Zacute ; B 23 0 588 929 ; C -1 ; WX 611 ; N Zcaron ; B 23 0 588 929 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 523 674 ; C -1 ; WX 722 ; N Eth ; B 0 0 674 718 ; C -1 ; WX 722 ; N Ccedilla ; B 44 -225 681 737 ; C -1 ; WX 222 ; N lcommaaccent ; B 67 -225 167 718 ; C -1 ; WX 317 ; N tcaron ; B 14 -7 329 808 ; C -1 ; WX 556 ; N eogonek ; B 40 -225 516 538 ; C -1 ; WX 722 ; N Uogonek ; B 79 -225 644 718 ; C -1 ; WX 667 ; N Aacute ; B 14 0 654 929 ; C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ; C -1 ; WX 556 ; N egrave ; B 40 -15 516 734 ; C -1 ; WX 500 ; N zacute ; B 31 0 469 734 ; C -1 ; WX 222 ; N iogonek ; B -31 -225 183 718 ; C -1 ; WX 778 ; N Oacute ; B 39 -19 739 929 ; C -1 ; WX 556 ; N oacute ; B 35 -14 521 734 ; C -1 ; WX 556 ; N amacron ; B 36 -15 530 684 ; C -1 ; WX 500 ; N sacute ; B 32 -15 464 734 ; C -1 ; WX 278 ; N idieresis ; B 13 0 266 706 ; C -1 ; WX 778 ; N Ocircumflex ; B 39 -19 739 929 ; C -1 ; WX 722 ; N Ugrave ; B 79 -19 644 929 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C -1 ; WX 556 ; N thorn ; B 58 -207 517 718 ; C -1 ; WX 333 ; N twosuperior ; B 4 281 323 703 ; C -1 ; WX 778 ; N Odieresis ; B 39 -19 739 901 ; C -1 ; WX 556 ; N mu ; B 68 -207 489 523 ; C -1 ; WX 278 ; N igrave ; B -13 0 184 734 ; C -1 ; WX 556 ; N ohungarumlaut ; B 35 -14 521 734 ; C -1 ; WX 667 ; N Eogonek ; B 86 -220 633 718 ; C -1 ; WX 556 ; N dcroat ; B 35 -15 550 718 ; C -1 ; WX 834 ; N threequarters ; B 45 -19 810 703 ; C -1 ; WX 667 ; N Scedilla ; B 49 -225 620 737 ; C -1 ; WX 299 ; N lcaron ; B 67 0 311 718 ; C -1 ; WX 667 ; N Kcommaaccent ; B 76 -225 663 718 ; C -1 ; WX 556 ; N Lacute ; B 76 0 537 929 ; C -1 ; WX 1000 ; N trademark ; B 46 306 903 718 ; C -1 ; WX 556 ; N edotaccent ; B 40 -15 516 706 ; C -1 ; WX 278 ; N Igrave ; B -13 0 188 929 ; C -1 ; WX 278 ; N Imacron ; B -17 0 296 879 ; C -1 ; WX 556 ; N Lcaron ; B 76 0 537 718 ; C -1 ; WX 834 ; N onehalf ; B 43 -19 773 703 ; C -1 ; WX 549 ; N lessequal ; B 26 0 523 674 ; C -1 ; WX 556 ; N ocircumflex ; B 35 -14 521 734 ; C -1 ; WX 556 ; N ntilde ; B 65 0 491 722 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 79 -19 644 929 ; C -1 ; WX 667 ; N Eacute ; B 86 0 616 929 ; C -1 ; WX 556 ; N emacron ; B 40 -15 516 684 ; C -1 ; WX 556 ; N gbreve ; B 40 -220 499 731 ; C -1 ; WX 834 ; N onequarter ; B 73 -19 756 703 ; C -1 ; WX 667 ; N Scaron ; B 49 -19 620 929 ; C -1 ; WX 667 ; N Scommaaccent ; B 49 -225 620 737 ; C -1 ; WX 778 ; N Ohungarumlaut ; B 39 -19 739 929 ; C -1 ; WX 400 ; N degree ; B 54 411 346 703 ; C -1 ; WX 556 ; N ograve ; B 35 -14 521 734 ; C -1 ; WX 722 ; N Ccaron ; B 44 -19 681 929 ; C -1 ; WX 556 ; N ugrave ; B 68 -15 489 734 ; C -1 ; WX 453 ; N radical ; B -4 -80 458 762 ; C -1 ; WX 722 ; N Dcaron ; B 81 0 674 929 ; C -1 ; WX 333 ; N rcommaaccent ; B 77 -225 332 538 ; C -1 ; WX 722 ; N Ntilde ; B 76 0 646 917 ; C -1 ; WX 556 ; N otilde ; B 35 -14 521 722 ; C -1 ; WX 722 ; N Rcommaaccent ; B 88 -225 684 718 ; C -1 ; WX 556 ; N Lcommaaccent ; B 76 -225 537 718 ; C -1 ; WX 667 ; N Atilde ; B 14 0 654 917 ; C -1 ; WX 667 ; N Aogonek ; B 14 -225 654 718 ; C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ; C -1 ; WX 778 ; N Otilde ; B 39 -19 739 917 ; C -1 ; WX 500 ; N zdotaccent ; B 31 0 469 706 ; C -1 ; WX 667 ; N Ecaron ; B 86 0 616 929 ; C -1 ; WX 278 ; N Iogonek ; B -3 -225 211 718 ; C -1 ; WX 500 ; N kcommaaccent ; B 67 -225 501 718 ; C -1 ; WX 584 ; N minus ; B 39 216 545 289 ; C -1 ; WX 278 ; N Icircumflex ; B -6 0 285 929 ; C -1 ; WX 556 ; N ncaron ; B 65 0 491 734 ; C -1 ; WX 278 ; N tcommaaccent ; B 14 -225 257 669 ; C -1 ; WX 584 ; N logicalnot ; B 39 108 545 390 ; C -1 ; WX 556 ; N odieresis ; B 35 -14 521 706 ; C -1 ; WX 556 ; N udieresis ; B 68 -15 489 706 ; C -1 ; WX 549 ; N notequal ; B 12 -35 537 551 ; C -1 ; WX 556 ; N gcommaaccent ; B 40 -220 499 822 ; C -1 ; WX 556 ; N eth ; B 35 -15 522 737 ; C -1 ; WX 500 ; N zcaron ; B 31 0 469 734 ; C -1 ; WX 556 ; N ncommaaccent ; B 65 -225 491 538 ; C -1 ; WX 333 ; N onesuperior ; B 43 281 222 703 ; C -1 ; WX 278 ; N imacron ; B 5 0 272 684 ; C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2705 KPX A C -30 KPX A Cacute -30 KPX A Ccaron -30 KPX A Ccedilla -30 KPX A G -30 KPX A Gbreve -30 KPX A Gcommaaccent -30 KPX A O -30 KPX A Oacute -30 KPX A Ocircumflex -30 KPX A Odieresis -30 KPX A Ograve -30 KPX A Ohungarumlaut -30 KPX A Omacron -30 KPX A Oslash -30 KPX A Otilde -30 KPX A Q -30 KPX A T -120 KPX A Tcaron -120 KPX A Tcommaaccent -120 KPX A U -50 KPX A Uacute -50 KPX A Ucircumflex -50 KPX A Udieresis -50 KPX A Ugrave -50 KPX A Uhungarumlaut -50 KPX A Umacron -50 KPX A Uogonek -50 KPX A Uring -50 KPX A V -70 KPX A W -50 KPX A Y -100 KPX A Yacute -100 KPX A Ydieresis -100 KPX A u -30 KPX A uacute -30 KPX A ucircumflex -30 KPX A udieresis -30 KPX A ugrave -30 KPX A uhungarumlaut -30 KPX A umacron -30 KPX A uogonek -30 KPX A uring -30 KPX A v -40 KPX A w -40 KPX A y -40 KPX A yacute -40 KPX A ydieresis -40 KPX Aacute C -30 KPX Aacute Cacute -30 KPX Aacute Ccaron -30 KPX Aacute Ccedilla -30 KPX Aacute G -30 KPX Aacute Gbreve -30 KPX Aacute Gcommaaccent -30 KPX Aacute O -30 KPX Aacute Oacute -30 KPX Aacute Ocircumflex -30 KPX Aacute Odieresis -30 KPX Aacute Ograve -30 KPX Aacute Ohungarumlaut -30 KPX Aacute Omacron -30 KPX Aacute Oslash -30 KPX Aacute Otilde -30 KPX Aacute Q -30 KPX Aacute T -120 KPX Aacute Tcaron -120 KPX Aacute Tcommaaccent -120 KPX Aacute U -50 KPX Aacute Uacute -50 KPX Aacute Ucircumflex -50 KPX Aacute Udieresis -50 KPX Aacute Ugrave -50 KPX Aacute Uhungarumlaut -50 KPX Aacute Umacron -50 KPX Aacute Uogonek -50 KPX Aacute Uring -50 KPX Aacute V -70 KPX Aacute W -50 KPX Aacute Y -100 KPX Aacute Yacute -100 KPX Aacute Ydieresis -100 KPX Aacute u -30 KPX Aacute uacute -30 KPX Aacute ucircumflex -30 KPX Aacute udieresis -30 KPX Aacute ugrave -30 KPX Aacute uhungarumlaut -30 KPX Aacute umacron -30 KPX Aacute uogonek -30 KPX Aacute uring -30 KPX Aacute v -40 KPX Aacute w -40 KPX Aacute y -40 KPX Aacute yacute -40 KPX Aacute ydieresis -40 KPX Abreve C -30 KPX Abreve Cacute -30 KPX Abreve Ccaron -30 KPX Abreve Ccedilla -30 KPX Abreve G -30 KPX Abreve Gbreve -30 KPX Abreve Gcommaaccent -30 KPX Abreve O -30 KPX Abreve Oacute -30 KPX Abreve Ocircumflex -30 KPX Abreve Odieresis -30 KPX Abreve Ograve -30 KPX Abreve Ohungarumlaut -30 KPX Abreve Omacron -30 KPX Abreve Oslash -30 KPX Abreve Otilde -30 KPX Abreve Q -30 KPX Abreve T -120 KPX Abreve Tcaron -120 KPX Abreve Tcommaaccent -120 KPX Abreve U -50 KPX Abreve Uacute -50 KPX Abreve Ucircumflex -50 KPX Abreve Udieresis -50 KPX Abreve Ugrave -50 KPX Abreve Uhungarumlaut -50 KPX Abreve Umacron -50 KPX Abreve Uogonek -50 KPX Abreve Uring -50 KPX Abreve V -70 KPX Abreve W -50 KPX Abreve Y -100 KPX Abreve Yacute -100 KPX Abreve Ydieresis -100 KPX Abreve u -30 KPX Abreve uacute -30 KPX Abreve ucircumflex -30 KPX Abreve udieresis -30 KPX Abreve ugrave -30 KPX Abreve uhungarumlaut -30 KPX Abreve umacron -30 KPX Abreve uogonek -30 KPX Abreve uring -30 KPX Abreve v -40 KPX Abreve w -40 KPX Abreve y -40 KPX Abreve yacute -40 KPX Abreve ydieresis -40 KPX Acircumflex C -30 KPX Acircumflex Cacute -30 KPX Acircumflex Ccaron -30 KPX Acircumflex Ccedilla -30 KPX Acircumflex G -30 KPX Acircumflex Gbreve -30 KPX Acircumflex Gcommaaccent -30 KPX Acircumflex O -30 KPX Acircumflex Oacute -30 KPX Acircumflex Ocircumflex -30 KPX Acircumflex Odieresis -30 KPX Acircumflex Ograve -30 KPX Acircumflex Ohungarumlaut -30 KPX Acircumflex Omacron -30 KPX Acircumflex Oslash -30 KPX Acircumflex Otilde -30 KPX Acircumflex Q -30 KPX Acircumflex T -120 KPX Acircumflex Tcaron -120 KPX Acircumflex Tcommaaccent -120 KPX Acircumflex U -50 KPX Acircumflex Uacute -50 KPX Acircumflex Ucircumflex -50 KPX Acircumflex Udieresis -50 KPX Acircumflex Ugrave -50 KPX Acircumflex Uhungarumlaut -50 KPX Acircumflex Umacron -50 KPX Acircumflex Uogonek -50 KPX Acircumflex Uring -50 KPX Acircumflex V -70 KPX Acircumflex W -50 KPX Acircumflex Y -100 KPX Acircumflex Yacute -100 KPX Acircumflex Ydieresis -100 KPX Acircumflex u -30 KPX Acircumflex uacute -30 KPX Acircumflex ucircumflex -30 KPX Acircumflex udieresis -30 KPX Acircumflex ugrave -30 KPX Acircumflex uhungarumlaut -30 KPX Acircumflex umacron -30 KPX Acircumflex uogonek -30 KPX Acircumflex uring -30 KPX Acircumflex v -40 KPX Acircumflex w -40 KPX Acircumflex y -40 KPX Acircumflex yacute -40 KPX Acircumflex ydieresis -40 KPX Adieresis C -30 KPX Adieresis Cacute -30 KPX Adieresis Ccaron -30 KPX Adieresis Ccedilla -30 KPX Adieresis G -30 KPX Adieresis Gbreve -30 KPX Adieresis Gcommaaccent -30 KPX Adieresis O -30 KPX Adieresis Oacute -30 KPX Adieresis Ocircumflex -30 KPX Adieresis Odieresis -30 KPX Adieresis Ograve -30 KPX Adieresis Ohungarumlaut -30 KPX Adieresis Omacron -30 KPX Adieresis Oslash -30 KPX Adieresis Otilde -30 KPX Adieresis Q -30 KPX Adieresis T -120 KPX Adieresis Tcaron -120 KPX Adieresis Tcommaaccent -120 KPX Adieresis U -50 KPX Adieresis Uacute -50 KPX Adieresis Ucircumflex -50 KPX Adieresis Udieresis -50 KPX Adieresis Ugrave -50 KPX Adieresis Uhungarumlaut -50 KPX Adieresis Umacron -50 KPX Adieresis Uogonek -50 KPX Adieresis Uring -50 KPX Adieresis V -70 KPX Adieresis W -50 KPX Adieresis Y -100 KPX Adieresis Yacute -100 KPX Adieresis Ydieresis -100 KPX Adieresis u -30 KPX Adieresis uacute -30 KPX Adieresis ucircumflex -30 KPX Adieresis udieresis -30 KPX Adieresis ugrave -30 KPX Adieresis uhungarumlaut -30 KPX Adieresis umacron -30 KPX Adieresis uogonek -30 KPX Adieresis uring -30 KPX Adieresis v -40 KPX Adieresis w -40 KPX Adieresis y -40 KPX Adieresis yacute -40 KPX Adieresis ydieresis -40 KPX Agrave C -30 KPX Agrave Cacute -30 KPX Agrave Ccaron -30 KPX Agrave Ccedilla -30 KPX Agrave G -30 KPX Agrave Gbreve -30 KPX Agrave Gcommaaccent -30 KPX Agrave O -30 KPX Agrave Oacute -30 KPX Agrave Ocircumflex -30 KPX Agrave Odieresis -30 KPX Agrave Ograve -30 KPX Agrave Ohungarumlaut -30 KPX Agrave Omacron -30 KPX Agrave Oslash -30 KPX Agrave Otilde -30 KPX Agrave Q -30 KPX Agrave T -120 KPX Agrave Tcaron -120 KPX Agrave Tcommaaccent -120 KPX Agrave U -50 KPX Agrave Uacute -50 KPX Agrave Ucircumflex -50 KPX Agrave Udieresis -50 KPX Agrave Ugrave -50 KPX Agrave Uhungarumlaut -50 KPX Agrave Umacron -50 KPX Agrave Uogonek -50 KPX Agrave Uring -50 KPX Agrave V -70 KPX Agrave W -50 KPX Agrave Y -100 KPX Agrave Yacute -100 KPX Agrave Ydieresis -100 KPX Agrave u -30 KPX Agrave uacute -30 KPX Agrave ucircumflex -30 KPX Agrave udieresis -30 KPX Agrave ugrave -30 KPX Agrave uhungarumlaut -30 KPX Agrave umacron -30 KPX Agrave uogonek -30 KPX Agrave uring -30 KPX Agrave v -40 KPX Agrave w -40 KPX Agrave y -40 KPX Agrave yacute -40 KPX Agrave ydieresis -40 KPX Amacron C -30 KPX Amacron Cacute -30 KPX Amacron Ccaron -30 KPX Amacron Ccedilla -30 KPX Amacron G -30 KPX Amacron Gbreve -30 KPX Amacron Gcommaaccent -30 KPX Amacron O -30 KPX Amacron Oacute -30 KPX Amacron Ocircumflex -30 KPX Amacron Odieresis -30 KPX Amacron Ograve -30 KPX Amacron Ohungarumlaut -30 KPX Amacron Omacron -30 KPX Amacron Oslash -30 KPX Amacron Otilde -30 KPX Amacron Q -30 KPX Amacron T -120 KPX Amacron Tcaron -120 KPX Amacron Tcommaaccent -120 KPX Amacron U -50 KPX Amacron Uacute -50 KPX Amacron Ucircumflex -50 KPX Amacron Udieresis -50 KPX Amacron Ugrave -50 KPX Amacron Uhungarumlaut -50 KPX Amacron Umacron -50 KPX Amacron Uogonek -50 KPX Amacron Uring -50 KPX Amacron V -70 KPX Amacron W -50 KPX Amacron Y -100 KPX Amacron Yacute -100 KPX Amacron Ydieresis -100 KPX Amacron u -30 KPX Amacron uacute -30 KPX Amacron ucircumflex -30 KPX Amacron udieresis -30 KPX Amacron ugrave -30 KPX Amacron uhungarumlaut -30 KPX Amacron umacron -30 KPX Amacron uogonek -30 KPX Amacron uring -30 KPX Amacron v -40 KPX Amacron w -40 KPX Amacron y -40 KPX Amacron yacute -40 KPX Amacron ydieresis -40 KPX Aogonek C -30 KPX Aogonek Cacute -30 KPX Aogonek Ccaron -30 KPX Aogonek Ccedilla -30 KPX Aogonek G -30 KPX Aogonek Gbreve -30 KPX Aogonek Gcommaaccent -30 KPX Aogonek O -30 KPX Aogonek Oacute -30 KPX Aogonek Ocircumflex -30 KPX Aogonek Odieresis -30 KPX Aogonek Ograve -30 KPX Aogonek Ohungarumlaut -30 KPX Aogonek Omacron -30 KPX Aogonek Oslash -30 KPX Aogonek Otilde -30 KPX Aogonek Q -30 KPX Aogonek T -120 KPX Aogonek Tcaron -120 KPX Aogonek Tcommaaccent -120 KPX Aogonek U -50 KPX Aogonek Uacute -50 KPX Aogonek Ucircumflex -50 KPX Aogonek Udieresis -50 KPX Aogonek Ugrave -50 KPX Aogonek Uhungarumlaut -50 KPX Aogonek Umacron -50 KPX Aogonek Uogonek -50 KPX Aogonek Uring -50 KPX Aogonek V -70 KPX Aogonek W -50 KPX Aogonek Y -100 KPX Aogonek Yacute -100 KPX Aogonek Ydieresis -100 KPX Aogonek u -30 KPX Aogonek uacute -30 KPX Aogonek ucircumflex -30 KPX Aogonek udieresis -30 KPX Aogonek ugrave -30 KPX Aogonek uhungarumlaut -30 KPX Aogonek umacron -30 KPX Aogonek uogonek -30 KPX Aogonek uring -30 KPX Aogonek v -40 KPX Aogonek w -40 KPX Aogonek y -40 KPX Aogonek yacute -40 KPX Aogonek ydieresis -40 KPX Aring C -30 KPX Aring Cacute -30 KPX Aring Ccaron -30 KPX Aring Ccedilla -30 KPX Aring G -30 KPX Aring Gbreve -30 KPX Aring Gcommaaccent -30 KPX Aring O -30 KPX Aring Oacute -30 KPX Aring Ocircumflex -30 KPX Aring Odieresis -30 KPX Aring Ograve -30 KPX Aring Ohungarumlaut -30 KPX Aring Omacron -30 KPX Aring Oslash -30 KPX Aring Otilde -30 KPX Aring Q -30 KPX Aring T -120 KPX Aring Tcaron -120 KPX Aring Tcommaaccent -120 KPX Aring U -50 KPX Aring Uacute -50 KPX Aring Ucircumflex -50 KPX Aring Udieresis -50 KPX Aring Ugrave -50 KPX Aring Uhungarumlaut -50 KPX Aring Umacron -50 KPX Aring Uogonek -50 KPX Aring Uring -50 KPX Aring V -70 KPX Aring W -50 KPX Aring Y -100 KPX Aring Yacute -100 KPX Aring Ydieresis -100 KPX Aring u -30 KPX Aring uacute -30 KPX Aring ucircumflex -30 KPX Aring udieresis -30 KPX Aring ugrave -30 KPX Aring uhungarumlaut -30 KPX Aring umacron -30 KPX Aring uogonek -30 KPX Aring uring -30 KPX Aring v -40 KPX Aring w -40 KPX Aring y -40 KPX Aring yacute -40 KPX Aring ydieresis -40 KPX Atilde C -30 KPX Atilde Cacute -30 KPX Atilde Ccaron -30 KPX Atilde Ccedilla -30 KPX Atilde G -30 KPX Atilde Gbreve -30 KPX Atilde Gcommaaccent -30 KPX Atilde O -30 KPX Atilde Oacute -30 KPX Atilde Ocircumflex -30 KPX Atilde Odieresis -30 KPX Atilde Ograve -30 KPX Atilde Ohungarumlaut -30 KPX Atilde Omacron -30 KPX Atilde Oslash -30 KPX Atilde Otilde -30 KPX Atilde Q -30 KPX Atilde T -120 KPX Atilde Tcaron -120 KPX Atilde Tcommaaccent -120 KPX Atilde U -50 KPX Atilde Uacute -50 KPX Atilde Ucircumflex -50 KPX Atilde Udieresis -50 KPX Atilde Ugrave -50 KPX Atilde Uhungarumlaut -50 KPX Atilde Umacron -50 KPX Atilde Uogonek -50 KPX Atilde Uring -50 KPX Atilde V -70 KPX Atilde W -50 KPX Atilde Y -100 KPX Atilde Yacute -100 KPX Atilde Ydieresis -100 KPX Atilde u -30 KPX Atilde uacute -30 KPX Atilde ucircumflex -30 KPX Atilde udieresis -30 KPX Atilde ugrave -30 KPX Atilde uhungarumlaut -30 KPX Atilde umacron -30 KPX Atilde uogonek -30 KPX Atilde uring -30 KPX Atilde v -40 KPX Atilde w -40 KPX Atilde y -40 KPX Atilde yacute -40 KPX Atilde ydieresis -40 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX B comma -20 KPX B period -20 KPX C comma -30 KPX C period -30 KPX Cacute comma -30 KPX Cacute period -30 KPX Ccaron comma -30 KPX Ccaron period -30 KPX Ccedilla comma -30 KPX Ccedilla period -30 KPX D A -40 KPX D Aacute -40 KPX D Abreve -40 KPX D Acircumflex -40 KPX D Adieresis -40 KPX D Agrave -40 KPX D Amacron -40 KPX D Aogonek -40 KPX D Aring -40 KPX D Atilde -40 KPX D V -70 KPX D W -40 KPX D Y -90 KPX D Yacute -90 KPX D Ydieresis -90 KPX D comma -70 KPX D period -70 KPX Dcaron A -40 KPX Dcaron Aacute -40 KPX Dcaron Abreve -40 KPX Dcaron Acircumflex -40 KPX Dcaron Adieresis -40 KPX Dcaron Agrave -40 KPX Dcaron Amacron -40 KPX Dcaron Aogonek -40 KPX Dcaron Aring -40 KPX Dcaron Atilde -40 KPX Dcaron V -70 KPX Dcaron W -40 KPX Dcaron Y -90 KPX Dcaron Yacute -90 KPX Dcaron Ydieresis -90 KPX Dcaron comma -70 KPX Dcaron period -70 KPX Dcroat A -40 KPX Dcroat Aacute -40 KPX Dcroat Abreve -40 KPX Dcroat Acircumflex -40 KPX Dcroat Adieresis -40 KPX Dcroat Agrave -40 KPX Dcroat Amacron -40 KPX Dcroat Aogonek -40 KPX Dcroat Aring -40 KPX Dcroat Atilde -40 KPX Dcroat V -70 KPX Dcroat W -40 KPX Dcroat Y -90 KPX Dcroat Yacute -90 KPX Dcroat Ydieresis -90 KPX Dcroat comma -70 KPX Dcroat period -70 KPX F A -80 KPX F Aacute -80 KPX F Abreve -80 KPX F Acircumflex -80 KPX F Adieresis -80 KPX F Agrave -80 KPX F Amacron -80 KPX F Aogonek -80 KPX F Aring -80 KPX F Atilde -80 KPX F a -50 KPX F aacute -50 KPX F abreve -50 KPX F acircumflex -50 KPX F adieresis -50 KPX F agrave -50 KPX F amacron -50 KPX F aogonek -50 KPX F aring -50 KPX F atilde -50 KPX F comma -150 KPX F e -30 KPX F eacute -30 KPX F ecaron -30 KPX F ecircumflex -30 KPX F edieresis -30 KPX F edotaccent -30 KPX F egrave -30 KPX F emacron -30 KPX F eogonek -30 KPX F o -30 KPX F oacute -30 KPX F ocircumflex -30 KPX F odieresis -30 KPX F ograve -30 KPX F ohungarumlaut -30 KPX F omacron -30 KPX F oslash -30 KPX F otilde -30 KPX F period -150 KPX F r -45 KPX F racute -45 KPX F rcaron -45 KPX F rcommaaccent -45 KPX J A -20 KPX J Aacute -20 KPX J Abreve -20 KPX J Acircumflex -20 KPX J Adieresis -20 KPX J Agrave -20 KPX J Amacron -20 KPX J Aogonek -20 KPX J Aring -20 KPX J Atilde -20 KPX J a -20 KPX J aacute -20 KPX J abreve -20 KPX J acircumflex -20 KPX J adieresis -20 KPX J agrave -20 KPX J amacron -20 KPX J aogonek -20 KPX J aring -20 KPX J atilde -20 KPX J comma -30 KPX J period -30 KPX J u -20 KPX J uacute -20 KPX J ucircumflex -20 KPX J udieresis -20 KPX J ugrave -20 KPX J uhungarumlaut -20 KPX J umacron -20 KPX J uogonek -20 KPX J uring -20 KPX K O -50 KPX K Oacute -50 KPX K Ocircumflex -50 KPX K Odieresis -50 KPX K Ograve -50 KPX K Ohungarumlaut -50 KPX K Omacron -50 KPX K Oslash -50 KPX K Otilde -50 KPX K e -40 KPX K eacute -40 KPX K ecaron -40 KPX K ecircumflex -40 KPX K edieresis -40 KPX K edotaccent -40 KPX K egrave -40 KPX K emacron -40 KPX K eogonek -40 KPX K o -40 KPX K oacute -40 KPX K ocircumflex -40 KPX K odieresis -40 KPX K ograve -40 KPX K ohungarumlaut -40 KPX K omacron -40 KPX K oslash -40 KPX K otilde -40 KPX K u -30 KPX K uacute -30 KPX K ucircumflex -30 KPX K udieresis -30 KPX K ugrave -30 KPX K uhungarumlaut -30 KPX K umacron -30 KPX K uogonek -30 KPX K uring -30 KPX K y -50 KPX K yacute -50 KPX K ydieresis -50 KPX Kcommaaccent O -50 KPX Kcommaaccent Oacute -50 KPX Kcommaaccent Ocircumflex -50 KPX Kcommaaccent Odieresis -50 KPX Kcommaaccent Ograve -50 KPX Kcommaaccent Ohungarumlaut -50 KPX Kcommaaccent Omacron -50 KPX Kcommaaccent Oslash -50 KPX Kcommaaccent Otilde -50 KPX Kcommaaccent e -40 KPX Kcommaaccent eacute -40 KPX Kcommaaccent ecaron -40 KPX Kcommaaccent ecircumflex -40 KPX Kcommaaccent edieresis -40 KPX Kcommaaccent edotaccent -40 KPX Kcommaaccent egrave -40 KPX Kcommaaccent emacron -40 KPX Kcommaaccent eogonek -40 KPX Kcommaaccent o -40 KPX Kcommaaccent oacute -40 KPX Kcommaaccent ocircumflex -40 KPX Kcommaaccent odieresis -40 KPX Kcommaaccent ograve -40 KPX Kcommaaccent ohungarumlaut -40 KPX Kcommaaccent omacron -40 KPX Kcommaaccent oslash -40 KPX Kcommaaccent otilde -40 KPX Kcommaaccent u -30 KPX Kcommaaccent uacute -30 KPX Kcommaaccent ucircumflex -30 KPX Kcommaaccent udieresis -30 KPX Kcommaaccent ugrave -30 KPX Kcommaaccent uhungarumlaut -30 KPX Kcommaaccent umacron -30 KPX Kcommaaccent uogonek -30 KPX Kcommaaccent uring -30 KPX Kcommaaccent y -50 KPX Kcommaaccent yacute -50 KPX Kcommaaccent ydieresis -50 KPX L T -110 KPX L Tcaron -110 KPX L Tcommaaccent -110 KPX L V -110 KPX L W -70 KPX L Y -140 KPX L Yacute -140 KPX L Ydieresis -140 KPX L quotedblright -140 KPX L quoteright -160 KPX L y -30 KPX L yacute -30 KPX L ydieresis -30 KPX Lacute T -110 KPX Lacute Tcaron -110 KPX Lacute Tcommaaccent -110 KPX Lacute V -110 KPX Lacute W -70 KPX Lacute Y -140 KPX Lacute Yacute -140 KPX Lacute Ydieresis -140 KPX Lacute quotedblright -140 KPX Lacute quoteright -160 KPX Lacute y -30 KPX Lacute yacute -30 KPX Lacute ydieresis -30 KPX Lcaron T -110 KPX Lcaron Tcaron -110 KPX Lcaron Tcommaaccent -110 KPX Lcaron V -110 KPX Lcaron W -70 KPX Lcaron Y -140 KPX Lcaron Yacute -140 KPX Lcaron Ydieresis -140 KPX Lcaron quotedblright -140 KPX Lcaron quoteright -160 KPX Lcaron y -30 KPX Lcaron yacute -30 KPX Lcaron ydieresis -30 KPX Lcommaaccent T -110 KPX Lcommaaccent Tcaron -110 KPX Lcommaaccent Tcommaaccent -110 KPX Lcommaaccent V -110 KPX Lcommaaccent W -70 KPX Lcommaaccent Y -140 KPX Lcommaaccent Yacute -140 KPX Lcommaaccent Ydieresis -140 KPX Lcommaaccent quotedblright -140 KPX Lcommaaccent quoteright -160 KPX Lcommaaccent y -30 KPX Lcommaaccent yacute -30 KPX Lcommaaccent ydieresis -30 KPX Lslash T -110 KPX Lslash Tcaron -110 KPX Lslash Tcommaaccent -110 KPX Lslash V -110 KPX Lslash W -70 KPX Lslash Y -140 KPX Lslash Yacute -140 KPX Lslash Ydieresis -140 KPX Lslash quotedblright -140 KPX Lslash quoteright -160 KPX Lslash y -30 KPX Lslash yacute -30 KPX Lslash ydieresis -30 KPX O A -20 KPX O Aacute -20 KPX O Abreve -20 KPX O Acircumflex -20 KPX O Adieresis -20 KPX O Agrave -20 KPX O Amacron -20 KPX O Aogonek -20 KPX O Aring -20 KPX O Atilde -20 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -30 KPX O X -60 KPX O Y -70 KPX O Yacute -70 KPX O Ydieresis -70 KPX O comma -40 KPX O period -40 KPX Oacute A -20 KPX Oacute Aacute -20 KPX Oacute Abreve -20 KPX Oacute Acircumflex -20 KPX Oacute Adieresis -20 KPX Oacute Agrave -20 KPX Oacute Amacron -20 KPX Oacute Aogonek -20 KPX Oacute Aring -20 KPX Oacute Atilde -20 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -30 KPX Oacute X -60 KPX Oacute Y -70 KPX Oacute Yacute -70 KPX Oacute Ydieresis -70 KPX Oacute comma -40 KPX Oacute period -40 KPX Ocircumflex A -20 KPX Ocircumflex Aacute -20 KPX Ocircumflex Abreve -20 KPX Ocircumflex Acircumflex -20 KPX Ocircumflex Adieresis -20 KPX Ocircumflex Agrave -20 KPX Ocircumflex Amacron -20 KPX Ocircumflex Aogonek -20 KPX Ocircumflex Aring -20 KPX Ocircumflex Atilde -20 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -30 KPX Ocircumflex X -60 KPX Ocircumflex Y -70 KPX Ocircumflex Yacute -70 KPX Ocircumflex Ydieresis -70 KPX Ocircumflex comma -40 KPX Ocircumflex period -40 KPX Odieresis A -20 KPX Odieresis Aacute -20 KPX Odieresis Abreve -20 KPX Odieresis Acircumflex -20 KPX Odieresis Adieresis -20 KPX Odieresis Agrave -20 KPX Odieresis Amacron -20 KPX Odieresis Aogonek -20 KPX Odieresis Aring -20 KPX Odieresis Atilde -20 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -30 KPX Odieresis X -60 KPX Odieresis Y -70 KPX Odieresis Yacute -70 KPX Odieresis Ydieresis -70 KPX Odieresis comma -40 KPX Odieresis period -40 KPX Ograve A -20 KPX Ograve Aacute -20 KPX Ograve Abreve -20 KPX Ograve Acircumflex -20 KPX Ograve Adieresis -20 KPX Ograve Agrave -20 KPX Ograve Amacron -20 KPX Ograve Aogonek -20 KPX Ograve Aring -20 KPX Ograve Atilde -20 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -30 KPX Ograve X -60 KPX Ograve Y -70 KPX Ograve Yacute -70 KPX Ograve Ydieresis -70 KPX Ograve comma -40 KPX Ograve period -40 KPX Ohungarumlaut A -20 KPX Ohungarumlaut Aacute -20 KPX Ohungarumlaut Abreve -20 KPX Ohungarumlaut Acircumflex -20 KPX Ohungarumlaut Adieresis -20 KPX Ohungarumlaut Agrave -20 KPX Ohungarumlaut Amacron -20 KPX Ohungarumlaut Aogonek -20 KPX Ohungarumlaut Aring -20 KPX Ohungarumlaut Atilde -20 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -30 KPX Ohungarumlaut X -60 KPX Ohungarumlaut Y -70 KPX Ohungarumlaut Yacute -70 KPX Ohungarumlaut Ydieresis -70 KPX Ohungarumlaut comma -40 KPX Ohungarumlaut period -40 KPX Omacron A -20 KPX Omacron Aacute -20 KPX Omacron Abreve -20 KPX Omacron Acircumflex -20 KPX Omacron Adieresis -20 KPX Omacron Agrave -20 KPX Omacron Amacron -20 KPX Omacron Aogonek -20 KPX Omacron Aring -20 KPX Omacron Atilde -20 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -30 KPX Omacron X -60 KPX Omacron Y -70 KPX Omacron Yacute -70 KPX Omacron Ydieresis -70 KPX Omacron comma -40 KPX Omacron period -40 KPX Oslash A -20 KPX Oslash Aacute -20 KPX Oslash Abreve -20 KPX Oslash Acircumflex -20 KPX Oslash Adieresis -20 KPX Oslash Agrave -20 KPX Oslash Amacron -20 KPX Oslash Aogonek -20 KPX Oslash Aring -20 KPX Oslash Atilde -20 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -30 KPX Oslash X -60 KPX Oslash Y -70 KPX Oslash Yacute -70 KPX Oslash Ydieresis -70 KPX Oslash comma -40 KPX Oslash period -40 KPX Otilde A -20 KPX Otilde Aacute -20 KPX Otilde Abreve -20 KPX Otilde Acircumflex -20 KPX Otilde Adieresis -20 KPX Otilde Agrave -20 KPX Otilde Amacron -20 KPX Otilde Aogonek -20 KPX Otilde Aring -20 KPX Otilde Atilde -20 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -30 KPX Otilde X -60 KPX Otilde Y -70 KPX Otilde Yacute -70 KPX Otilde Ydieresis -70 KPX Otilde comma -40 KPX Otilde period -40 KPX P A -120 KPX P Aacute -120 KPX P Abreve -120 KPX P Acircumflex -120 KPX P Adieresis -120 KPX P Agrave -120 KPX P Amacron -120 KPX P Aogonek -120 KPX P Aring -120 KPX P Atilde -120 KPX P a -40 KPX P aacute -40 KPX P abreve -40 KPX P acircumflex -40 KPX P adieresis -40 KPX P agrave -40 KPX P amacron -40 KPX P aogonek -40 KPX P aring -40 KPX P atilde -40 KPX P comma -180 KPX P e -50 KPX P eacute -50 KPX P ecaron -50 KPX P ecircumflex -50 KPX P edieresis -50 KPX P edotaccent -50 KPX P egrave -50 KPX P emacron -50 KPX P eogonek -50 KPX P o -50 KPX P oacute -50 KPX P ocircumflex -50 KPX P odieresis -50 KPX P ograve -50 KPX P ohungarumlaut -50 KPX P omacron -50 KPX P oslash -50 KPX P otilde -50 KPX P period -180 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX R O -20 KPX R Oacute -20 KPX R Ocircumflex -20 KPX R Odieresis -20 KPX R Ograve -20 KPX R Ohungarumlaut -20 KPX R Omacron -20 KPX R Oslash -20 KPX R Otilde -20 KPX R T -30 KPX R Tcaron -30 KPX R Tcommaaccent -30 KPX R U -40 KPX R Uacute -40 KPX R Ucircumflex -40 KPX R Udieresis -40 KPX R Ugrave -40 KPX R Uhungarumlaut -40 KPX R Umacron -40 KPX R Uogonek -40 KPX R Uring -40 KPX R V -50 KPX R W -30 KPX R Y -50 KPX R Yacute -50 KPX R Ydieresis -50 KPX Racute O -20 KPX Racute Oacute -20 KPX Racute Ocircumflex -20 KPX Racute Odieresis -20 KPX Racute Ograve -20 KPX Racute Ohungarumlaut -20 KPX Racute Omacron -20 KPX Racute Oslash -20 KPX Racute Otilde -20 KPX Racute T -30 KPX Racute Tcaron -30 KPX Racute Tcommaaccent -30 KPX Racute U -40 KPX Racute Uacute -40 KPX Racute Ucircumflex -40 KPX Racute Udieresis -40 KPX Racute Ugrave -40 KPX Racute Uhungarumlaut -40 KPX Racute Umacron -40 KPX Racute Uogonek -40 KPX Racute Uring -40 KPX Racute V -50 KPX Racute W -30 KPX Racute Y -50 KPX Racute Yacute -50 KPX Racute Ydieresis -50 KPX Rcaron O -20 KPX Rcaron Oacute -20 KPX Rcaron Ocircumflex -20 KPX Rcaron Odieresis -20 KPX Rcaron Ograve -20 KPX Rcaron Ohungarumlaut -20 KPX Rcaron Omacron -20 KPX Rcaron Oslash -20 KPX Rcaron Otilde -20 KPX Rcaron T -30 KPX Rcaron Tcaron -30 KPX Rcaron Tcommaaccent -30 KPX Rcaron U -40 KPX Rcaron Uacute -40 KPX Rcaron Ucircumflex -40 KPX Rcaron Udieresis -40 KPX Rcaron Ugrave -40 KPX Rcaron Uhungarumlaut -40 KPX Rcaron Umacron -40 KPX Rcaron Uogonek -40 KPX Rcaron Uring -40 KPX Rcaron V -50 KPX Rcaron W -30 KPX Rcaron Y -50 KPX Rcaron Yacute -50 KPX Rcaron Ydieresis -50 KPX Rcommaaccent O -20 KPX Rcommaaccent Oacute -20 KPX Rcommaaccent Ocircumflex -20 KPX Rcommaaccent Odieresis -20 KPX Rcommaaccent Ograve -20 KPX Rcommaaccent Ohungarumlaut -20 KPX Rcommaaccent Omacron -20 KPX Rcommaaccent Oslash -20 KPX Rcommaaccent Otilde -20 KPX Rcommaaccent T -30 KPX Rcommaaccent Tcaron -30 KPX Rcommaaccent Tcommaaccent -30 KPX Rcommaaccent U -40 KPX Rcommaaccent Uacute -40 KPX Rcommaaccent Ucircumflex -40 KPX Rcommaaccent Udieresis -40 KPX Rcommaaccent Ugrave -40 KPX Rcommaaccent Uhungarumlaut -40 KPX Rcommaaccent Umacron -40 KPX Rcommaaccent Uogonek -40 KPX Rcommaaccent Uring -40 KPX Rcommaaccent V -50 KPX Rcommaaccent W -30 KPX Rcommaaccent Y -50 KPX Rcommaaccent Yacute -50 KPX Rcommaaccent Ydieresis -50 KPX S comma -20 KPX S period -20 KPX Sacute comma -20 KPX Sacute period -20 KPX Scaron comma -20 KPX Scaron period -20 KPX Scedilla comma -20 KPX Scedilla period -20 KPX Scommaaccent comma -20 KPX Scommaaccent period -20 KPX T A -120 KPX T Aacute -120 KPX T Abreve -120 KPX T Acircumflex -120 KPX T Adieresis -120 KPX T Agrave -120 KPX T Amacron -120 KPX T Aogonek -120 KPX T Aring -120 KPX T Atilde -120 KPX T O -40 KPX T Oacute -40 KPX T Ocircumflex -40 KPX T Odieresis -40 KPX T Ograve -40 KPX T Ohungarumlaut -40 KPX T Omacron -40 KPX T Oslash -40 KPX T Otilde -40 KPX T a -120 KPX T aacute -120 KPX T abreve -60 KPX T acircumflex -120 KPX T adieresis -120 KPX T agrave -120 KPX T amacron -60 KPX T aogonek -120 KPX T aring -120 KPX T atilde -60 KPX T colon -20 KPX T comma -120 KPX T e -120 KPX T eacute -120 KPX T ecaron -120 KPX T ecircumflex -120 KPX T edieresis -120 KPX T edotaccent -120 KPX T egrave -60 KPX T emacron -60 KPX T eogonek -120 KPX T hyphen -140 KPX T o -120 KPX T oacute -120 KPX T ocircumflex -120 KPX T odieresis -120 KPX T ograve -120 KPX T ohungarumlaut -120 KPX T omacron -60 KPX T oslash -120 KPX T otilde -60 KPX T period -120 KPX T r -120 KPX T racute -120 KPX T rcaron -120 KPX T rcommaaccent -120 KPX T semicolon -20 KPX T u -120 KPX T uacute -120 KPX T ucircumflex -120 KPX T udieresis -120 KPX T ugrave -120 KPX T uhungarumlaut -120 KPX T umacron -60 KPX T uogonek -120 KPX T uring -120 KPX T w -120 KPX T y -120 KPX T yacute -120 KPX T ydieresis -60 KPX Tcaron A -120 KPX Tcaron Aacute -120 KPX Tcaron Abreve -120 KPX Tcaron Acircumflex -120 KPX Tcaron Adieresis -120 KPX Tcaron Agrave -120 KPX Tcaron Amacron -120 KPX Tcaron Aogonek -120 KPX Tcaron Aring -120 KPX Tcaron Atilde -120 KPX Tcaron O -40 KPX Tcaron Oacute -40 KPX Tcaron Ocircumflex -40 KPX Tcaron Odieresis -40 KPX Tcaron Ograve -40 KPX Tcaron Ohungarumlaut -40 KPX Tcaron Omacron -40 KPX Tcaron Oslash -40 KPX Tcaron Otilde -40 KPX Tcaron a -120 KPX Tcaron aacute -120 KPX Tcaron abreve -60 KPX Tcaron acircumflex -120 KPX Tcaron adieresis -120 KPX Tcaron agrave -120 KPX Tcaron amacron -60 KPX Tcaron aogonek -120 KPX Tcaron aring -120 KPX Tcaron atilde -60 KPX Tcaron colon -20 KPX Tcaron comma -120 KPX Tcaron e -120 KPX Tcaron eacute -120 KPX Tcaron ecaron -120 KPX Tcaron ecircumflex -120 KPX Tcaron edieresis -120 KPX Tcaron edotaccent -120 KPX Tcaron egrave -60 KPX Tcaron emacron -60 KPX Tcaron eogonek -120 KPX Tcaron hyphen -140 KPX Tcaron o -120 KPX Tcaron oacute -120 KPX Tcaron ocircumflex -120 KPX Tcaron odieresis -120 KPX Tcaron ograve -120 KPX Tcaron ohungarumlaut -120 KPX Tcaron omacron -60 KPX Tcaron oslash -120 KPX Tcaron otilde -60 KPX Tcaron period -120 KPX Tcaron r -120 KPX Tcaron racute -120 KPX Tcaron rcaron -120 KPX Tcaron rcommaaccent -120 KPX Tcaron semicolon -20 KPX Tcaron u -120 KPX Tcaron uacute -120 KPX Tcaron ucircumflex -120 KPX Tcaron udieresis -120 KPX Tcaron ugrave -120 KPX Tcaron uhungarumlaut -120 KPX Tcaron umacron -60 KPX Tcaron uogonek -120 KPX Tcaron uring -120 KPX Tcaron w -120 KPX Tcaron y -120 KPX Tcaron yacute -120 KPX Tcaron ydieresis -60 KPX Tcommaaccent A -120 KPX Tcommaaccent Aacute -120 KPX Tcommaaccent Abreve -120 KPX Tcommaaccent Acircumflex -120 KPX Tcommaaccent Adieresis -120 KPX Tcommaaccent Agrave -120 KPX Tcommaaccent Amacron -120 KPX Tcommaaccent Aogonek -120 KPX Tcommaaccent Aring -120 KPX Tcommaaccent Atilde -120 KPX Tcommaaccent O -40 KPX Tcommaaccent Oacute -40 KPX Tcommaaccent Ocircumflex -40 KPX Tcommaaccent Odieresis -40 KPX Tcommaaccent Ograve -40 KPX Tcommaaccent Ohungarumlaut -40 KPX Tcommaaccent Omacron -40 KPX Tcommaaccent Oslash -40 KPX Tcommaaccent Otilde -40 KPX Tcommaaccent a -120 KPX Tcommaaccent aacute -120 KPX Tcommaaccent abreve -60 KPX Tcommaaccent acircumflex -120 KPX Tcommaaccent adieresis -120 KPX Tcommaaccent agrave -120 KPX Tcommaaccent amacron -60 KPX Tcommaaccent aogonek -120 KPX Tcommaaccent aring -120 KPX Tcommaaccent atilde -60 KPX Tcommaaccent colon -20 KPX Tcommaaccent comma -120 KPX Tcommaaccent e -120 KPX Tcommaaccent eacute -120 KPX Tcommaaccent ecaron -120 KPX Tcommaaccent ecircumflex -120 KPX Tcommaaccent edieresis -120 KPX Tcommaaccent edotaccent -120 KPX Tcommaaccent egrave -60 KPX Tcommaaccent emacron -60 KPX Tcommaaccent eogonek -120 KPX Tcommaaccent hyphen -140 KPX Tcommaaccent o -120 KPX Tcommaaccent oacute -120 KPX Tcommaaccent ocircumflex -120 KPX Tcommaaccent odieresis -120 KPX Tcommaaccent ograve -120 KPX Tcommaaccent ohungarumlaut -120 KPX Tcommaaccent omacron -60 KPX Tcommaaccent oslash -120 KPX Tcommaaccent otilde -60 KPX Tcommaaccent period -120 KPX Tcommaaccent r -120 KPX Tcommaaccent racute -120 KPX Tcommaaccent rcaron -120 KPX Tcommaaccent rcommaaccent -120 KPX Tcommaaccent semicolon -20 KPX Tcommaaccent u -120 KPX Tcommaaccent uacute -120 KPX Tcommaaccent ucircumflex -120 KPX Tcommaaccent udieresis -120 KPX Tcommaaccent ugrave -120 KPX Tcommaaccent uhungarumlaut -120 KPX Tcommaaccent umacron -60 KPX Tcommaaccent uogonek -120 KPX Tcommaaccent uring -120 KPX Tcommaaccent w -120 KPX Tcommaaccent y -120 KPX Tcommaaccent yacute -120 KPX Tcommaaccent ydieresis -60 KPX U A -40 KPX U Aacute -40 KPX U Abreve -40 KPX U Acircumflex -40 KPX U Adieresis -40 KPX U Agrave -40 KPX U Amacron -40 KPX U Aogonek -40 KPX U Aring -40 KPX U Atilde -40 KPX U comma -40 KPX U period -40 KPX Uacute A -40 KPX Uacute Aacute -40 KPX Uacute Abreve -40 KPX Uacute Acircumflex -40 KPX Uacute Adieresis -40 KPX Uacute Agrave -40 KPX Uacute Amacron -40 KPX Uacute Aogonek -40 KPX Uacute Aring -40 KPX Uacute Atilde -40 KPX Uacute comma -40 KPX Uacute period -40 KPX Ucircumflex A -40 KPX Ucircumflex Aacute -40 KPX Ucircumflex Abreve -40 KPX Ucircumflex Acircumflex -40 KPX Ucircumflex Adieresis -40 KPX Ucircumflex Agrave -40 KPX Ucircumflex Amacron -40 KPX Ucircumflex Aogonek -40 KPX Ucircumflex Aring -40 KPX Ucircumflex Atilde -40 KPX Ucircumflex comma -40 KPX Ucircumflex period -40 KPX Udieresis A -40 KPX Udieresis Aacute -40 KPX Udieresis Abreve -40 KPX Udieresis Acircumflex -40 KPX Udieresis Adieresis -40 KPX Udieresis Agrave -40 KPX Udieresis Amacron -40 KPX Udieresis Aogonek -40 KPX Udieresis Aring -40 KPX Udieresis Atilde -40 KPX Udieresis comma -40 KPX Udieresis period -40 KPX Ugrave A -40 KPX Ugrave Aacute -40 KPX Ugrave Abreve -40 KPX Ugrave Acircumflex -40 KPX Ugrave Adieresis -40 KPX Ugrave Agrave -40 KPX Ugrave Amacron -40 KPX Ugrave Aogonek -40 KPX Ugrave Aring -40 KPX Ugrave Atilde -40 KPX Ugrave comma -40 KPX Ugrave period -40 KPX Uhungarumlaut A -40 KPX Uhungarumlaut Aacute -40 KPX Uhungarumlaut Abreve -40 KPX Uhungarumlaut Acircumflex -40 KPX Uhungarumlaut Adieresis -40 KPX Uhungarumlaut Agrave -40 KPX Uhungarumlaut Amacron -40 KPX Uhungarumlaut Aogonek -40 KPX Uhungarumlaut Aring -40 KPX Uhungarumlaut Atilde -40 KPX Uhungarumlaut comma -40 KPX Uhungarumlaut period -40 KPX Umacron A -40 KPX Umacron Aacute -40 KPX Umacron Abreve -40 KPX Umacron Acircumflex -40 KPX Umacron Adieresis -40 KPX Umacron Agrave -40 KPX Umacron Amacron -40 KPX Umacron Aogonek -40 KPX Umacron Aring -40 KPX Umacron Atilde -40 KPX Umacron comma -40 KPX Umacron period -40 KPX Uogonek A -40 KPX Uogonek Aacute -40 KPX Uogonek Abreve -40 KPX Uogonek Acircumflex -40 KPX Uogonek Adieresis -40 KPX Uogonek Agrave -40 KPX Uogonek Amacron -40 KPX Uogonek Aogonek -40 KPX Uogonek Aring -40 KPX Uogonek Atilde -40 KPX Uogonek comma -40 KPX Uogonek period -40 KPX Uring A -40 KPX Uring Aacute -40 KPX Uring Abreve -40 KPX Uring Acircumflex -40 KPX Uring Adieresis -40 KPX Uring Agrave -40 KPX Uring Amacron -40 KPX Uring Aogonek -40 KPX Uring Aring -40 KPX Uring Atilde -40 KPX Uring comma -40 KPX Uring period -40 KPX V A -80 KPX V Aacute -80 KPX V Abreve -80 KPX V Acircumflex -80 KPX V Adieresis -80 KPX V Agrave -80 KPX V Amacron -80 KPX V Aogonek -80 KPX V Aring -80 KPX V Atilde -80 KPX V G -40 KPX V Gbreve -40 KPX V Gcommaaccent -40 KPX V O -40 KPX V Oacute -40 KPX V Ocircumflex -40 KPX V Odieresis -40 KPX V Ograve -40 KPX V Ohungarumlaut -40 KPX V Omacron -40 KPX V Oslash -40 KPX V Otilde -40 KPX V a -70 KPX V aacute -70 KPX V abreve -70 KPX V acircumflex -70 KPX V adieresis -70 KPX V agrave -70 KPX V amacron -70 KPX V aogonek -70 KPX V aring -70 KPX V atilde -70 KPX V colon -40 KPX V comma -125 KPX V e -80 KPX V eacute -80 KPX V ecaron -80 KPX V ecircumflex -80 KPX V edieresis -80 KPX V edotaccent -80 KPX V egrave -80 KPX V emacron -80 KPX V eogonek -80 KPX V hyphen -80 KPX V o -80 KPX V oacute -80 KPX V ocircumflex -80 KPX V odieresis -80 KPX V ograve -80 KPX V ohungarumlaut -80 KPX V omacron -80 KPX V oslash -80 KPX V otilde -80 KPX V period -125 KPX V semicolon -40 KPX V u -70 KPX V uacute -70 KPX V ucircumflex -70 KPX V udieresis -70 KPX V ugrave -70 KPX V uhungarumlaut -70 KPX V umacron -70 KPX V uogonek -70 KPX V uring -70 KPX W A -50 KPX W Aacute -50 KPX W Abreve -50 KPX W Acircumflex -50 KPX W Adieresis -50 KPX W Agrave -50 KPX W Amacron -50 KPX W Aogonek -50 KPX W Aring -50 KPX W Atilde -50 KPX W O -20 KPX W Oacute -20 KPX W Ocircumflex -20 KPX W Odieresis -20 KPX W Ograve -20 KPX W Ohungarumlaut -20 KPX W Omacron -20 KPX W Oslash -20 KPX W Otilde -20 KPX W a -40 KPX W aacute -40 KPX W abreve -40 KPX W acircumflex -40 KPX W adieresis -40 KPX W agrave -40 KPX W amacron -40 KPX W aogonek -40 KPX W aring -40 KPX W atilde -40 KPX W comma -80 KPX W e -30 KPX W eacute -30 KPX W ecaron -30 KPX W ecircumflex -30 KPX W edieresis -30 KPX W edotaccent -30 KPX W egrave -30 KPX W emacron -30 KPX W eogonek -30 KPX W hyphen -40 KPX W o -30 KPX W oacute -30 KPX W ocircumflex -30 KPX W odieresis -30 KPX W ograve -30 KPX W ohungarumlaut -30 KPX W omacron -30 KPX W oslash -30 KPX W otilde -30 KPX W period -80 KPX W u -30 KPX W uacute -30 KPX W ucircumflex -30 KPX W udieresis -30 KPX W ugrave -30 KPX W uhungarumlaut -30 KPX W umacron -30 KPX W uogonek -30 KPX W uring -30 KPX W y -20 KPX W yacute -20 KPX W ydieresis -20 KPX Y A -110 KPX Y Aacute -110 KPX Y Abreve -110 KPX Y Acircumflex -110 KPX Y Adieresis -110 KPX Y Agrave -110 KPX Y Amacron -110 KPX Y Aogonek -110 KPX Y Aring -110 KPX Y Atilde -110 KPX Y O -85 KPX Y Oacute -85 KPX Y Ocircumflex -85 KPX Y Odieresis -85 KPX Y Ograve -85 KPX Y Ohungarumlaut -85 KPX Y Omacron -85 KPX Y Oslash -85 KPX Y Otilde -85 KPX Y a -140 KPX Y aacute -140 KPX Y abreve -70 KPX Y acircumflex -140 KPX Y adieresis -140 KPX Y agrave -140 KPX Y amacron -70 KPX Y aogonek -140 KPX Y aring -140 KPX Y atilde -140 KPX Y colon -60 KPX Y comma -140 KPX Y e -140 KPX Y eacute -140 KPX Y ecaron -140 KPX Y ecircumflex -140 KPX Y edieresis -140 KPX Y edotaccent -140 KPX Y egrave -140 KPX Y emacron -70 KPX Y eogonek -140 KPX Y hyphen -140 KPX Y i -20 KPX Y iacute -20 KPX Y iogonek -20 KPX Y o -140 KPX Y oacute -140 KPX Y ocircumflex -140 KPX Y odieresis -140 KPX Y ograve -140 KPX Y ohungarumlaut -140 KPX Y omacron -140 KPX Y oslash -140 KPX Y otilde -140 KPX Y period -140 KPX Y semicolon -60 KPX Y u -110 KPX Y uacute -110 KPX Y ucircumflex -110 KPX Y udieresis -110 KPX Y ugrave -110 KPX Y uhungarumlaut -110 KPX Y umacron -110 KPX Y uogonek -110 KPX Y uring -110 KPX Yacute A -110 KPX Yacute Aacute -110 KPX Yacute Abreve -110 KPX Yacute Acircumflex -110 KPX Yacute Adieresis -110 KPX Yacute Agrave -110 KPX Yacute Amacron -110 KPX Yacute Aogonek -110 KPX Yacute Aring -110 KPX Yacute Atilde -110 KPX Yacute O -85 KPX Yacute Oacute -85 KPX Yacute Ocircumflex -85 KPX Yacute Odieresis -85 KPX Yacute Ograve -85 KPX Yacute Ohungarumlaut -85 KPX Yacute Omacron -85 KPX Yacute Oslash -85 KPX Yacute Otilde -85 KPX Yacute a -140 KPX Yacute aacute -140 KPX Yacute abreve -70 KPX Yacute acircumflex -140 KPX Yacute adieresis -140 KPX Yacute agrave -140 KPX Yacute amacron -70 KPX Yacute aogonek -140 KPX Yacute aring -140 KPX Yacute atilde -70 KPX Yacute colon -60 KPX Yacute comma -140 KPX Yacute e -140 KPX Yacute eacute -140 KPX Yacute ecaron -140 KPX Yacute ecircumflex -140 KPX Yacute edieresis -140 KPX Yacute edotaccent -140 KPX Yacute egrave -140 KPX Yacute emacron -70 KPX Yacute eogonek -140 KPX Yacute hyphen -140 KPX Yacute i -20 KPX Yacute iacute -20 KPX Yacute iogonek -20 KPX Yacute o -140 KPX Yacute oacute -140 KPX Yacute ocircumflex -140 KPX Yacute odieresis -140 KPX Yacute ograve -140 KPX Yacute ohungarumlaut -140 KPX Yacute omacron -70 KPX Yacute oslash -140 KPX Yacute otilde -140 KPX Yacute period -140 KPX Yacute semicolon -60 KPX Yacute u -110 KPX Yacute uacute -110 KPX Yacute ucircumflex -110 KPX Yacute udieresis -110 KPX Yacute ugrave -110 KPX Yacute uhungarumlaut -110 KPX Yacute umacron -110 KPX Yacute uogonek -110 KPX Yacute uring -110 KPX Ydieresis A -110 KPX Ydieresis Aacute -110 KPX Ydieresis Abreve -110 KPX Ydieresis Acircumflex -110 KPX Ydieresis Adieresis -110 KPX Ydieresis Agrave -110 KPX Ydieresis Amacron -110 KPX Ydieresis Aogonek -110 KPX Ydieresis Aring -110 KPX Ydieresis Atilde -110 KPX Ydieresis O -85 KPX Ydieresis Oacute -85 KPX Ydieresis Ocircumflex -85 KPX Ydieresis Odieresis -85 KPX Ydieresis Ograve -85 KPX Ydieresis Ohungarumlaut -85 KPX Ydieresis Omacron -85 KPX Ydieresis Oslash -85 KPX Ydieresis Otilde -85 KPX Ydieresis a -140 KPX Ydieresis aacute -140 KPX Ydieresis abreve -70 KPX Ydieresis acircumflex -140 KPX Ydieresis adieresis -140 KPX Ydieresis agrave -140 KPX Ydieresis amacron -70 KPX Ydieresis aogonek -140 KPX Ydieresis aring -140 KPX Ydieresis atilde -70 KPX Ydieresis colon -60 KPX Ydieresis comma -140 KPX Ydieresis e -140 KPX Ydieresis eacute -140 KPX Ydieresis ecaron -140 KPX Ydieresis ecircumflex -140 KPX Ydieresis edieresis -140 KPX Ydieresis edotaccent -140 KPX Ydieresis egrave -140 KPX Ydieresis emacron -70 KPX Ydieresis eogonek -140 KPX Ydieresis hyphen -140 KPX Ydieresis i -20 KPX Ydieresis iacute -20 KPX Ydieresis iogonek -20 KPX Ydieresis o -140 KPX Ydieresis oacute -140 KPX Ydieresis ocircumflex -140 KPX Ydieresis odieresis -140 KPX Ydieresis ograve -140 KPX Ydieresis ohungarumlaut -140 KPX Ydieresis omacron -140 KPX Ydieresis oslash -140 KPX Ydieresis otilde -140 KPX Ydieresis period -140 KPX Ydieresis semicolon -60 KPX Ydieresis u -110 KPX Ydieresis uacute -110 KPX Ydieresis ucircumflex -110 KPX Ydieresis udieresis -110 KPX Ydieresis ugrave -110 KPX Ydieresis uhungarumlaut -110 KPX Ydieresis umacron -110 KPX Ydieresis uogonek -110 KPX Ydieresis uring -110 KPX a v -20 KPX a w -20 KPX a y -30 KPX a yacute -30 KPX a ydieresis -30 KPX aacute v -20 KPX aacute w -20 KPX aacute y -30 KPX aacute yacute -30 KPX aacute ydieresis -30 KPX abreve v -20 KPX abreve w -20 KPX abreve y -30 KPX abreve yacute -30 KPX abreve ydieresis -30 KPX acircumflex v -20 KPX acircumflex w -20 KPX acircumflex y -30 KPX acircumflex yacute -30 KPX acircumflex ydieresis -30 KPX adieresis v -20 KPX adieresis w -20 KPX adieresis y -30 KPX adieresis yacute -30 KPX adieresis ydieresis -30 KPX agrave v -20 KPX agrave w -20 KPX agrave y -30 KPX agrave yacute -30 KPX agrave ydieresis -30 KPX amacron v -20 KPX amacron w -20 KPX amacron y -30 KPX amacron yacute -30 KPX amacron ydieresis -30 KPX aogonek v -20 KPX aogonek w -20 KPX aogonek y -30 KPX aogonek yacute -30 KPX aogonek ydieresis -30 KPX aring v -20 KPX aring w -20 KPX aring y -30 KPX aring yacute -30 KPX aring ydieresis -30 KPX atilde v -20 KPX atilde w -20 KPX atilde y -30 KPX atilde yacute -30 KPX atilde ydieresis -30 KPX b b -10 KPX b comma -40 KPX b l -20 KPX b lacute -20 KPX b lcommaaccent -20 KPX b lslash -20 KPX b period -40 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX b v -20 KPX b y -20 KPX b yacute -20 KPX b ydieresis -20 KPX c comma -15 KPX c k -20 KPX c kcommaaccent -20 KPX cacute comma -15 KPX cacute k -20 KPX cacute kcommaaccent -20 KPX ccaron comma -15 KPX ccaron k -20 KPX ccaron kcommaaccent -20 KPX ccedilla comma -15 KPX ccedilla k -20 KPX ccedilla kcommaaccent -20 KPX colon space -50 KPX comma quotedblright -100 KPX comma quoteright -100 KPX e comma -15 KPX e period -15 KPX e v -30 KPX e w -20 KPX e x -30 KPX e y -20 KPX e yacute -20 KPX e ydieresis -20 KPX eacute comma -15 KPX eacute period -15 KPX eacute v -30 KPX eacute w -20 KPX eacute x -30 KPX eacute y -20 KPX eacute yacute -20 KPX eacute ydieresis -20 KPX ecaron comma -15 KPX ecaron period -15 KPX ecaron v -30 KPX ecaron w -20 KPX ecaron x -30 KPX ecaron y -20 KPX ecaron yacute -20 KPX ecaron ydieresis -20 KPX ecircumflex comma -15 KPX ecircumflex period -15 KPX ecircumflex v -30 KPX ecircumflex w -20 KPX ecircumflex x -30 KPX ecircumflex y -20 KPX ecircumflex yacute -20 KPX ecircumflex ydieresis -20 KPX edieresis comma -15 KPX edieresis period -15 KPX edieresis v -30 KPX edieresis w -20 KPX edieresis x -30 KPX edieresis y -20 KPX edieresis yacute -20 KPX edieresis ydieresis -20 KPX edotaccent comma -15 KPX edotaccent period -15 KPX edotaccent v -30 KPX edotaccent w -20 KPX edotaccent x -30 KPX edotaccent y -20 KPX edotaccent yacute -20 KPX edotaccent ydieresis -20 KPX egrave comma -15 KPX egrave period -15 KPX egrave v -30 KPX egrave w -20 KPX egrave x -30 KPX egrave y -20 KPX egrave yacute -20 KPX egrave ydieresis -20 KPX emacron comma -15 KPX emacron period -15 KPX emacron v -30 KPX emacron w -20 KPX emacron x -30 KPX emacron y -20 KPX emacron yacute -20 KPX emacron ydieresis -20 KPX eogonek comma -15 KPX eogonek period -15 KPX eogonek v -30 KPX eogonek w -20 KPX eogonek x -30 KPX eogonek y -20 KPX eogonek yacute -20 KPX eogonek ydieresis -20 KPX f a -30 KPX f aacute -30 KPX f abreve -30 KPX f acircumflex -30 KPX f adieresis -30 KPX f agrave -30 KPX f amacron -30 KPX f aogonek -30 KPX f aring -30 KPX f atilde -30 KPX f comma -30 KPX f dotlessi -28 KPX f e -30 KPX f eacute -30 KPX f ecaron -30 KPX f ecircumflex -30 KPX f edieresis -30 KPX f edotaccent -30 KPX f egrave -30 KPX f emacron -30 KPX f eogonek -30 KPX f o -30 KPX f oacute -30 KPX f ocircumflex -30 KPX f odieresis -30 KPX f ograve -30 KPX f ohungarumlaut -30 KPX f omacron -30 KPX f oslash -30 KPX f otilde -30 KPX f period -30 KPX f quotedblright 60 KPX f quoteright 50 KPX g r -10 KPX g racute -10 KPX g rcaron -10 KPX g rcommaaccent -10 KPX gbreve r -10 KPX gbreve racute -10 KPX gbreve rcaron -10 KPX gbreve rcommaaccent -10 KPX gcommaaccent r -10 KPX gcommaaccent racute -10 KPX gcommaaccent rcaron -10 KPX gcommaaccent rcommaaccent -10 KPX h y -30 KPX h yacute -30 KPX h ydieresis -30 KPX k e -20 KPX k eacute -20 KPX k ecaron -20 KPX k ecircumflex -20 KPX k edieresis -20 KPX k edotaccent -20 KPX k egrave -20 KPX k emacron -20 KPX k eogonek -20 KPX k o -20 KPX k oacute -20 KPX k ocircumflex -20 KPX k odieresis -20 KPX k ograve -20 KPX k ohungarumlaut -20 KPX k omacron -20 KPX k oslash -20 KPX k otilde -20 KPX kcommaaccent e -20 KPX kcommaaccent eacute -20 KPX kcommaaccent ecaron -20 KPX kcommaaccent ecircumflex -20 KPX kcommaaccent edieresis -20 KPX kcommaaccent edotaccent -20 KPX kcommaaccent egrave -20 KPX kcommaaccent emacron -20 KPX kcommaaccent eogonek -20 KPX kcommaaccent o -20 KPX kcommaaccent oacute -20 KPX kcommaaccent ocircumflex -20 KPX kcommaaccent odieresis -20 KPX kcommaaccent ograve -20 KPX kcommaaccent ohungarumlaut -20 KPX kcommaaccent omacron -20 KPX kcommaaccent oslash -20 KPX kcommaaccent otilde -20 KPX m u -10 KPX m uacute -10 KPX m ucircumflex -10 KPX m udieresis -10 KPX m ugrave -10 KPX m uhungarumlaut -10 KPX m umacron -10 KPX m uogonek -10 KPX m uring -10 KPX m y -15 KPX m yacute -15 KPX m ydieresis -15 KPX n u -10 KPX n uacute -10 KPX n ucircumflex -10 KPX n udieresis -10 KPX n ugrave -10 KPX n uhungarumlaut -10 KPX n umacron -10 KPX n uogonek -10 KPX n uring -10 KPX n v -20 KPX n y -15 KPX n yacute -15 KPX n ydieresis -15 KPX nacute u -10 KPX nacute uacute -10 KPX nacute ucircumflex -10 KPX nacute udieresis -10 KPX nacute ugrave -10 KPX nacute uhungarumlaut -10 KPX nacute umacron -10 KPX nacute uogonek -10 KPX nacute uring -10 KPX nacute v -20 KPX nacute y -15 KPX nacute yacute -15 KPX nacute ydieresis -15 KPX ncaron u -10 KPX ncaron uacute -10 KPX ncaron ucircumflex -10 KPX ncaron udieresis -10 KPX ncaron ugrave -10 KPX ncaron uhungarumlaut -10 KPX ncaron umacron -10 KPX ncaron uogonek -10 KPX ncaron uring -10 KPX ncaron v -20 KPX ncaron y -15 KPX ncaron yacute -15 KPX ncaron ydieresis -15 KPX ncommaaccent u -10 KPX ncommaaccent uacute -10 KPX ncommaaccent ucircumflex -10 KPX ncommaaccent udieresis -10 KPX ncommaaccent ugrave -10 KPX ncommaaccent uhungarumlaut -10 KPX ncommaaccent umacron -10 KPX ncommaaccent uogonek -10 KPX ncommaaccent uring -10 KPX ncommaaccent v -20 KPX ncommaaccent y -15 KPX ncommaaccent yacute -15 KPX ncommaaccent ydieresis -15 KPX ntilde u -10 KPX ntilde uacute -10 KPX ntilde ucircumflex -10 KPX ntilde udieresis -10 KPX ntilde ugrave -10 KPX ntilde uhungarumlaut -10 KPX ntilde umacron -10 KPX ntilde uogonek -10 KPX ntilde uring -10 KPX ntilde v -20 KPX ntilde y -15 KPX ntilde yacute -15 KPX ntilde ydieresis -15 KPX o comma -40 KPX o period -40 KPX o v -15 KPX o w -15 KPX o x -30 KPX o y -30 KPX o yacute -30 KPX o ydieresis -30 KPX oacute comma -40 KPX oacute period -40 KPX oacute v -15 KPX oacute w -15 KPX oacute x -30 KPX oacute y -30 KPX oacute yacute -30 KPX oacute ydieresis -30 KPX ocircumflex comma -40 KPX ocircumflex period -40 KPX ocircumflex v -15 KPX ocircumflex w -15 KPX ocircumflex x -30 KPX ocircumflex y -30 KPX ocircumflex yacute -30 KPX ocircumflex ydieresis -30 KPX odieresis comma -40 KPX odieresis period -40 KPX odieresis v -15 KPX odieresis w -15 KPX odieresis x -30 KPX odieresis y -30 KPX odieresis yacute -30 KPX odieresis ydieresis -30 KPX ograve comma -40 KPX ograve period -40 KPX ograve v -15 KPX ograve w -15 KPX ograve x -30 KPX ograve y -30 KPX ograve yacute -30 KPX ograve ydieresis -30 KPX ohungarumlaut comma -40 KPX ohungarumlaut period -40 KPX ohungarumlaut v -15 KPX ohungarumlaut w -15 KPX ohungarumlaut x -30 KPX ohungarumlaut y -30 KPX ohungarumlaut yacute -30 KPX ohungarumlaut ydieresis -30 KPX omacron comma -40 KPX omacron period -40 KPX omacron v -15 KPX omacron w -15 KPX omacron x -30 KPX omacron y -30 KPX omacron yacute -30 KPX omacron ydieresis -30 KPX oslash a -55 KPX oslash aacute -55 KPX oslash abreve -55 KPX oslash acircumflex -55 KPX oslash adieresis -55 KPX oslash agrave -55 KPX oslash amacron -55 KPX oslash aogonek -55 KPX oslash aring -55 KPX oslash atilde -55 KPX oslash b -55 KPX oslash c -55 KPX oslash cacute -55 KPX oslash ccaron -55 KPX oslash ccedilla -55 KPX oslash comma -95 KPX oslash d -55 KPX oslash dcroat -55 KPX oslash e -55 KPX oslash eacute -55 KPX oslash ecaron -55 KPX oslash ecircumflex -55 KPX oslash edieresis -55 KPX oslash edotaccent -55 KPX oslash egrave -55 KPX oslash emacron -55 KPX oslash eogonek -55 KPX oslash f -55 KPX oslash g -55 KPX oslash gbreve -55 KPX oslash gcommaaccent -55 KPX oslash h -55 KPX oslash i -55 KPX oslash iacute -55 KPX oslash icircumflex -55 KPX oslash idieresis -55 KPX oslash igrave -55 KPX oslash imacron -55 KPX oslash iogonek -55 KPX oslash j -55 KPX oslash k -55 KPX oslash kcommaaccent -55 KPX oslash l -55 KPX oslash lacute -55 KPX oslash lcommaaccent -55 KPX oslash lslash -55 KPX oslash m -55 KPX oslash n -55 KPX oslash nacute -55 KPX oslash ncaron -55 KPX oslash ncommaaccent -55 KPX oslash ntilde -55 KPX oslash o -55 KPX oslash oacute -55 KPX oslash ocircumflex -55 KPX oslash odieresis -55 KPX oslash ograve -55 KPX oslash ohungarumlaut -55 KPX oslash omacron -55 KPX oslash oslash -55 KPX oslash otilde -55 KPX oslash p -55 KPX oslash period -95 KPX oslash q -55 KPX oslash r -55 KPX oslash racute -55 KPX oslash rcaron -55 KPX oslash rcommaaccent -55 KPX oslash s -55 KPX oslash sacute -55 KPX oslash scaron -55 KPX oslash scedilla -55 KPX oslash scommaaccent -55 KPX oslash t -55 KPX oslash tcommaaccent -55 KPX oslash u -55 KPX oslash uacute -55 KPX oslash ucircumflex -55 KPX oslash udieresis -55 KPX oslash ugrave -55 KPX oslash uhungarumlaut -55 KPX oslash umacron -55 KPX oslash uogonek -55 KPX oslash uring -55 KPX oslash v -70 KPX oslash w -70 KPX oslash x -85 KPX oslash y -70 KPX oslash yacute -70 KPX oslash ydieresis -70 KPX oslash z -55 KPX oslash zacute -55 KPX oslash zcaron -55 KPX oslash zdotaccent -55 KPX otilde comma -40 KPX otilde period -40 KPX otilde v -15 KPX otilde w -15 KPX otilde x -30 KPX otilde y -30 KPX otilde yacute -30 KPX otilde ydieresis -30 KPX p comma -35 KPX p period -35 KPX p y -30 KPX p yacute -30 KPX p ydieresis -30 KPX period quotedblright -100 KPX period quoteright -100 KPX period space -60 KPX quotedblright space -40 KPX quoteleft quoteleft -57 KPX quoteright d -50 KPX quoteright dcroat -50 KPX quoteright quoteright -57 KPX quoteright r -50 KPX quoteright racute -50 KPX quoteright rcaron -50 KPX quoteright rcommaaccent -50 KPX quoteright s -50 KPX quoteright sacute -50 KPX quoteright scaron -50 KPX quoteright scedilla -50 KPX quoteright scommaaccent -50 KPX quoteright space -70 KPX r a -10 KPX r aacute -10 KPX r abreve -10 KPX r acircumflex -10 KPX r adieresis -10 KPX r agrave -10 KPX r amacron -10 KPX r aogonek -10 KPX r aring -10 KPX r atilde -10 KPX r colon 30 KPX r comma -50 KPX r i 15 KPX r iacute 15 KPX r icircumflex 15 KPX r idieresis 15 KPX r igrave 15 KPX r imacron 15 KPX r iogonek 15 KPX r k 15 KPX r kcommaaccent 15 KPX r l 15 KPX r lacute 15 KPX r lcommaaccent 15 KPX r lslash 15 KPX r m 25 KPX r n 25 KPX r nacute 25 KPX r ncaron 25 KPX r ncommaaccent 25 KPX r ntilde 25 KPX r p 30 KPX r period -50 KPX r semicolon 30 KPX r t 40 KPX r tcommaaccent 40 KPX r u 15 KPX r uacute 15 KPX r ucircumflex 15 KPX r udieresis 15 KPX r ugrave 15 KPX r uhungarumlaut 15 KPX r umacron 15 KPX r uogonek 15 KPX r uring 15 KPX r v 30 KPX r y 30 KPX r yacute 30 KPX r ydieresis 30 KPX racute a -10 KPX racute aacute -10 KPX racute abreve -10 KPX racute acircumflex -10 KPX racute adieresis -10 KPX racute agrave -10 KPX racute amacron -10 KPX racute aogonek -10 KPX racute aring -10 KPX racute atilde -10 KPX racute colon 30 KPX racute comma -50 KPX racute i 15 KPX racute iacute 15 KPX racute icircumflex 15 KPX racute idieresis 15 KPX racute igrave 15 KPX racute imacron 15 KPX racute iogonek 15 KPX racute k 15 KPX racute kcommaaccent 15 KPX racute l 15 KPX racute lacute 15 KPX racute lcommaaccent 15 KPX racute lslash 15 KPX racute m 25 KPX racute n 25 KPX racute nacute 25 KPX racute ncaron 25 KPX racute ncommaaccent 25 KPX racute ntilde 25 KPX racute p 30 KPX racute period -50 KPX racute semicolon 30 KPX racute t 40 KPX racute tcommaaccent 40 KPX racute u 15 KPX racute uacute 15 KPX racute ucircumflex 15 KPX racute udieresis 15 KPX racute ugrave 15 KPX racute uhungarumlaut 15 KPX racute umacron 15 KPX racute uogonek 15 KPX racute uring 15 KPX racute v 30 KPX racute y 30 KPX racute yacute 30 KPX racute ydieresis 30 KPX rcaron a -10 KPX rcaron aacute -10 KPX rcaron abreve -10 KPX rcaron acircumflex -10 KPX rcaron adieresis -10 KPX rcaron agrave -10 KPX rcaron amacron -10 KPX rcaron aogonek -10 KPX rcaron aring -10 KPX rcaron atilde -10 KPX rcaron colon 30 KPX rcaron comma -50 KPX rcaron i 15 KPX rcaron iacute 15 KPX rcaron icircumflex 15 KPX rcaron idieresis 15 KPX rcaron igrave 15 KPX rcaron imacron 15 KPX rcaron iogonek 15 KPX rcaron k 15 KPX rcaron kcommaaccent 15 KPX rcaron l 15 KPX rcaron lacute 15 KPX rcaron lcommaaccent 15 KPX rcaron lslash 15 KPX rcaron m 25 KPX rcaron n 25 KPX rcaron nacute 25 KPX rcaron ncaron 25 KPX rcaron ncommaaccent 25 KPX rcaron ntilde 25 KPX rcaron p 30 KPX rcaron period -50 KPX rcaron semicolon 30 KPX rcaron t 40 KPX rcaron tcommaaccent 40 KPX rcaron u 15 KPX rcaron uacute 15 KPX rcaron ucircumflex 15 KPX rcaron udieresis 15 KPX rcaron ugrave 15 KPX rcaron uhungarumlaut 15 KPX rcaron umacron 15 KPX rcaron uogonek 15 KPX rcaron uring 15 KPX rcaron v 30 KPX rcaron y 30 KPX rcaron yacute 30 KPX rcaron ydieresis 30 KPX rcommaaccent a -10 KPX rcommaaccent aacute -10 KPX rcommaaccent abreve -10 KPX rcommaaccent acircumflex -10 KPX rcommaaccent adieresis -10 KPX rcommaaccent agrave -10 KPX rcommaaccent amacron -10 KPX rcommaaccent aogonek -10 KPX rcommaaccent aring -10 KPX rcommaaccent atilde -10 KPX rcommaaccent colon 30 KPX rcommaaccent comma -50 KPX rcommaaccent i 15 KPX rcommaaccent iacute 15 KPX rcommaaccent icircumflex 15 KPX rcommaaccent idieresis 15 KPX rcommaaccent igrave 15 KPX rcommaaccent imacron 15 KPX rcommaaccent iogonek 15 KPX rcommaaccent k 15 KPX rcommaaccent kcommaaccent 15 KPX rcommaaccent l 15 KPX rcommaaccent lacute 15 KPX rcommaaccent lcommaaccent 15 KPX rcommaaccent lslash 15 KPX rcommaaccent m 25 KPX rcommaaccent n 25 KPX rcommaaccent nacute 25 KPX rcommaaccent ncaron 25 KPX rcommaaccent ncommaaccent 25 KPX rcommaaccent ntilde 25 KPX rcommaaccent p 30 KPX rcommaaccent period -50 KPX rcommaaccent semicolon 30 KPX rcommaaccent t 40 KPX rcommaaccent tcommaaccent 40 KPX rcommaaccent u 15 KPX rcommaaccent uacute 15 KPX rcommaaccent ucircumflex 15 KPX rcommaaccent udieresis 15 KPX rcommaaccent ugrave 15 KPX rcommaaccent uhungarumlaut 15 KPX rcommaaccent umacron 15 KPX rcommaaccent uogonek 15 KPX rcommaaccent uring 15 KPX rcommaaccent v 30 KPX rcommaaccent y 30 KPX rcommaaccent yacute 30 KPX rcommaaccent ydieresis 30 KPX s comma -15 KPX s period -15 KPX s w -30 KPX sacute comma -15 KPX sacute period -15 KPX sacute w -30 KPX scaron comma -15 KPX scaron period -15 KPX scaron w -30 KPX scedilla comma -15 KPX scedilla period -15 KPX scedilla w -30 KPX scommaaccent comma -15 KPX scommaaccent period -15 KPX scommaaccent w -30 KPX semicolon space -50 KPX space T -50 KPX space Tcaron -50 KPX space Tcommaaccent -50 KPX space V -50 KPX space W -40 KPX space Y -90 KPX space Yacute -90 KPX space Ydieresis -90 KPX space quotedblleft -30 KPX space quoteleft -60 KPX v a -25 KPX v aacute -25 KPX v abreve -25 KPX v acircumflex -25 KPX v adieresis -25 KPX v agrave -25 KPX v amacron -25 KPX v aogonek -25 KPX v aring -25 KPX v atilde -25 KPX v comma -80 KPX v e -25 KPX v eacute -25 KPX v ecaron -25 KPX v ecircumflex -25 KPX v edieresis -25 KPX v edotaccent -25 KPX v egrave -25 KPX v emacron -25 KPX v eogonek -25 KPX v o -25 KPX v oacute -25 KPX v ocircumflex -25 KPX v odieresis -25 KPX v ograve -25 KPX v ohungarumlaut -25 KPX v omacron -25 KPX v oslash -25 KPX v otilde -25 KPX v period -80 KPX w a -15 KPX w aacute -15 KPX w abreve -15 KPX w acircumflex -15 KPX w adieresis -15 KPX w agrave -15 KPX w amacron -15 KPX w aogonek -15 KPX w aring -15 KPX w atilde -15 KPX w comma -60 KPX w e -10 KPX w eacute -10 KPX w ecaron -10 KPX w ecircumflex -10 KPX w edieresis -10 KPX w edotaccent -10 KPX w egrave -10 KPX w emacron -10 KPX w eogonek -10 KPX w o -10 KPX w oacute -10 KPX w ocircumflex -10 KPX w odieresis -10 KPX w ograve -10 KPX w ohungarumlaut -10 KPX w omacron -10 KPX w oslash -10 KPX w otilde -10 KPX w period -60 KPX x e -30 KPX x eacute -30 KPX x ecaron -30 KPX x ecircumflex -30 KPX x edieresis -30 KPX x edotaccent -30 KPX x egrave -30 KPX x emacron -30 KPX x eogonek -30 KPX y a -20 KPX y aacute -20 KPX y abreve -20 KPX y acircumflex -20 KPX y adieresis -20 KPX y agrave -20 KPX y amacron -20 KPX y aogonek -20 KPX y aring -20 KPX y atilde -20 KPX y comma -100 KPX y e -20 KPX y eacute -20 KPX y ecaron -20 KPX y ecircumflex -20 KPX y edieresis -20 KPX y edotaccent -20 KPX y egrave -20 KPX y emacron -20 KPX y eogonek -20 KPX y o -20 KPX y oacute -20 KPX y ocircumflex -20 KPX y odieresis -20 KPX y ograve -20 KPX y ohungarumlaut -20 KPX y omacron -20 KPX y oslash -20 KPX y otilde -20 KPX y period -100 KPX yacute a -20 KPX yacute aacute -20 KPX yacute abreve -20 KPX yacute acircumflex -20 KPX yacute adieresis -20 KPX yacute agrave -20 KPX yacute amacron -20 KPX yacute aogonek -20 KPX yacute aring -20 KPX yacute atilde -20 KPX yacute comma -100 KPX yacute e -20 KPX yacute eacute -20 KPX yacute ecaron -20 KPX yacute ecircumflex -20 KPX yacute edieresis -20 KPX yacute edotaccent -20 KPX yacute egrave -20 KPX yacute emacron -20 KPX yacute eogonek -20 KPX yacute o -20 KPX yacute oacute -20 KPX yacute ocircumflex -20 KPX yacute odieresis -20 KPX yacute ograve -20 KPX yacute ohungarumlaut -20 KPX yacute omacron -20 KPX yacute oslash -20 KPX yacute otilde -20 KPX yacute period -100 KPX ydieresis a -20 KPX ydieresis aacute -20 KPX ydieresis abreve -20 KPX ydieresis acircumflex -20 KPX ydieresis adieresis -20 KPX ydieresis agrave -20 KPX ydieresis amacron -20 KPX ydieresis aogonek -20 KPX ydieresis aring -20 KPX ydieresis atilde -20 KPX ydieresis comma -100 KPX ydieresis e -20 KPX ydieresis eacute -20 KPX ydieresis ecaron -20 KPX ydieresis ecircumflex -20 KPX ydieresis edieresis -20 KPX ydieresis edotaccent -20 KPX ydieresis egrave -20 KPX ydieresis emacron -20 KPX ydieresis eogonek -20 KPX ydieresis o -20 KPX ydieresis oacute -20 KPX ydieresis ocircumflex -20 KPX ydieresis odieresis -20 KPX ydieresis ograve -20 KPX ydieresis ohungarumlaut -20 KPX ydieresis omacron -20 KPX ydieresis oslash -20 KPX ydieresis otilde -20 KPX ydieresis period -100 KPX z e -15 KPX z eacute -15 KPX z ecaron -15 KPX z ecircumflex -15 KPX z edieresis -15 KPX z edotaccent -15 KPX z egrave -15 KPX z emacron -15 KPX z eogonek -15 KPX z o -15 KPX z oacute -15 KPX z ocircumflex -15 KPX z odieresis -15 KPX z ograve -15 KPX z ohungarumlaut -15 KPX z omacron -15 KPX z oslash -15 KPX z otilde -15 KPX zacute e -15 KPX zacute eacute -15 KPX zacute ecaron -15 KPX zacute ecircumflex -15 KPX zacute edieresis -15 KPX zacute edotaccent -15 KPX zacute egrave -15 KPX zacute emacron -15 KPX zacute eogonek -15 KPX zacute o -15 KPX zacute oacute -15 KPX zacute ocircumflex -15 KPX zacute odieresis -15 KPX zacute ograve -15 KPX zacute ohungarumlaut -15 KPX zacute omacron -15 KPX zacute oslash -15 KPX zacute otilde -15 KPX zcaron e -15 KPX zcaron eacute -15 KPX zcaron ecaron -15 KPX zcaron ecircumflex -15 KPX zcaron edieresis -15 KPX zcaron edotaccent -15 KPX zcaron egrave -15 KPX zcaron emacron -15 KPX zcaron eogonek -15 KPX zcaron o -15 KPX zcaron oacute -15 KPX zcaron ocircumflex -15 KPX zcaron odieresis -15 KPX zcaron ograve -15 KPX zcaron ohungarumlaut -15 KPX zcaron omacron -15 KPX zcaron oslash -15 KPX zcaron otilde -15 KPX zdotaccent e -15 KPX zdotaccent eacute -15 KPX zdotaccent ecaron -15 KPX zdotaccent ecircumflex -15 KPX zdotaccent edieresis -15 KPX zdotaccent edotaccent -15 KPX zdotaccent egrave -15 KPX zdotaccent emacron -15 KPX zdotaccent eogonek -15 KPX zdotaccent o -15 KPX zdotaccent oacute -15 KPX zdotaccent ocircumflex -15 KPX zdotaccent odieresis -15 KPX zdotaccent ograve -15 KPX zdotaccent ohungarumlaut -15 KPX zdotaccent omacron -15 KPX zdotaccent oslash -15 KPX zdotaccent otilde -15 EndKernPairs EndKernData EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/MustRead.html000066400000000000000000000016511320103431700271530ustar00rootroot00000000000000 Core 14 AFM Files - ReadMe or
This file and the 14 PostScript(R) AFM files it accompanies may be used, copied, and distributed for any purpose and without charge, with or without modification, provided that all copyright notices are retained; that the AFM files are not distributed without this file; that all modifications to this file or any of the AFM files are prominently noted in the modified file(s); and that this paragraph is not modified. Adobe Systems has no responsibility or obligation to support the use of the AFM files. Col
sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Symbol.afm000066400000000000000000000233411320103431700264730ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved. Comment Creation Date: Thu May 1 15:12:25 1997 Comment UniqueID 43064 Comment VMusage 30820 39997 FontName Symbol FullName Symbol FamilyName Symbol Weight Medium ItalicAngle 0 IsFixedPitch false CharacterSet Special FontBBox -180 -293 1090 1010 UnderlinePosition -100 UnderlineThickness 50 Version 001.008 Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved. EncodingScheme FontSpecific StdHW 92 StdVW 85 StartCharMetrics 190 C 32 ; WX 250 ; N space ; B 0 0 0 0 ; C 33 ; WX 333 ; N exclam ; B 128 -17 240 672 ; C 34 ; WX 713 ; N universal ; B 31 0 681 705 ; C 35 ; WX 500 ; N numbersign ; B 20 -16 481 673 ; C 36 ; WX 549 ; N existential ; B 25 0 478 707 ; C 37 ; WX 833 ; N percent ; B 63 -36 771 655 ; C 38 ; WX 778 ; N ampersand ; B 41 -18 750 661 ; C 39 ; WX 439 ; N suchthat ; B 48 -17 414 500 ; C 40 ; WX 333 ; N parenleft ; B 53 -191 300 673 ; C 41 ; WX 333 ; N parenright ; B 30 -191 277 673 ; C 42 ; WX 500 ; N asteriskmath ; B 65 134 427 551 ; C 43 ; WX 549 ; N plus ; B 10 0 539 533 ; C 44 ; WX 250 ; N comma ; B 56 -152 194 104 ; C 45 ; WX 549 ; N minus ; B 11 233 535 288 ; C 46 ; WX 250 ; N period ; B 69 -17 181 95 ; C 47 ; WX 278 ; N slash ; B 0 -18 254 646 ; C 48 ; WX 500 ; N zero ; B 24 -14 476 685 ; C 49 ; WX 500 ; N one ; B 117 0 390 673 ; C 50 ; WX 500 ; N two ; B 25 0 475 685 ; C 51 ; WX 500 ; N three ; B 43 -14 435 685 ; C 52 ; WX 500 ; N four ; B 15 0 469 685 ; C 53 ; WX 500 ; N five ; B 32 -14 445 690 ; C 54 ; WX 500 ; N six ; B 34 -14 468 685 ; C 55 ; WX 500 ; N seven ; B 24 -16 448 673 ; C 56 ; WX 500 ; N eight ; B 56 -14 445 685 ; C 57 ; WX 500 ; N nine ; B 30 -18 459 685 ; C 58 ; WX 278 ; N colon ; B 81 -17 193 460 ; C 59 ; WX 278 ; N semicolon ; B 83 -152 221 460 ; C 60 ; WX 549 ; N less ; B 26 0 523 522 ; C 61 ; WX 549 ; N equal ; B 11 141 537 390 ; C 62 ; WX 549 ; N greater ; B 26 0 523 522 ; C 63 ; WX 444 ; N question ; B 70 -17 412 686 ; C 64 ; WX 549 ; N congruent ; B 11 0 537 475 ; C 65 ; WX 722 ; N Alpha ; B 4 0 684 673 ; C 66 ; WX 667 ; N Beta ; B 29 0 592 673 ; C 67 ; WX 722 ; N Chi ; B -9 0 704 673 ; C 68 ; WX 612 ; N Delta ; B 6 0 608 688 ; C 69 ; WX 611 ; N Epsilon ; B 32 0 617 673 ; C 70 ; WX 763 ; N Phi ; B 26 0 741 673 ; C 71 ; WX 603 ; N Gamma ; B 24 0 609 673 ; C 72 ; WX 722 ; N Eta ; B 39 0 729 673 ; C 73 ; WX 333 ; N Iota ; B 32 0 316 673 ; C 74 ; WX 631 ; N theta1 ; B 18 -18 623 689 ; C 75 ; WX 722 ; N Kappa ; B 35 0 722 673 ; C 76 ; WX 686 ; N Lambda ; B 6 0 680 688 ; C 77 ; WX 889 ; N Mu ; B 28 0 887 673 ; C 78 ; WX 722 ; N Nu ; B 29 -8 720 673 ; C 79 ; WX 722 ; N Omicron ; B 41 -17 715 685 ; C 80 ; WX 768 ; N Pi ; B 25 0 745 673 ; C 81 ; WX 741 ; N Theta ; B 41 -17 715 685 ; C 82 ; WX 556 ; N Rho ; B 28 0 563 673 ; C 83 ; WX 592 ; N Sigma ; B 5 0 589 673 ; C 84 ; WX 611 ; N Tau ; B 33 0 607 673 ; C 85 ; WX 690 ; N Upsilon ; B -8 0 694 673 ; C 86 ; WX 439 ; N sigma1 ; B 40 -233 436 500 ; C 87 ; WX 768 ; N Omega ; B 34 0 736 688 ; C 88 ; WX 645 ; N Xi ; B 40 0 599 673 ; C 89 ; WX 795 ; N Psi ; B 15 0 781 684 ; C 90 ; WX 611 ; N Zeta ; B 44 0 636 673 ; C 91 ; WX 333 ; N bracketleft ; B 86 -155 299 674 ; C 92 ; WX 863 ; N therefore ; B 163 0 701 487 ; C 93 ; WX 333 ; N bracketright ; B 33 -155 246 674 ; C 94 ; WX 658 ; N perpendicular ; B 15 0 652 674 ; C 95 ; WX 500 ; N underscore ; B -2 -125 502 -75 ; C 96 ; WX 500 ; N radicalex ; B 480 881 1090 917 ; C 97 ; WX 631 ; N alpha ; B 41 -18 622 500 ; C 98 ; WX 549 ; N beta ; B 61 -223 515 741 ; C 99 ; WX 549 ; N chi ; B 12 -231 522 499 ; C 100 ; WX 494 ; N delta ; B 40 -19 481 740 ; C 101 ; WX 439 ; N epsilon ; B 22 -19 427 502 ; C 102 ; WX 521 ; N phi ; B 28 -224 492 673 ; C 103 ; WX 411 ; N gamma ; B 5 -225 484 499 ; C 104 ; WX 603 ; N eta ; B 0 -202 527 514 ; C 105 ; WX 329 ; N iota ; B 0 -17 301 503 ; C 106 ; WX 603 ; N phi1 ; B 36 -224 587 499 ; C 107 ; WX 549 ; N kappa ; B 33 0 558 501 ; C 108 ; WX 549 ; N lambda ; B 24 -17 548 739 ; C 109 ; WX 576 ; N mu ; B 33 -223 567 500 ; C 110 ; WX 521 ; N nu ; B -9 -16 475 507 ; C 111 ; WX 549 ; N omicron ; B 35 -19 501 499 ; C 112 ; WX 549 ; N pi ; B 10 -19 530 487 ; C 113 ; WX 521 ; N theta ; B 43 -17 485 690 ; C 114 ; WX 549 ; N rho ; B 50 -230 490 499 ; C 115 ; WX 603 ; N sigma ; B 30 -21 588 500 ; C 116 ; WX 439 ; N tau ; B 10 -19 418 500 ; C 117 ; WX 576 ; N upsilon ; B 7 -18 535 507 ; C 118 ; WX 713 ; N omega1 ; B 12 -18 671 583 ; C 119 ; WX 686 ; N omega ; B 42 -17 684 500 ; C 120 ; WX 493 ; N xi ; B 27 -224 469 766 ; C 121 ; WX 686 ; N psi ; B 12 -228 701 500 ; C 122 ; WX 494 ; N zeta ; B 60 -225 467 756 ; C 123 ; WX 480 ; N braceleft ; B 58 -183 397 673 ; C 124 ; WX 200 ; N bar ; B 65 -293 135 707 ; C 125 ; WX 480 ; N braceright ; B 79 -183 418 673 ; C 126 ; WX 549 ; N similar ; B 17 203 529 307 ; C 160 ; WX 750 ; N Euro ; B 20 -12 714 685 ; C 161 ; WX 620 ; N Upsilon1 ; B -2 0 610 685 ; C 162 ; WX 247 ; N minute ; B 27 459 228 735 ; C 163 ; WX 549 ; N lessequal ; B 29 0 526 639 ; C 164 ; WX 167 ; N fraction ; B -180 -12 340 677 ; C 165 ; WX 713 ; N infinity ; B 26 124 688 404 ; C 166 ; WX 500 ; N florin ; B 2 -193 494 686 ; C 167 ; WX 753 ; N club ; B 86 -26 660 533 ; C 168 ; WX 753 ; N diamond ; B 142 -36 600 550 ; C 169 ; WX 753 ; N heart ; B 117 -33 631 532 ; C 170 ; WX 753 ; N spade ; B 113 -36 629 548 ; C 171 ; WX 1042 ; N arrowboth ; B 24 -15 1024 511 ; C 172 ; WX 987 ; N arrowleft ; B 32 -15 942 511 ; C 173 ; WX 603 ; N arrowup ; B 45 0 571 910 ; C 174 ; WX 987 ; N arrowright ; B 49 -15 959 511 ; C 175 ; WX 603 ; N arrowdown ; B 45 -22 571 888 ; C 176 ; WX 400 ; N degree ; B 50 385 350 685 ; C 177 ; WX 549 ; N plusminus ; B 10 0 539 645 ; C 178 ; WX 411 ; N second ; B 20 459 413 737 ; C 179 ; WX 549 ; N greaterequal ; B 29 0 526 639 ; C 180 ; WX 549 ; N multiply ; B 17 8 533 524 ; C 181 ; WX 713 ; N proportional ; B 27 123 639 404 ; C 182 ; WX 494 ; N partialdiff ; B 26 -20 462 746 ; C 183 ; WX 460 ; N bullet ; B 50 113 410 473 ; C 184 ; WX 549 ; N divide ; B 10 71 536 456 ; C 185 ; WX 549 ; N notequal ; B 15 -25 540 549 ; C 186 ; WX 549 ; N equivalence ; B 14 82 538 443 ; C 187 ; WX 549 ; N approxequal ; B 14 135 527 394 ; C 188 ; WX 1000 ; N ellipsis ; B 111 -17 889 95 ; C 189 ; WX 603 ; N arrowvertex ; B 280 -120 336 1010 ; C 190 ; WX 1000 ; N arrowhorizex ; B -60 220 1050 276 ; C 191 ; WX 658 ; N carriagereturn ; B 15 -16 602 629 ; C 192 ; WX 823 ; N aleph ; B 175 -18 661 658 ; C 193 ; WX 686 ; N Ifraktur ; B 10 -53 578 740 ; C 194 ; WX 795 ; N Rfraktur ; B 26 -15 759 734 ; C 195 ; WX 987 ; N weierstrass ; B 159 -211 870 573 ; C 196 ; WX 768 ; N circlemultiply ; B 43 -17 733 673 ; C 197 ; WX 768 ; N circleplus ; B 43 -15 733 675 ; C 198 ; WX 823 ; N emptyset ; B 39 -24 781 719 ; C 199 ; WX 768 ; N intersection ; B 40 0 732 509 ; C 200 ; WX 768 ; N union ; B 40 -17 732 492 ; C 201 ; WX 713 ; N propersuperset ; B 20 0 673 470 ; C 202 ; WX 713 ; N reflexsuperset ; B 20 -125 673 470 ; C 203 ; WX 713 ; N notsubset ; B 36 -70 690 540 ; C 204 ; WX 713 ; N propersubset ; B 37 0 690 470 ; C 205 ; WX 713 ; N reflexsubset ; B 37 -125 690 470 ; C 206 ; WX 713 ; N element ; B 45 0 505 468 ; C 207 ; WX 713 ; N notelement ; B 45 -58 505 555 ; C 208 ; WX 768 ; N angle ; B 26 0 738 673 ; C 209 ; WX 713 ; N gradient ; B 36 -19 681 718 ; C 210 ; WX 790 ; N registerserif ; B 50 -17 740 673 ; C 211 ; WX 790 ; N copyrightserif ; B 51 -15 741 675 ; C 212 ; WX 890 ; N trademarkserif ; B 18 293 855 673 ; C 213 ; WX 823 ; N product ; B 25 -101 803 751 ; C 214 ; WX 549 ; N radical ; B 10 -38 515 917 ; C 215 ; WX 250 ; N dotmath ; B 69 210 169 310 ; C 216 ; WX 713 ; N logicalnot ; B 15 0 680 288 ; C 217 ; WX 603 ; N logicaland ; B 23 0 583 454 ; C 218 ; WX 603 ; N logicalor ; B 30 0 578 477 ; C 219 ; WX 1042 ; N arrowdblboth ; B 27 -20 1023 510 ; C 220 ; WX 987 ; N arrowdblleft ; B 30 -15 939 513 ; C 221 ; WX 603 ; N arrowdblup ; B 39 2 567 911 ; C 222 ; WX 987 ; N arrowdblright ; B 45 -20 954 508 ; C 223 ; WX 603 ; N arrowdbldown ; B 44 -19 572 890 ; C 224 ; WX 494 ; N lozenge ; B 18 0 466 745 ; C 225 ; WX 329 ; N angleleft ; B 25 -198 306 746 ; C 226 ; WX 790 ; N registersans ; B 50 -20 740 670 ; C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ; C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ; C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ; C 230 ; WX 384 ; N parenlefttp ; B 24 -293 436 926 ; C 231 ; WX 384 ; N parenleftex ; B 24 -85 108 925 ; C 232 ; WX 384 ; N parenleftbt ; B 24 -293 436 926 ; C 233 ; WX 384 ; N bracketlefttp ; B 0 -80 349 926 ; C 234 ; WX 384 ; N bracketleftex ; B 0 -79 77 925 ; C 235 ; WX 384 ; N bracketleftbt ; B 0 -80 349 926 ; C 236 ; WX 494 ; N bracelefttp ; B 209 -85 445 925 ; C 237 ; WX 494 ; N braceleftmid ; B 20 -85 284 935 ; C 238 ; WX 494 ; N braceleftbt ; B 209 -75 445 935 ; C 239 ; WX 494 ; N braceex ; B 209 -85 284 935 ; C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ; C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ; C 243 ; WX 686 ; N integraltp ; B 308 -88 675 920 ; C 244 ; WX 686 ; N integralex ; B 308 -88 378 975 ; C 245 ; WX 686 ; N integralbt ; B 11 -87 378 921 ; C 246 ; WX 384 ; N parenrighttp ; B 54 -293 466 926 ; C 247 ; WX 384 ; N parenrightex ; B 382 -85 466 925 ; C 248 ; WX 384 ; N parenrightbt ; B 54 -293 466 926 ; C 249 ; WX 384 ; N bracketrighttp ; B 22 -80 371 926 ; C 250 ; WX 384 ; N bracketrightex ; B 294 -79 371 925 ; C 251 ; WX 384 ; N bracketrightbt ; B 22 -80 371 926 ; C 252 ; WX 494 ; N bracerighttp ; B 48 -85 284 925 ; C 253 ; WX 494 ; N bracerightmid ; B 209 -85 473 935 ; C 254 ; WX 494 ; N bracerightbt ; B 48 -75 284 935 ; C -1 ; WX 790 ; N apple ; B 56 -3 733 808 ; EndCharMetrics EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Times-Bold.afm000066400000000000000000002024271320103431700271710ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 12:52:56 1997 Comment UniqueID 43065 Comment VMusage 41636 52661 FontName Times-Bold FullName Times Bold FamilyName Times Weight Bold ItalicAngle 0 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -168 -218 1000 935 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme AdobeStandardEncoding CapHeight 676 XHeight 461 Ascender 683 Descender -217 StdHW 44 StdVW 139 StartCharMetrics 315 C 32 ; WX 250 ; N space ; B 0 0 0 0 ; C 33 ; WX 333 ; N exclam ; B 81 -13 251 691 ; C 34 ; WX 555 ; N quotedbl ; B 83 404 472 691 ; C 35 ; WX 500 ; N numbersign ; B 4 0 496 700 ; C 36 ; WX 500 ; N dollar ; B 29 -99 472 750 ; C 37 ; WX 1000 ; N percent ; B 124 -14 877 692 ; C 38 ; WX 833 ; N ampersand ; B 62 -16 787 691 ; C 39 ; WX 333 ; N quoteright ; B 79 356 263 691 ; C 40 ; WX 333 ; N parenleft ; B 46 -168 306 694 ; C 41 ; WX 333 ; N parenright ; B 27 -168 287 694 ; C 42 ; WX 500 ; N asterisk ; B 56 255 447 691 ; C 43 ; WX 570 ; N plus ; B 33 0 537 506 ; C 44 ; WX 250 ; N comma ; B 39 -180 223 155 ; C 45 ; WX 333 ; N hyphen ; B 44 171 287 287 ; C 46 ; WX 250 ; N period ; B 41 -13 210 156 ; C 47 ; WX 278 ; N slash ; B -24 -19 302 691 ; C 48 ; WX 500 ; N zero ; B 24 -13 476 688 ; C 49 ; WX 500 ; N one ; B 65 0 442 688 ; C 50 ; WX 500 ; N two ; B 17 0 478 688 ; C 51 ; WX 500 ; N three ; B 16 -14 468 688 ; C 52 ; WX 500 ; N four ; B 19 0 475 688 ; C 53 ; WX 500 ; N five ; B 22 -8 470 676 ; C 54 ; WX 500 ; N six ; B 28 -13 475 688 ; C 55 ; WX 500 ; N seven ; B 17 0 477 676 ; C 56 ; WX 500 ; N eight ; B 28 -13 472 688 ; C 57 ; WX 500 ; N nine ; B 26 -13 473 688 ; C 58 ; WX 333 ; N colon ; B 82 -13 251 472 ; C 59 ; WX 333 ; N semicolon ; B 82 -180 266 472 ; C 60 ; WX 570 ; N less ; B 31 -8 539 514 ; C 61 ; WX 570 ; N equal ; B 33 107 537 399 ; C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ; C 63 ; WX 500 ; N question ; B 57 -13 445 689 ; C 64 ; WX 930 ; N at ; B 108 -19 822 691 ; C 65 ; WX 722 ; N A ; B 9 0 689 690 ; C 66 ; WX 667 ; N B ; B 16 0 619 676 ; C 67 ; WX 722 ; N C ; B 49 -19 687 691 ; C 68 ; WX 722 ; N D ; B 14 0 690 676 ; C 69 ; WX 667 ; N E ; B 16 0 641 676 ; C 70 ; WX 611 ; N F ; B 16 0 583 676 ; C 71 ; WX 778 ; N G ; B 37 -19 755 691 ; C 72 ; WX 778 ; N H ; B 21 0 759 676 ; C 73 ; WX 389 ; N I ; B 20 0 370 676 ; C 74 ; WX 500 ; N J ; B 3 -96 479 676 ; C 75 ; WX 778 ; N K ; B 30 0 769 676 ; C 76 ; WX 667 ; N L ; B 19 0 638 676 ; C 77 ; WX 944 ; N M ; B 14 0 921 676 ; C 78 ; WX 722 ; N N ; B 16 -18 701 676 ; C 79 ; WX 778 ; N O ; B 35 -19 743 691 ; C 80 ; WX 611 ; N P ; B 16 0 600 676 ; C 81 ; WX 778 ; N Q ; B 35 -176 743 691 ; C 82 ; WX 722 ; N R ; B 26 0 715 676 ; C 83 ; WX 556 ; N S ; B 35 -19 513 692 ; C 84 ; WX 667 ; N T ; B 31 0 636 676 ; C 85 ; WX 722 ; N U ; B 16 -19 701 676 ; C 86 ; WX 722 ; N V ; B 16 -18 701 676 ; C 87 ; WX 1000 ; N W ; B 19 -15 981 676 ; C 88 ; WX 722 ; N X ; B 16 0 699 676 ; C 89 ; WX 722 ; N Y ; B 15 0 699 676 ; C 90 ; WX 667 ; N Z ; B 28 0 634 676 ; C 91 ; WX 333 ; N bracketleft ; B 67 -149 301 678 ; C 92 ; WX 278 ; N backslash ; B -25 -19 303 691 ; C 93 ; WX 333 ; N bracketright ; B 32 -149 266 678 ; C 94 ; WX 581 ; N asciicircum ; B 73 311 509 676 ; C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; C 96 ; WX 333 ; N quoteleft ; B 70 356 254 691 ; C 97 ; WX 500 ; N a ; B 25 -14 488 473 ; C 98 ; WX 556 ; N b ; B 17 -14 521 676 ; C 99 ; WX 444 ; N c ; B 25 -14 430 473 ; C 100 ; WX 556 ; N d ; B 25 -14 534 676 ; C 101 ; WX 444 ; N e ; B 25 -14 426 473 ; C 102 ; WX 333 ; N f ; B 14 0 389 691 ; L i fi ; L l fl ; C 103 ; WX 500 ; N g ; B 28 -206 483 473 ; C 104 ; WX 556 ; N h ; B 16 0 534 676 ; C 105 ; WX 278 ; N i ; B 16 0 255 691 ; C 106 ; WX 333 ; N j ; B -57 -203 263 691 ; C 107 ; WX 556 ; N k ; B 22 0 543 676 ; C 108 ; WX 278 ; N l ; B 16 0 255 676 ; C 109 ; WX 833 ; N m ; B 16 0 814 473 ; C 110 ; WX 556 ; N n ; B 21 0 539 473 ; C 111 ; WX 500 ; N o ; B 25 -14 476 473 ; C 112 ; WX 556 ; N p ; B 19 -205 524 473 ; C 113 ; WX 556 ; N q ; B 34 -205 536 473 ; C 114 ; WX 444 ; N r ; B 29 0 434 473 ; C 115 ; WX 389 ; N s ; B 25 -14 361 473 ; C 116 ; WX 333 ; N t ; B 20 -12 332 630 ; C 117 ; WX 556 ; N u ; B 16 -14 537 461 ; C 118 ; WX 500 ; N v ; B 21 -14 485 461 ; C 119 ; WX 722 ; N w ; B 23 -14 707 461 ; C 120 ; WX 500 ; N x ; B 12 0 484 461 ; C 121 ; WX 500 ; N y ; B 16 -205 480 461 ; C 122 ; WX 444 ; N z ; B 21 0 420 461 ; C 123 ; WX 394 ; N braceleft ; B 22 -175 340 698 ; C 124 ; WX 220 ; N bar ; B 66 -218 154 782 ; C 125 ; WX 394 ; N braceright ; B 54 -175 372 698 ; C 126 ; WX 520 ; N asciitilde ; B 29 173 491 333 ; C 161 ; WX 333 ; N exclamdown ; B 82 -203 252 501 ; C 162 ; WX 500 ; N cent ; B 53 -140 458 588 ; C 163 ; WX 500 ; N sterling ; B 21 -14 477 684 ; C 164 ; WX 167 ; N fraction ; B -168 -12 329 688 ; C 165 ; WX 500 ; N yen ; B -64 0 547 676 ; C 166 ; WX 500 ; N florin ; B 0 -155 498 706 ; C 167 ; WX 500 ; N section ; B 57 -132 443 691 ; C 168 ; WX 500 ; N currency ; B -26 61 526 613 ; C 169 ; WX 278 ; N quotesingle ; B 75 404 204 691 ; C 170 ; WX 500 ; N quotedblleft ; B 32 356 486 691 ; C 171 ; WX 500 ; N guillemotleft ; B 23 36 473 415 ; C 172 ; WX 333 ; N guilsinglleft ; B 51 36 305 415 ; C 173 ; WX 333 ; N guilsinglright ; B 28 36 282 415 ; C 174 ; WX 556 ; N fi ; B 14 0 536 691 ; C 175 ; WX 556 ; N fl ; B 14 0 536 691 ; C 177 ; WX 500 ; N endash ; B 0 181 500 271 ; C 178 ; WX 500 ; N dagger ; B 47 -134 453 691 ; C 179 ; WX 500 ; N daggerdbl ; B 45 -132 456 691 ; C 180 ; WX 250 ; N periodcentered ; B 41 248 210 417 ; C 182 ; WX 540 ; N paragraph ; B 0 -186 519 676 ; C 183 ; WX 350 ; N bullet ; B 35 198 315 478 ; C 184 ; WX 333 ; N quotesinglbase ; B 79 -180 263 155 ; C 185 ; WX 500 ; N quotedblbase ; B 14 -180 468 155 ; C 186 ; WX 500 ; N quotedblright ; B 14 356 468 691 ; C 187 ; WX 500 ; N guillemotright ; B 27 36 477 415 ; C 188 ; WX 1000 ; N ellipsis ; B 82 -13 917 156 ; C 189 ; WX 1000 ; N perthousand ; B 7 -29 995 706 ; C 191 ; WX 500 ; N questiondown ; B 55 -201 443 501 ; C 193 ; WX 333 ; N grave ; B 8 528 246 713 ; C 194 ; WX 333 ; N acute ; B 86 528 324 713 ; C 195 ; WX 333 ; N circumflex ; B -2 528 335 704 ; C 196 ; WX 333 ; N tilde ; B -16 547 349 674 ; C 197 ; WX 333 ; N macron ; B 1 565 331 637 ; C 198 ; WX 333 ; N breve ; B 15 528 318 691 ; C 199 ; WX 333 ; N dotaccent ; B 103 536 258 691 ; C 200 ; WX 333 ; N dieresis ; B -2 537 335 667 ; C 202 ; WX 333 ; N ring ; B 60 527 273 740 ; C 203 ; WX 333 ; N cedilla ; B 68 -218 294 0 ; C 205 ; WX 333 ; N hungarumlaut ; B -13 528 425 713 ; C 206 ; WX 333 ; N ogonek ; B 90 -193 319 24 ; C 207 ; WX 333 ; N caron ; B -2 528 335 704 ; C 208 ; WX 1000 ; N emdash ; B 0 181 1000 271 ; C 225 ; WX 1000 ; N AE ; B 4 0 951 676 ; C 227 ; WX 300 ; N ordfeminine ; B -1 397 301 688 ; C 232 ; WX 667 ; N Lslash ; B 19 0 638 676 ; C 233 ; WX 778 ; N Oslash ; B 35 -74 743 737 ; C 234 ; WX 1000 ; N OE ; B 22 -5 981 684 ; C 235 ; WX 330 ; N ordmasculine ; B 18 397 312 688 ; C 241 ; WX 722 ; N ae ; B 33 -14 693 473 ; C 245 ; WX 278 ; N dotlessi ; B 16 0 255 461 ; C 248 ; WX 278 ; N lslash ; B -22 0 303 676 ; C 249 ; WX 500 ; N oslash ; B 25 -92 476 549 ; C 250 ; WX 722 ; N oe ; B 22 -14 696 473 ; C 251 ; WX 556 ; N germandbls ; B 19 -12 517 691 ; C -1 ; WX 389 ; N Idieresis ; B 20 0 370 877 ; C -1 ; WX 444 ; N eacute ; B 25 -14 426 713 ; C -1 ; WX 500 ; N abreve ; B 25 -14 488 691 ; C -1 ; WX 556 ; N uhungarumlaut ; B 16 -14 557 713 ; C -1 ; WX 444 ; N ecaron ; B 25 -14 426 704 ; C -1 ; WX 722 ; N Ydieresis ; B 15 0 699 877 ; C -1 ; WX 570 ; N divide ; B 33 -31 537 537 ; C -1 ; WX 722 ; N Yacute ; B 15 0 699 923 ; C -1 ; WX 722 ; N Acircumflex ; B 9 0 689 914 ; C -1 ; WX 500 ; N aacute ; B 25 -14 488 713 ; C -1 ; WX 722 ; N Ucircumflex ; B 16 -19 701 914 ; C -1 ; WX 500 ; N yacute ; B 16 -205 480 713 ; C -1 ; WX 389 ; N scommaaccent ; B 25 -218 361 473 ; C -1 ; WX 444 ; N ecircumflex ; B 25 -14 426 704 ; C -1 ; WX 722 ; N Uring ; B 16 -19 701 935 ; C -1 ; WX 722 ; N Udieresis ; B 16 -19 701 877 ; C -1 ; WX 500 ; N aogonek ; B 25 -193 504 473 ; C -1 ; WX 722 ; N Uacute ; B 16 -19 701 923 ; C -1 ; WX 556 ; N uogonek ; B 16 -193 539 461 ; C -1 ; WX 667 ; N Edieresis ; B 16 0 641 877 ; C -1 ; WX 722 ; N Dcroat ; B 6 0 690 676 ; C -1 ; WX 250 ; N commaaccent ; B 47 -218 203 -50 ; C -1 ; WX 747 ; N copyright ; B 26 -19 721 691 ; C -1 ; WX 667 ; N Emacron ; B 16 0 641 847 ; C -1 ; WX 444 ; N ccaron ; B 25 -14 430 704 ; C -1 ; WX 500 ; N aring ; B 25 -14 488 740 ; C -1 ; WX 722 ; N Ncommaaccent ; B 16 -188 701 676 ; C -1 ; WX 278 ; N lacute ; B 16 0 297 923 ; C -1 ; WX 500 ; N agrave ; B 25 -14 488 713 ; C -1 ; WX 667 ; N Tcommaaccent ; B 31 -218 636 676 ; C -1 ; WX 722 ; N Cacute ; B 49 -19 687 923 ; C -1 ; WX 500 ; N atilde ; B 25 -14 488 674 ; C -1 ; WX 667 ; N Edotaccent ; B 16 0 641 901 ; C -1 ; WX 389 ; N scaron ; B 25 -14 363 704 ; C -1 ; WX 389 ; N scedilla ; B 25 -218 361 473 ; C -1 ; WX 278 ; N iacute ; B 16 0 289 713 ; C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ; C -1 ; WX 722 ; N Rcaron ; B 26 0 715 914 ; C -1 ; WX 778 ; N Gcommaaccent ; B 37 -218 755 691 ; C -1 ; WX 556 ; N ucircumflex ; B 16 -14 537 704 ; C -1 ; WX 500 ; N acircumflex ; B 25 -14 488 704 ; C -1 ; WX 722 ; N Amacron ; B 9 0 689 847 ; C -1 ; WX 444 ; N rcaron ; B 29 0 434 704 ; C -1 ; WX 444 ; N ccedilla ; B 25 -218 430 473 ; C -1 ; WX 667 ; N Zdotaccent ; B 28 0 634 901 ; C -1 ; WX 611 ; N Thorn ; B 16 0 600 676 ; C -1 ; WX 778 ; N Omacron ; B 35 -19 743 847 ; C -1 ; WX 722 ; N Racute ; B 26 0 715 923 ; C -1 ; WX 556 ; N Sacute ; B 35 -19 513 923 ; C -1 ; WX 672 ; N dcaron ; B 25 -14 681 682 ; C -1 ; WX 722 ; N Umacron ; B 16 -19 701 847 ; C -1 ; WX 556 ; N uring ; B 16 -14 537 740 ; C -1 ; WX 300 ; N threesuperior ; B 3 268 297 688 ; C -1 ; WX 778 ; N Ograve ; B 35 -19 743 923 ; C -1 ; WX 722 ; N Agrave ; B 9 0 689 923 ; C -1 ; WX 722 ; N Abreve ; B 9 0 689 901 ; C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ; C -1 ; WX 556 ; N uacute ; B 16 -14 537 713 ; C -1 ; WX 667 ; N Tcaron ; B 31 0 636 914 ; C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ; C -1 ; WX 500 ; N ydieresis ; B 16 -205 480 667 ; C -1 ; WX 722 ; N Nacute ; B 16 -18 701 923 ; C -1 ; WX 278 ; N icircumflex ; B -37 0 300 704 ; C -1 ; WX 667 ; N Ecircumflex ; B 16 0 641 914 ; C -1 ; WX 500 ; N adieresis ; B 25 -14 488 667 ; C -1 ; WX 444 ; N edieresis ; B 25 -14 426 667 ; C -1 ; WX 444 ; N cacute ; B 25 -14 430 713 ; C -1 ; WX 556 ; N nacute ; B 21 0 539 713 ; C -1 ; WX 556 ; N umacron ; B 16 -14 537 637 ; C -1 ; WX 722 ; N Ncaron ; B 16 -18 701 914 ; C -1 ; WX 389 ; N Iacute ; B 20 0 370 923 ; C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ; C -1 ; WX 220 ; N brokenbar ; B 66 -143 154 707 ; C -1 ; WX 747 ; N registered ; B 26 -19 721 691 ; C -1 ; WX 778 ; N Gbreve ; B 37 -19 755 901 ; C -1 ; WX 389 ; N Idotaccent ; B 20 0 370 901 ; C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ; C -1 ; WX 667 ; N Egrave ; B 16 0 641 923 ; C -1 ; WX 444 ; N racute ; B 29 0 434 713 ; C -1 ; WX 500 ; N omacron ; B 25 -14 476 637 ; C -1 ; WX 667 ; N Zacute ; B 28 0 634 923 ; C -1 ; WX 667 ; N Zcaron ; B 28 0 634 914 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ; C -1 ; WX 722 ; N Eth ; B 6 0 690 676 ; C -1 ; WX 722 ; N Ccedilla ; B 49 -218 687 691 ; C -1 ; WX 278 ; N lcommaaccent ; B 16 -218 255 676 ; C -1 ; WX 416 ; N tcaron ; B 20 -12 425 815 ; C -1 ; WX 444 ; N eogonek ; B 25 -193 426 473 ; C -1 ; WX 722 ; N Uogonek ; B 16 -193 701 676 ; C -1 ; WX 722 ; N Aacute ; B 9 0 689 923 ; C -1 ; WX 722 ; N Adieresis ; B 9 0 689 877 ; C -1 ; WX 444 ; N egrave ; B 25 -14 426 713 ; C -1 ; WX 444 ; N zacute ; B 21 0 420 713 ; C -1 ; WX 278 ; N iogonek ; B 16 -193 274 691 ; C -1 ; WX 778 ; N Oacute ; B 35 -19 743 923 ; C -1 ; WX 500 ; N oacute ; B 25 -14 476 713 ; C -1 ; WX 500 ; N amacron ; B 25 -14 488 637 ; C -1 ; WX 389 ; N sacute ; B 25 -14 361 713 ; C -1 ; WX 278 ; N idieresis ; B -37 0 300 667 ; C -1 ; WX 778 ; N Ocircumflex ; B 35 -19 743 914 ; C -1 ; WX 722 ; N Ugrave ; B 16 -19 701 923 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C -1 ; WX 556 ; N thorn ; B 19 -205 524 676 ; C -1 ; WX 300 ; N twosuperior ; B 0 275 300 688 ; C -1 ; WX 778 ; N Odieresis ; B 35 -19 743 877 ; C -1 ; WX 556 ; N mu ; B 33 -206 536 461 ; C -1 ; WX 278 ; N igrave ; B -27 0 255 713 ; C -1 ; WX 500 ; N ohungarumlaut ; B 25 -14 529 713 ; C -1 ; WX 667 ; N Eogonek ; B 16 -193 644 676 ; C -1 ; WX 556 ; N dcroat ; B 25 -14 534 676 ; C -1 ; WX 750 ; N threequarters ; B 23 -12 733 688 ; C -1 ; WX 556 ; N Scedilla ; B 35 -218 513 692 ; C -1 ; WX 394 ; N lcaron ; B 16 0 412 682 ; C -1 ; WX 778 ; N Kcommaaccent ; B 30 -218 769 676 ; C -1 ; WX 667 ; N Lacute ; B 19 0 638 923 ; C -1 ; WX 1000 ; N trademark ; B 24 271 977 676 ; C -1 ; WX 444 ; N edotaccent ; B 25 -14 426 691 ; C -1 ; WX 389 ; N Igrave ; B 20 0 370 923 ; C -1 ; WX 389 ; N Imacron ; B 20 0 370 847 ; C -1 ; WX 667 ; N Lcaron ; B 19 0 652 682 ; C -1 ; WX 750 ; N onehalf ; B -7 -12 775 688 ; C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ; C -1 ; WX 500 ; N ocircumflex ; B 25 -14 476 704 ; C -1 ; WX 556 ; N ntilde ; B 21 0 539 674 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 16 -19 701 923 ; C -1 ; WX 667 ; N Eacute ; B 16 0 641 923 ; C -1 ; WX 444 ; N emacron ; B 25 -14 426 637 ; C -1 ; WX 500 ; N gbreve ; B 28 -206 483 691 ; C -1 ; WX 750 ; N onequarter ; B 28 -12 743 688 ; C -1 ; WX 556 ; N Scaron ; B 35 -19 513 914 ; C -1 ; WX 556 ; N Scommaaccent ; B 35 -218 513 692 ; C -1 ; WX 778 ; N Ohungarumlaut ; B 35 -19 743 923 ; C -1 ; WX 400 ; N degree ; B 57 402 343 688 ; C -1 ; WX 500 ; N ograve ; B 25 -14 476 713 ; C -1 ; WX 722 ; N Ccaron ; B 49 -19 687 914 ; C -1 ; WX 556 ; N ugrave ; B 16 -14 537 713 ; C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ; C -1 ; WX 722 ; N Dcaron ; B 14 0 690 914 ; C -1 ; WX 444 ; N rcommaaccent ; B 29 -218 434 473 ; C -1 ; WX 722 ; N Ntilde ; B 16 -18 701 884 ; C -1 ; WX 500 ; N otilde ; B 25 -14 476 674 ; C -1 ; WX 722 ; N Rcommaaccent ; B 26 -218 715 676 ; C -1 ; WX 667 ; N Lcommaaccent ; B 19 -218 638 676 ; C -1 ; WX 722 ; N Atilde ; B 9 0 689 884 ; C -1 ; WX 722 ; N Aogonek ; B 9 -193 699 690 ; C -1 ; WX 722 ; N Aring ; B 9 0 689 935 ; C -1 ; WX 778 ; N Otilde ; B 35 -19 743 884 ; C -1 ; WX 444 ; N zdotaccent ; B 21 0 420 691 ; C -1 ; WX 667 ; N Ecaron ; B 16 0 641 914 ; C -1 ; WX 389 ; N Iogonek ; B 20 -193 370 676 ; C -1 ; WX 556 ; N kcommaaccent ; B 22 -218 543 676 ; C -1 ; WX 570 ; N minus ; B 33 209 537 297 ; C -1 ; WX 389 ; N Icircumflex ; B 20 0 370 914 ; C -1 ; WX 556 ; N ncaron ; B 21 0 539 704 ; C -1 ; WX 333 ; N tcommaaccent ; B 20 -218 332 630 ; C -1 ; WX 570 ; N logicalnot ; B 33 108 537 399 ; C -1 ; WX 500 ; N odieresis ; B 25 -14 476 667 ; C -1 ; WX 556 ; N udieresis ; B 16 -14 537 667 ; C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ; C -1 ; WX 500 ; N gcommaaccent ; B 28 -206 483 829 ; C -1 ; WX 500 ; N eth ; B 25 -14 476 691 ; C -1 ; WX 444 ; N zcaron ; B 21 0 420 704 ; C -1 ; WX 556 ; N ncommaaccent ; B 21 -218 539 473 ; C -1 ; WX 300 ; N onesuperior ; B 28 275 273 688 ; C -1 ; WX 278 ; N imacron ; B -8 0 272 637 ; C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2242 KPX A C -55 KPX A Cacute -55 KPX A Ccaron -55 KPX A Ccedilla -55 KPX A G -55 KPX A Gbreve -55 KPX A Gcommaaccent -55 KPX A O -45 KPX A Oacute -45 KPX A Ocircumflex -45 KPX A Odieresis -45 KPX A Ograve -45 KPX A Ohungarumlaut -45 KPX A Omacron -45 KPX A Oslash -45 KPX A Otilde -45 KPX A Q -45 KPX A T -95 KPX A Tcaron -95 KPX A Tcommaaccent -95 KPX A U -50 KPX A Uacute -50 KPX A Ucircumflex -50 KPX A Udieresis -50 KPX A Ugrave -50 KPX A Uhungarumlaut -50 KPX A Umacron -50 KPX A Uogonek -50 KPX A Uring -50 KPX A V -145 KPX A W -130 KPX A Y -100 KPX A Yacute -100 KPX A Ydieresis -100 KPX A p -25 KPX A quoteright -74 KPX A u -50 KPX A uacute -50 KPX A ucircumflex -50 KPX A udieresis -50 KPX A ugrave -50 KPX A uhungarumlaut -50 KPX A umacron -50 KPX A uogonek -50 KPX A uring -50 KPX A v -100 KPX A w -90 KPX A y -74 KPX A yacute -74 KPX A ydieresis -74 KPX Aacute C -55 KPX Aacute Cacute -55 KPX Aacute Ccaron -55 KPX Aacute Ccedilla -55 KPX Aacute G -55 KPX Aacute Gbreve -55 KPX Aacute Gcommaaccent -55 KPX Aacute O -45 KPX Aacute Oacute -45 KPX Aacute Ocircumflex -45 KPX Aacute Odieresis -45 KPX Aacute Ograve -45 KPX Aacute Ohungarumlaut -45 KPX Aacute Omacron -45 KPX Aacute Oslash -45 KPX Aacute Otilde -45 KPX Aacute Q -45 KPX Aacute T -95 KPX Aacute Tcaron -95 KPX Aacute Tcommaaccent -95 KPX Aacute U -50 KPX Aacute Uacute -50 KPX Aacute Ucircumflex -50 KPX Aacute Udieresis -50 KPX Aacute Ugrave -50 KPX Aacute Uhungarumlaut -50 KPX Aacute Umacron -50 KPX Aacute Uogonek -50 KPX Aacute Uring -50 KPX Aacute V -145 KPX Aacute W -130 KPX Aacute Y -100 KPX Aacute Yacute -100 KPX Aacute Ydieresis -100 KPX Aacute p -25 KPX Aacute quoteright -74 KPX Aacute u -50 KPX Aacute uacute -50 KPX Aacute ucircumflex -50 KPX Aacute udieresis -50 KPX Aacute ugrave -50 KPX Aacute uhungarumlaut -50 KPX Aacute umacron -50 KPX Aacute uogonek -50 KPX Aacute uring -50 KPX Aacute v -100 KPX Aacute w -90 KPX Aacute y -74 KPX Aacute yacute -74 KPX Aacute ydieresis -74 KPX Abreve C -55 KPX Abreve Cacute -55 KPX Abreve Ccaron -55 KPX Abreve Ccedilla -55 KPX Abreve G -55 KPX Abreve Gbreve -55 KPX Abreve Gcommaaccent -55 KPX Abreve O -45 KPX Abreve Oacute -45 KPX Abreve Ocircumflex -45 KPX Abreve Odieresis -45 KPX Abreve Ograve -45 KPX Abreve Ohungarumlaut -45 KPX Abreve Omacron -45 KPX Abreve Oslash -45 KPX Abreve Otilde -45 KPX Abreve Q -45 KPX Abreve T -95 KPX Abreve Tcaron -95 KPX Abreve Tcommaaccent -95 KPX Abreve U -50 KPX Abreve Uacute -50 KPX Abreve Ucircumflex -50 KPX Abreve Udieresis -50 KPX Abreve Ugrave -50 KPX Abreve Uhungarumlaut -50 KPX Abreve Umacron -50 KPX Abreve Uogonek -50 KPX Abreve Uring -50 KPX Abreve V -145 KPX Abreve W -130 KPX Abreve Y -100 KPX Abreve Yacute -100 KPX Abreve Ydieresis -100 KPX Abreve p -25 KPX Abreve quoteright -74 KPX Abreve u -50 KPX Abreve uacute -50 KPX Abreve ucircumflex -50 KPX Abreve udieresis -50 KPX Abreve ugrave -50 KPX Abreve uhungarumlaut -50 KPX Abreve umacron -50 KPX Abreve uogonek -50 KPX Abreve uring -50 KPX Abreve v -100 KPX Abreve w -90 KPX Abreve y -74 KPX Abreve yacute -74 KPX Abreve ydieresis -74 KPX Acircumflex C -55 KPX Acircumflex Cacute -55 KPX Acircumflex Ccaron -55 KPX Acircumflex Ccedilla -55 KPX Acircumflex G -55 KPX Acircumflex Gbreve -55 KPX Acircumflex Gcommaaccent -55 KPX Acircumflex O -45 KPX Acircumflex Oacute -45 KPX Acircumflex Ocircumflex -45 KPX Acircumflex Odieresis -45 KPX Acircumflex Ograve -45 KPX Acircumflex Ohungarumlaut -45 KPX Acircumflex Omacron -45 KPX Acircumflex Oslash -45 KPX Acircumflex Otilde -45 KPX Acircumflex Q -45 KPX Acircumflex T -95 KPX Acircumflex Tcaron -95 KPX Acircumflex Tcommaaccent -95 KPX Acircumflex U -50 KPX Acircumflex Uacute -50 KPX Acircumflex Ucircumflex -50 KPX Acircumflex Udieresis -50 KPX Acircumflex Ugrave -50 KPX Acircumflex Uhungarumlaut -50 KPX Acircumflex Umacron -50 KPX Acircumflex Uogonek -50 KPX Acircumflex Uring -50 KPX Acircumflex V -145 KPX Acircumflex W -130 KPX Acircumflex Y -100 KPX Acircumflex Yacute -100 KPX Acircumflex Ydieresis -100 KPX Acircumflex p -25 KPX Acircumflex quoteright -74 KPX Acircumflex u -50 KPX Acircumflex uacute -50 KPX Acircumflex ucircumflex -50 KPX Acircumflex udieresis -50 KPX Acircumflex ugrave -50 KPX Acircumflex uhungarumlaut -50 KPX Acircumflex umacron -50 KPX Acircumflex uogonek -50 KPX Acircumflex uring -50 KPX Acircumflex v -100 KPX Acircumflex w -90 KPX Acircumflex y -74 KPX Acircumflex yacute -74 KPX Acircumflex ydieresis -74 KPX Adieresis C -55 KPX Adieresis Cacute -55 KPX Adieresis Ccaron -55 KPX Adieresis Ccedilla -55 KPX Adieresis G -55 KPX Adieresis Gbreve -55 KPX Adieresis Gcommaaccent -55 KPX Adieresis O -45 KPX Adieresis Oacute -45 KPX Adieresis Ocircumflex -45 KPX Adieresis Odieresis -45 KPX Adieresis Ograve -45 KPX Adieresis Ohungarumlaut -45 KPX Adieresis Omacron -45 KPX Adieresis Oslash -45 KPX Adieresis Otilde -45 KPX Adieresis Q -45 KPX Adieresis T -95 KPX Adieresis Tcaron -95 KPX Adieresis Tcommaaccent -95 KPX Adieresis U -50 KPX Adieresis Uacute -50 KPX Adieresis Ucircumflex -50 KPX Adieresis Udieresis -50 KPX Adieresis Ugrave -50 KPX Adieresis Uhungarumlaut -50 KPX Adieresis Umacron -50 KPX Adieresis Uogonek -50 KPX Adieresis Uring -50 KPX Adieresis V -145 KPX Adieresis W -130 KPX Adieresis Y -100 KPX Adieresis Yacute -100 KPX Adieresis Ydieresis -100 KPX Adieresis p -25 KPX Adieresis quoteright -74 KPX Adieresis u -50 KPX Adieresis uacute -50 KPX Adieresis ucircumflex -50 KPX Adieresis udieresis -50 KPX Adieresis ugrave -50 KPX Adieresis uhungarumlaut -50 KPX Adieresis umacron -50 KPX Adieresis uogonek -50 KPX Adieresis uring -50 KPX Adieresis v -100 KPX Adieresis w -90 KPX Adieresis y -74 KPX Adieresis yacute -74 KPX Adieresis ydieresis -74 KPX Agrave C -55 KPX Agrave Cacute -55 KPX Agrave Ccaron -55 KPX Agrave Ccedilla -55 KPX Agrave G -55 KPX Agrave Gbreve -55 KPX Agrave Gcommaaccent -55 KPX Agrave O -45 KPX Agrave Oacute -45 KPX Agrave Ocircumflex -45 KPX Agrave Odieresis -45 KPX Agrave Ograve -45 KPX Agrave Ohungarumlaut -45 KPX Agrave Omacron -45 KPX Agrave Oslash -45 KPX Agrave Otilde -45 KPX Agrave Q -45 KPX Agrave T -95 KPX Agrave Tcaron -95 KPX Agrave Tcommaaccent -95 KPX Agrave U -50 KPX Agrave Uacute -50 KPX Agrave Ucircumflex -50 KPX Agrave Udieresis -50 KPX Agrave Ugrave -50 KPX Agrave Uhungarumlaut -50 KPX Agrave Umacron -50 KPX Agrave Uogonek -50 KPX Agrave Uring -50 KPX Agrave V -145 KPX Agrave W -130 KPX Agrave Y -100 KPX Agrave Yacute -100 KPX Agrave Ydieresis -100 KPX Agrave p -25 KPX Agrave quoteright -74 KPX Agrave u -50 KPX Agrave uacute -50 KPX Agrave ucircumflex -50 KPX Agrave udieresis -50 KPX Agrave ugrave -50 KPX Agrave uhungarumlaut -50 KPX Agrave umacron -50 KPX Agrave uogonek -50 KPX Agrave uring -50 KPX Agrave v -100 KPX Agrave w -90 KPX Agrave y -74 KPX Agrave yacute -74 KPX Agrave ydieresis -74 KPX Amacron C -55 KPX Amacron Cacute -55 KPX Amacron Ccaron -55 KPX Amacron Ccedilla -55 KPX Amacron G -55 KPX Amacron Gbreve -55 KPX Amacron Gcommaaccent -55 KPX Amacron O -45 KPX Amacron Oacute -45 KPX Amacron Ocircumflex -45 KPX Amacron Odieresis -45 KPX Amacron Ograve -45 KPX Amacron Ohungarumlaut -45 KPX Amacron Omacron -45 KPX Amacron Oslash -45 KPX Amacron Otilde -45 KPX Amacron Q -45 KPX Amacron T -95 KPX Amacron Tcaron -95 KPX Amacron Tcommaaccent -95 KPX Amacron U -50 KPX Amacron Uacute -50 KPX Amacron Ucircumflex -50 KPX Amacron Udieresis -50 KPX Amacron Ugrave -50 KPX Amacron Uhungarumlaut -50 KPX Amacron Umacron -50 KPX Amacron Uogonek -50 KPX Amacron Uring -50 KPX Amacron V -145 KPX Amacron W -130 KPX Amacron Y -100 KPX Amacron Yacute -100 KPX Amacron Ydieresis -100 KPX Amacron p -25 KPX Amacron quoteright -74 KPX Amacron u -50 KPX Amacron uacute -50 KPX Amacron ucircumflex -50 KPX Amacron udieresis -50 KPX Amacron ugrave -50 KPX Amacron uhungarumlaut -50 KPX Amacron umacron -50 KPX Amacron uogonek -50 KPX Amacron uring -50 KPX Amacron v -100 KPX Amacron w -90 KPX Amacron y -74 KPX Amacron yacute -74 KPX Amacron ydieresis -74 KPX Aogonek C -55 KPX Aogonek Cacute -55 KPX Aogonek Ccaron -55 KPX Aogonek Ccedilla -55 KPX Aogonek G -55 KPX Aogonek Gbreve -55 KPX Aogonek Gcommaaccent -55 KPX Aogonek O -45 KPX Aogonek Oacute -45 KPX Aogonek Ocircumflex -45 KPX Aogonek Odieresis -45 KPX Aogonek Ograve -45 KPX Aogonek Ohungarumlaut -45 KPX Aogonek Omacron -45 KPX Aogonek Oslash -45 KPX Aogonek Otilde -45 KPX Aogonek Q -45 KPX Aogonek T -95 KPX Aogonek Tcaron -95 KPX Aogonek Tcommaaccent -95 KPX Aogonek U -50 KPX Aogonek Uacute -50 KPX Aogonek Ucircumflex -50 KPX Aogonek Udieresis -50 KPX Aogonek Ugrave -50 KPX Aogonek Uhungarumlaut -50 KPX Aogonek Umacron -50 KPX Aogonek Uogonek -50 KPX Aogonek Uring -50 KPX Aogonek V -145 KPX Aogonek W -130 KPX Aogonek Y -100 KPX Aogonek Yacute -100 KPX Aogonek Ydieresis -100 KPX Aogonek p -25 KPX Aogonek quoteright -74 KPX Aogonek u -50 KPX Aogonek uacute -50 KPX Aogonek ucircumflex -50 KPX Aogonek udieresis -50 KPX Aogonek ugrave -50 KPX Aogonek uhungarumlaut -50 KPX Aogonek umacron -50 KPX Aogonek uogonek -50 KPX Aogonek uring -50 KPX Aogonek v -100 KPX Aogonek w -90 KPX Aogonek y -34 KPX Aogonek yacute -34 KPX Aogonek ydieresis -34 KPX Aring C -55 KPX Aring Cacute -55 KPX Aring Ccaron -55 KPX Aring Ccedilla -55 KPX Aring G -55 KPX Aring Gbreve -55 KPX Aring Gcommaaccent -55 KPX Aring O -45 KPX Aring Oacute -45 KPX Aring Ocircumflex -45 KPX Aring Odieresis -45 KPX Aring Ograve -45 KPX Aring Ohungarumlaut -45 KPX Aring Omacron -45 KPX Aring Oslash -45 KPX Aring Otilde -45 KPX Aring Q -45 KPX Aring T -95 KPX Aring Tcaron -95 KPX Aring Tcommaaccent -95 KPX Aring U -50 KPX Aring Uacute -50 KPX Aring Ucircumflex -50 KPX Aring Udieresis -50 KPX Aring Ugrave -50 KPX Aring Uhungarumlaut -50 KPX Aring Umacron -50 KPX Aring Uogonek -50 KPX Aring Uring -50 KPX Aring V -145 KPX Aring W -130 KPX Aring Y -100 KPX Aring Yacute -100 KPX Aring Ydieresis -100 KPX Aring p -25 KPX Aring quoteright -74 KPX Aring u -50 KPX Aring uacute -50 KPX Aring ucircumflex -50 KPX Aring udieresis -50 KPX Aring ugrave -50 KPX Aring uhungarumlaut -50 KPX Aring umacron -50 KPX Aring uogonek -50 KPX Aring uring -50 KPX Aring v -100 KPX Aring w -90 KPX Aring y -74 KPX Aring yacute -74 KPX Aring ydieresis -74 KPX Atilde C -55 KPX Atilde Cacute -55 KPX Atilde Ccaron -55 KPX Atilde Ccedilla -55 KPX Atilde G -55 KPX Atilde Gbreve -55 KPX Atilde Gcommaaccent -55 KPX Atilde O -45 KPX Atilde Oacute -45 KPX Atilde Ocircumflex -45 KPX Atilde Odieresis -45 KPX Atilde Ograve -45 KPX Atilde Ohungarumlaut -45 KPX Atilde Omacron -45 KPX Atilde Oslash -45 KPX Atilde Otilde -45 KPX Atilde Q -45 KPX Atilde T -95 KPX Atilde Tcaron -95 KPX Atilde Tcommaaccent -95 KPX Atilde U -50 KPX Atilde Uacute -50 KPX Atilde Ucircumflex -50 KPX Atilde Udieresis -50 KPX Atilde Ugrave -50 KPX Atilde Uhungarumlaut -50 KPX Atilde Umacron -50 KPX Atilde Uogonek -50 KPX Atilde Uring -50 KPX Atilde V -145 KPX Atilde W -130 KPX Atilde Y -100 KPX Atilde Yacute -100 KPX Atilde Ydieresis -100 KPX Atilde p -25 KPX Atilde quoteright -74 KPX Atilde u -50 KPX Atilde uacute -50 KPX Atilde ucircumflex -50 KPX Atilde udieresis -50 KPX Atilde ugrave -50 KPX Atilde uhungarumlaut -50 KPX Atilde umacron -50 KPX Atilde uogonek -50 KPX Atilde uring -50 KPX Atilde v -100 KPX Atilde w -90 KPX Atilde y -74 KPX Atilde yacute -74 KPX Atilde ydieresis -74 KPX B A -30 KPX B Aacute -30 KPX B Abreve -30 KPX B Acircumflex -30 KPX B Adieresis -30 KPX B Agrave -30 KPX B Amacron -30 KPX B Aogonek -30 KPX B Aring -30 KPX B Atilde -30 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX D A -35 KPX D Aacute -35 KPX D Abreve -35 KPX D Acircumflex -35 KPX D Adieresis -35 KPX D Agrave -35 KPX D Amacron -35 KPX D Aogonek -35 KPX D Aring -35 KPX D Atilde -35 KPX D V -40 KPX D W -40 KPX D Y -40 KPX D Yacute -40 KPX D Ydieresis -40 KPX D period -20 KPX Dcaron A -35 KPX Dcaron Aacute -35 KPX Dcaron Abreve -35 KPX Dcaron Acircumflex -35 KPX Dcaron Adieresis -35 KPX Dcaron Agrave -35 KPX Dcaron Amacron -35 KPX Dcaron Aogonek -35 KPX Dcaron Aring -35 KPX Dcaron Atilde -35 KPX Dcaron V -40 KPX Dcaron W -40 KPX Dcaron Y -40 KPX Dcaron Yacute -40 KPX Dcaron Ydieresis -40 KPX Dcaron period -20 KPX Dcroat A -35 KPX Dcroat Aacute -35 KPX Dcroat Abreve -35 KPX Dcroat Acircumflex -35 KPX Dcroat Adieresis -35 KPX Dcroat Agrave -35 KPX Dcroat Amacron -35 KPX Dcroat Aogonek -35 KPX Dcroat Aring -35 KPX Dcroat Atilde -35 KPX Dcroat V -40 KPX Dcroat W -40 KPX Dcroat Y -40 KPX Dcroat Yacute -40 KPX Dcroat Ydieresis -40 KPX Dcroat period -20 KPX F A -90 KPX F Aacute -90 KPX F Abreve -90 KPX F Acircumflex -90 KPX F Adieresis -90 KPX F Agrave -90 KPX F Amacron -90 KPX F Aogonek -90 KPX F Aring -90 KPX F Atilde -90 KPX F a -25 KPX F aacute -25 KPX F abreve -25 KPX F acircumflex -25 KPX F adieresis -25 KPX F agrave -25 KPX F amacron -25 KPX F aogonek -25 KPX F aring -25 KPX F atilde -25 KPX F comma -92 KPX F e -25 KPX F eacute -25 KPX F ecaron -25 KPX F ecircumflex -25 KPX F edieresis -25 KPX F edotaccent -25 KPX F egrave -25 KPX F emacron -25 KPX F eogonek -25 KPX F o -25 KPX F oacute -25 KPX F ocircumflex -25 KPX F odieresis -25 KPX F ograve -25 KPX F ohungarumlaut -25 KPX F omacron -25 KPX F oslash -25 KPX F otilde -25 KPX F period -110 KPX J A -30 KPX J Aacute -30 KPX J Abreve -30 KPX J Acircumflex -30 KPX J Adieresis -30 KPX J Agrave -30 KPX J Amacron -30 KPX J Aogonek -30 KPX J Aring -30 KPX J Atilde -30 KPX J a -15 KPX J aacute -15 KPX J abreve -15 KPX J acircumflex -15 KPX J adieresis -15 KPX J agrave -15 KPX J amacron -15 KPX J aogonek -15 KPX J aring -15 KPX J atilde -15 KPX J e -15 KPX J eacute -15 KPX J ecaron -15 KPX J ecircumflex -15 KPX J edieresis -15 KPX J edotaccent -15 KPX J egrave -15 KPX J emacron -15 KPX J eogonek -15 KPX J o -15 KPX J oacute -15 KPX J ocircumflex -15 KPX J odieresis -15 KPX J ograve -15 KPX J ohungarumlaut -15 KPX J omacron -15 KPX J oslash -15 KPX J otilde -15 KPX J period -20 KPX J u -15 KPX J uacute -15 KPX J ucircumflex -15 KPX J udieresis -15 KPX J ugrave -15 KPX J uhungarumlaut -15 KPX J umacron -15 KPX J uogonek -15 KPX J uring -15 KPX K O -30 KPX K Oacute -30 KPX K Ocircumflex -30 KPX K Odieresis -30 KPX K Ograve -30 KPX K Ohungarumlaut -30 KPX K Omacron -30 KPX K Oslash -30 KPX K Otilde -30 KPX K e -25 KPX K eacute -25 KPX K ecaron -25 KPX K ecircumflex -25 KPX K edieresis -25 KPX K edotaccent -25 KPX K egrave -25 KPX K emacron -25 KPX K eogonek -25 KPX K o -25 KPX K oacute -25 KPX K ocircumflex -25 KPX K odieresis -25 KPX K ograve -25 KPX K ohungarumlaut -25 KPX K omacron -25 KPX K oslash -25 KPX K otilde -25 KPX K u -15 KPX K uacute -15 KPX K ucircumflex -15 KPX K udieresis -15 KPX K ugrave -15 KPX K uhungarumlaut -15 KPX K umacron -15 KPX K uogonek -15 KPX K uring -15 KPX K y -45 KPX K yacute -45 KPX K ydieresis -45 KPX Kcommaaccent O -30 KPX Kcommaaccent Oacute -30 KPX Kcommaaccent Ocircumflex -30 KPX Kcommaaccent Odieresis -30 KPX Kcommaaccent Ograve -30 KPX Kcommaaccent Ohungarumlaut -30 KPX Kcommaaccent Omacron -30 KPX Kcommaaccent Oslash -30 KPX Kcommaaccent Otilde -30 KPX Kcommaaccent e -25 KPX Kcommaaccent eacute -25 KPX Kcommaaccent ecaron -25 KPX Kcommaaccent ecircumflex -25 KPX Kcommaaccent edieresis -25 KPX Kcommaaccent edotaccent -25 KPX Kcommaaccent egrave -25 KPX Kcommaaccent emacron -25 KPX Kcommaaccent eogonek -25 KPX Kcommaaccent o -25 KPX Kcommaaccent oacute -25 KPX Kcommaaccent ocircumflex -25 KPX Kcommaaccent odieresis -25 KPX Kcommaaccent ograve -25 KPX Kcommaaccent ohungarumlaut -25 KPX Kcommaaccent omacron -25 KPX Kcommaaccent oslash -25 KPX Kcommaaccent otilde -25 KPX Kcommaaccent u -15 KPX Kcommaaccent uacute -15 KPX Kcommaaccent ucircumflex -15 KPX Kcommaaccent udieresis -15 KPX Kcommaaccent ugrave -15 KPX Kcommaaccent uhungarumlaut -15 KPX Kcommaaccent umacron -15 KPX Kcommaaccent uogonek -15 KPX Kcommaaccent uring -15 KPX Kcommaaccent y -45 KPX Kcommaaccent yacute -45 KPX Kcommaaccent ydieresis -45 KPX L T -92 KPX L Tcaron -92 KPX L Tcommaaccent -92 KPX L V -92 KPX L W -92 KPX L Y -92 KPX L Yacute -92 KPX L Ydieresis -92 KPX L quotedblright -20 KPX L quoteright -110 KPX L y -55 KPX L yacute -55 KPX L ydieresis -55 KPX Lacute T -92 KPX Lacute Tcaron -92 KPX Lacute Tcommaaccent -92 KPX Lacute V -92 KPX Lacute W -92 KPX Lacute Y -92 KPX Lacute Yacute -92 KPX Lacute Ydieresis -92 KPX Lacute quotedblright -20 KPX Lacute quoteright -110 KPX Lacute y -55 KPX Lacute yacute -55 KPX Lacute ydieresis -55 KPX Lcommaaccent T -92 KPX Lcommaaccent Tcaron -92 KPX Lcommaaccent Tcommaaccent -92 KPX Lcommaaccent V -92 KPX Lcommaaccent W -92 KPX Lcommaaccent Y -92 KPX Lcommaaccent Yacute -92 KPX Lcommaaccent Ydieresis -92 KPX Lcommaaccent quotedblright -20 KPX Lcommaaccent quoteright -110 KPX Lcommaaccent y -55 KPX Lcommaaccent yacute -55 KPX Lcommaaccent ydieresis -55 KPX Lslash T -92 KPX Lslash Tcaron -92 KPX Lslash Tcommaaccent -92 KPX Lslash V -92 KPX Lslash W -92 KPX Lslash Y -92 KPX Lslash Yacute -92 KPX Lslash Ydieresis -92 KPX Lslash quotedblright -20 KPX Lslash quoteright -110 KPX Lslash y -55 KPX Lslash yacute -55 KPX Lslash ydieresis -55 KPX N A -20 KPX N Aacute -20 KPX N Abreve -20 KPX N Acircumflex -20 KPX N Adieresis -20 KPX N Agrave -20 KPX N Amacron -20 KPX N Aogonek -20 KPX N Aring -20 KPX N Atilde -20 KPX Nacute A -20 KPX Nacute Aacute -20 KPX Nacute Abreve -20 KPX Nacute Acircumflex -20 KPX Nacute Adieresis -20 KPX Nacute Agrave -20 KPX Nacute Amacron -20 KPX Nacute Aogonek -20 KPX Nacute Aring -20 KPX Nacute Atilde -20 KPX Ncaron A -20 KPX Ncaron Aacute -20 KPX Ncaron Abreve -20 KPX Ncaron Acircumflex -20 KPX Ncaron Adieresis -20 KPX Ncaron Agrave -20 KPX Ncaron Amacron -20 KPX Ncaron Aogonek -20 KPX Ncaron Aring -20 KPX Ncaron Atilde -20 KPX Ncommaaccent A -20 KPX Ncommaaccent Aacute -20 KPX Ncommaaccent Abreve -20 KPX Ncommaaccent Acircumflex -20 KPX Ncommaaccent Adieresis -20 KPX Ncommaaccent Agrave -20 KPX Ncommaaccent Amacron -20 KPX Ncommaaccent Aogonek -20 KPX Ncommaaccent Aring -20 KPX Ncommaaccent Atilde -20 KPX Ntilde A -20 KPX Ntilde Aacute -20 KPX Ntilde Abreve -20 KPX Ntilde Acircumflex -20 KPX Ntilde Adieresis -20 KPX Ntilde Agrave -20 KPX Ntilde Amacron -20 KPX Ntilde Aogonek -20 KPX Ntilde Aring -20 KPX Ntilde Atilde -20 KPX O A -40 KPX O Aacute -40 KPX O Abreve -40 KPX O Acircumflex -40 KPX O Adieresis -40 KPX O Agrave -40 KPX O Amacron -40 KPX O Aogonek -40 KPX O Aring -40 KPX O Atilde -40 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -50 KPX O X -40 KPX O Y -50 KPX O Yacute -50 KPX O Ydieresis -50 KPX Oacute A -40 KPX Oacute Aacute -40 KPX Oacute Abreve -40 KPX Oacute Acircumflex -40 KPX Oacute Adieresis -40 KPX Oacute Agrave -40 KPX Oacute Amacron -40 KPX Oacute Aogonek -40 KPX Oacute Aring -40 KPX Oacute Atilde -40 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -50 KPX Oacute X -40 KPX Oacute Y -50 KPX Oacute Yacute -50 KPX Oacute Ydieresis -50 KPX Ocircumflex A -40 KPX Ocircumflex Aacute -40 KPX Ocircumflex Abreve -40 KPX Ocircumflex Acircumflex -40 KPX Ocircumflex Adieresis -40 KPX Ocircumflex Agrave -40 KPX Ocircumflex Amacron -40 KPX Ocircumflex Aogonek -40 KPX Ocircumflex Aring -40 KPX Ocircumflex Atilde -40 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -50 KPX Ocircumflex X -40 KPX Ocircumflex Y -50 KPX Ocircumflex Yacute -50 KPX Ocircumflex Ydieresis -50 KPX Odieresis A -40 KPX Odieresis Aacute -40 KPX Odieresis Abreve -40 KPX Odieresis Acircumflex -40 KPX Odieresis Adieresis -40 KPX Odieresis Agrave -40 KPX Odieresis Amacron -40 KPX Odieresis Aogonek -40 KPX Odieresis Aring -40 KPX Odieresis Atilde -40 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -50 KPX Odieresis X -40 KPX Odieresis Y -50 KPX Odieresis Yacute -50 KPX Odieresis Ydieresis -50 KPX Ograve A -40 KPX Ograve Aacute -40 KPX Ograve Abreve -40 KPX Ograve Acircumflex -40 KPX Ograve Adieresis -40 KPX Ograve Agrave -40 KPX Ograve Amacron -40 KPX Ograve Aogonek -40 KPX Ograve Aring -40 KPX Ograve Atilde -40 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -50 KPX Ograve X -40 KPX Ograve Y -50 KPX Ograve Yacute -50 KPX Ograve Ydieresis -50 KPX Ohungarumlaut A -40 KPX Ohungarumlaut Aacute -40 KPX Ohungarumlaut Abreve -40 KPX Ohungarumlaut Acircumflex -40 KPX Ohungarumlaut Adieresis -40 KPX Ohungarumlaut Agrave -40 KPX Ohungarumlaut Amacron -40 KPX Ohungarumlaut Aogonek -40 KPX Ohungarumlaut Aring -40 KPX Ohungarumlaut Atilde -40 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -50 KPX Ohungarumlaut X -40 KPX Ohungarumlaut Y -50 KPX Ohungarumlaut Yacute -50 KPX Ohungarumlaut Ydieresis -50 KPX Omacron A -40 KPX Omacron Aacute -40 KPX Omacron Abreve -40 KPX Omacron Acircumflex -40 KPX Omacron Adieresis -40 KPX Omacron Agrave -40 KPX Omacron Amacron -40 KPX Omacron Aogonek -40 KPX Omacron Aring -40 KPX Omacron Atilde -40 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -50 KPX Omacron X -40 KPX Omacron Y -50 KPX Omacron Yacute -50 KPX Omacron Ydieresis -50 KPX Oslash A -40 KPX Oslash Aacute -40 KPX Oslash Abreve -40 KPX Oslash Acircumflex -40 KPX Oslash Adieresis -40 KPX Oslash Agrave -40 KPX Oslash Amacron -40 KPX Oslash Aogonek -40 KPX Oslash Aring -40 KPX Oslash Atilde -40 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -50 KPX Oslash X -40 KPX Oslash Y -50 KPX Oslash Yacute -50 KPX Oslash Ydieresis -50 KPX Otilde A -40 KPX Otilde Aacute -40 KPX Otilde Abreve -40 KPX Otilde Acircumflex -40 KPX Otilde Adieresis -40 KPX Otilde Agrave -40 KPX Otilde Amacron -40 KPX Otilde Aogonek -40 KPX Otilde Aring -40 KPX Otilde Atilde -40 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -50 KPX Otilde X -40 KPX Otilde Y -50 KPX Otilde Yacute -50 KPX Otilde Ydieresis -50 KPX P A -74 KPX P Aacute -74 KPX P Abreve -74 KPX P Acircumflex -74 KPX P Adieresis -74 KPX P Agrave -74 KPX P Amacron -74 KPX P Aogonek -74 KPX P Aring -74 KPX P Atilde -74 KPX P a -10 KPX P aacute -10 KPX P abreve -10 KPX P acircumflex -10 KPX P adieresis -10 KPX P agrave -10 KPX P amacron -10 KPX P aogonek -10 KPX P aring -10 KPX P atilde -10 KPX P comma -92 KPX P e -20 KPX P eacute -20 KPX P ecaron -20 KPX P ecircumflex -20 KPX P edieresis -20 KPX P edotaccent -20 KPX P egrave -20 KPX P emacron -20 KPX P eogonek -20 KPX P o -20 KPX P oacute -20 KPX P ocircumflex -20 KPX P odieresis -20 KPX P ograve -20 KPX P ohungarumlaut -20 KPX P omacron -20 KPX P oslash -20 KPX P otilde -20 KPX P period -110 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX Q period -20 KPX R O -30 KPX R Oacute -30 KPX R Ocircumflex -30 KPX R Odieresis -30 KPX R Ograve -30 KPX R Ohungarumlaut -30 KPX R Omacron -30 KPX R Oslash -30 KPX R Otilde -30 KPX R T -40 KPX R Tcaron -40 KPX R Tcommaaccent -40 KPX R U -30 KPX R Uacute -30 KPX R Ucircumflex -30 KPX R Udieresis -30 KPX R Ugrave -30 KPX R Uhungarumlaut -30 KPX R Umacron -30 KPX R Uogonek -30 KPX R Uring -30 KPX R V -55 KPX R W -35 KPX R Y -35 KPX R Yacute -35 KPX R Ydieresis -35 KPX Racute O -30 KPX Racute Oacute -30 KPX Racute Ocircumflex -30 KPX Racute Odieresis -30 KPX Racute Ograve -30 KPX Racute Ohungarumlaut -30 KPX Racute Omacron -30 KPX Racute Oslash -30 KPX Racute Otilde -30 KPX Racute T -40 KPX Racute Tcaron -40 KPX Racute Tcommaaccent -40 KPX Racute U -30 KPX Racute Uacute -30 KPX Racute Ucircumflex -30 KPX Racute Udieresis -30 KPX Racute Ugrave -30 KPX Racute Uhungarumlaut -30 KPX Racute Umacron -30 KPX Racute Uogonek -30 KPX Racute Uring -30 KPX Racute V -55 KPX Racute W -35 KPX Racute Y -35 KPX Racute Yacute -35 KPX Racute Ydieresis -35 KPX Rcaron O -30 KPX Rcaron Oacute -30 KPX Rcaron Ocircumflex -30 KPX Rcaron Odieresis -30 KPX Rcaron Ograve -30 KPX Rcaron Ohungarumlaut -30 KPX Rcaron Omacron -30 KPX Rcaron Oslash -30 KPX Rcaron Otilde -30 KPX Rcaron T -40 KPX Rcaron Tcaron -40 KPX Rcaron Tcommaaccent -40 KPX Rcaron U -30 KPX Rcaron Uacute -30 KPX Rcaron Ucircumflex -30 KPX Rcaron Udieresis -30 KPX Rcaron Ugrave -30 KPX Rcaron Uhungarumlaut -30 KPX Rcaron Umacron -30 KPX Rcaron Uogonek -30 KPX Rcaron Uring -30 KPX Rcaron V -55 KPX Rcaron W -35 KPX Rcaron Y -35 KPX Rcaron Yacute -35 KPX Rcaron Ydieresis -35 KPX Rcommaaccent O -30 KPX Rcommaaccent Oacute -30 KPX Rcommaaccent Ocircumflex -30 KPX Rcommaaccent Odieresis -30 KPX Rcommaaccent Ograve -30 KPX Rcommaaccent Ohungarumlaut -30 KPX Rcommaaccent Omacron -30 KPX Rcommaaccent Oslash -30 KPX Rcommaaccent Otilde -30 KPX Rcommaaccent T -40 KPX Rcommaaccent Tcaron -40 KPX Rcommaaccent Tcommaaccent -40 KPX Rcommaaccent U -30 KPX Rcommaaccent Uacute -30 KPX Rcommaaccent Ucircumflex -30 KPX Rcommaaccent Udieresis -30 KPX Rcommaaccent Ugrave -30 KPX Rcommaaccent Uhungarumlaut -30 KPX Rcommaaccent Umacron -30 KPX Rcommaaccent Uogonek -30 KPX Rcommaaccent Uring -30 KPX Rcommaaccent V -55 KPX Rcommaaccent W -35 KPX Rcommaaccent Y -35 KPX Rcommaaccent Yacute -35 KPX Rcommaaccent Ydieresis -35 KPX T A -90 KPX T Aacute -90 KPX T Abreve -90 KPX T Acircumflex -90 KPX T Adieresis -90 KPX T Agrave -90 KPX T Amacron -90 KPX T Aogonek -90 KPX T Aring -90 KPX T Atilde -90 KPX T O -18 KPX T Oacute -18 KPX T Ocircumflex -18 KPX T Odieresis -18 KPX T Ograve -18 KPX T Ohungarumlaut -18 KPX T Omacron -18 KPX T Oslash -18 KPX T Otilde -18 KPX T a -92 KPX T aacute -92 KPX T abreve -52 KPX T acircumflex -52 KPX T adieresis -52 KPX T agrave -52 KPX T amacron -52 KPX T aogonek -92 KPX T aring -92 KPX T atilde -52 KPX T colon -74 KPX T comma -74 KPX T e -92 KPX T eacute -92 KPX T ecaron -92 KPX T ecircumflex -92 KPX T edieresis -52 KPX T edotaccent -92 KPX T egrave -52 KPX T emacron -52 KPX T eogonek -92 KPX T hyphen -92 KPX T i -18 KPX T iacute -18 KPX T iogonek -18 KPX T o -92 KPX T oacute -92 KPX T ocircumflex -92 KPX T odieresis -92 KPX T ograve -92 KPX T ohungarumlaut -92 KPX T omacron -92 KPX T oslash -92 KPX T otilde -92 KPX T period -90 KPX T r -74 KPX T racute -74 KPX T rcaron -74 KPX T rcommaaccent -74 KPX T semicolon -74 KPX T u -92 KPX T uacute -92 KPX T ucircumflex -92 KPX T udieresis -92 KPX T ugrave -92 KPX T uhungarumlaut -92 KPX T umacron -92 KPX T uogonek -92 KPX T uring -92 KPX T w -74 KPX T y -34 KPX T yacute -34 KPX T ydieresis -34 KPX Tcaron A -90 KPX Tcaron Aacute -90 KPX Tcaron Abreve -90 KPX Tcaron Acircumflex -90 KPX Tcaron Adieresis -90 KPX Tcaron Agrave -90 KPX Tcaron Amacron -90 KPX Tcaron Aogonek -90 KPX Tcaron Aring -90 KPX Tcaron Atilde -90 KPX Tcaron O -18 KPX Tcaron Oacute -18 KPX Tcaron Ocircumflex -18 KPX Tcaron Odieresis -18 KPX Tcaron Ograve -18 KPX Tcaron Ohungarumlaut -18 KPX Tcaron Omacron -18 KPX Tcaron Oslash -18 KPX Tcaron Otilde -18 KPX Tcaron a -92 KPX Tcaron aacute -92 KPX Tcaron abreve -52 KPX Tcaron acircumflex -52 KPX Tcaron adieresis -52 KPX Tcaron agrave -52 KPX Tcaron amacron -52 KPX Tcaron aogonek -92 KPX Tcaron aring -92 KPX Tcaron atilde -52 KPX Tcaron colon -74 KPX Tcaron comma -74 KPX Tcaron e -92 KPX Tcaron eacute -92 KPX Tcaron ecaron -92 KPX Tcaron ecircumflex -92 KPX Tcaron edieresis -52 KPX Tcaron edotaccent -92 KPX Tcaron egrave -52 KPX Tcaron emacron -52 KPX Tcaron eogonek -92 KPX Tcaron hyphen -92 KPX Tcaron i -18 KPX Tcaron iacute -18 KPX Tcaron iogonek -18 KPX Tcaron o -92 KPX Tcaron oacute -92 KPX Tcaron ocircumflex -92 KPX Tcaron odieresis -92 KPX Tcaron ograve -92 KPX Tcaron ohungarumlaut -92 KPX Tcaron omacron -92 KPX Tcaron oslash -92 KPX Tcaron otilde -92 KPX Tcaron period -90 KPX Tcaron r -74 KPX Tcaron racute -74 KPX Tcaron rcaron -74 KPX Tcaron rcommaaccent -74 KPX Tcaron semicolon -74 KPX Tcaron u -92 KPX Tcaron uacute -92 KPX Tcaron ucircumflex -92 KPX Tcaron udieresis -92 KPX Tcaron ugrave -92 KPX Tcaron uhungarumlaut -92 KPX Tcaron umacron -92 KPX Tcaron uogonek -92 KPX Tcaron uring -92 KPX Tcaron w -74 KPX Tcaron y -34 KPX Tcaron yacute -34 KPX Tcaron ydieresis -34 KPX Tcommaaccent A -90 KPX Tcommaaccent Aacute -90 KPX Tcommaaccent Abreve -90 KPX Tcommaaccent Acircumflex -90 KPX Tcommaaccent Adieresis -90 KPX Tcommaaccent Agrave -90 KPX Tcommaaccent Amacron -90 KPX Tcommaaccent Aogonek -90 KPX Tcommaaccent Aring -90 KPX Tcommaaccent Atilde -90 KPX Tcommaaccent O -18 KPX Tcommaaccent Oacute -18 KPX Tcommaaccent Ocircumflex -18 KPX Tcommaaccent Odieresis -18 KPX Tcommaaccent Ograve -18 KPX Tcommaaccent Ohungarumlaut -18 KPX Tcommaaccent Omacron -18 KPX Tcommaaccent Oslash -18 KPX Tcommaaccent Otilde -18 KPX Tcommaaccent a -92 KPX Tcommaaccent aacute -92 KPX Tcommaaccent abreve -52 KPX Tcommaaccent acircumflex -52 KPX Tcommaaccent adieresis -52 KPX Tcommaaccent agrave -52 KPX Tcommaaccent amacron -52 KPX Tcommaaccent aogonek -92 KPX Tcommaaccent aring -92 KPX Tcommaaccent atilde -52 KPX Tcommaaccent colon -74 KPX Tcommaaccent comma -74 KPX Tcommaaccent e -92 KPX Tcommaaccent eacute -92 KPX Tcommaaccent ecaron -92 KPX Tcommaaccent ecircumflex -92 KPX Tcommaaccent edieresis -52 KPX Tcommaaccent edotaccent -92 KPX Tcommaaccent egrave -52 KPX Tcommaaccent emacron -52 KPX Tcommaaccent eogonek -92 KPX Tcommaaccent hyphen -92 KPX Tcommaaccent i -18 KPX Tcommaaccent iacute -18 KPX Tcommaaccent iogonek -18 KPX Tcommaaccent o -92 KPX Tcommaaccent oacute -92 KPX Tcommaaccent ocircumflex -92 KPX Tcommaaccent odieresis -92 KPX Tcommaaccent ograve -92 KPX Tcommaaccent ohungarumlaut -92 KPX Tcommaaccent omacron -92 KPX Tcommaaccent oslash -92 KPX Tcommaaccent otilde -92 KPX Tcommaaccent period -90 KPX Tcommaaccent r -74 KPX Tcommaaccent racute -74 KPX Tcommaaccent rcaron -74 KPX Tcommaaccent rcommaaccent -74 KPX Tcommaaccent semicolon -74 KPX Tcommaaccent u -92 KPX Tcommaaccent uacute -92 KPX Tcommaaccent ucircumflex -92 KPX Tcommaaccent udieresis -92 KPX Tcommaaccent ugrave -92 KPX Tcommaaccent uhungarumlaut -92 KPX Tcommaaccent umacron -92 KPX Tcommaaccent uogonek -92 KPX Tcommaaccent uring -92 KPX Tcommaaccent w -74 KPX Tcommaaccent y -34 KPX Tcommaaccent yacute -34 KPX Tcommaaccent ydieresis -34 KPX U A -60 KPX U Aacute -60 KPX U Abreve -60 KPX U Acircumflex -60 KPX U Adieresis -60 KPX U Agrave -60 KPX U Amacron -60 KPX U Aogonek -60 KPX U Aring -60 KPX U Atilde -60 KPX U comma -50 KPX U period -50 KPX Uacute A -60 KPX Uacute Aacute -60 KPX Uacute Abreve -60 KPX Uacute Acircumflex -60 KPX Uacute Adieresis -60 KPX Uacute Agrave -60 KPX Uacute Amacron -60 KPX Uacute Aogonek -60 KPX Uacute Aring -60 KPX Uacute Atilde -60 KPX Uacute comma -50 KPX Uacute period -50 KPX Ucircumflex A -60 KPX Ucircumflex Aacute -60 KPX Ucircumflex Abreve -60 KPX Ucircumflex Acircumflex -60 KPX Ucircumflex Adieresis -60 KPX Ucircumflex Agrave -60 KPX Ucircumflex Amacron -60 KPX Ucircumflex Aogonek -60 KPX Ucircumflex Aring -60 KPX Ucircumflex Atilde -60 KPX Ucircumflex comma -50 KPX Ucircumflex period -50 KPX Udieresis A -60 KPX Udieresis Aacute -60 KPX Udieresis Abreve -60 KPX Udieresis Acircumflex -60 KPX Udieresis Adieresis -60 KPX Udieresis Agrave -60 KPX Udieresis Amacron -60 KPX Udieresis Aogonek -60 KPX Udieresis Aring -60 KPX Udieresis Atilde -60 KPX Udieresis comma -50 KPX Udieresis period -50 KPX Ugrave A -60 KPX Ugrave Aacute -60 KPX Ugrave Abreve -60 KPX Ugrave Acircumflex -60 KPX Ugrave Adieresis -60 KPX Ugrave Agrave -60 KPX Ugrave Amacron -60 KPX Ugrave Aogonek -60 KPX Ugrave Aring -60 KPX Ugrave Atilde -60 KPX Ugrave comma -50 KPX Ugrave period -50 KPX Uhungarumlaut A -60 KPX Uhungarumlaut Aacute -60 KPX Uhungarumlaut Abreve -60 KPX Uhungarumlaut Acircumflex -60 KPX Uhungarumlaut Adieresis -60 KPX Uhungarumlaut Agrave -60 KPX Uhungarumlaut Amacron -60 KPX Uhungarumlaut Aogonek -60 KPX Uhungarumlaut Aring -60 KPX Uhungarumlaut Atilde -60 KPX Uhungarumlaut comma -50 KPX Uhungarumlaut period -50 KPX Umacron A -60 KPX Umacron Aacute -60 KPX Umacron Abreve -60 KPX Umacron Acircumflex -60 KPX Umacron Adieresis -60 KPX Umacron Agrave -60 KPX Umacron Amacron -60 KPX Umacron Aogonek -60 KPX Umacron Aring -60 KPX Umacron Atilde -60 KPX Umacron comma -50 KPX Umacron period -50 KPX Uogonek A -60 KPX Uogonek Aacute -60 KPX Uogonek Abreve -60 KPX Uogonek Acircumflex -60 KPX Uogonek Adieresis -60 KPX Uogonek Agrave -60 KPX Uogonek Amacron -60 KPX Uogonek Aogonek -60 KPX Uogonek Aring -60 KPX Uogonek Atilde -60 KPX Uogonek comma -50 KPX Uogonek period -50 KPX Uring A -60 KPX Uring Aacute -60 KPX Uring Abreve -60 KPX Uring Acircumflex -60 KPX Uring Adieresis -60 KPX Uring Agrave -60 KPX Uring Amacron -60 KPX Uring Aogonek -60 KPX Uring Aring -60 KPX Uring Atilde -60 KPX Uring comma -50 KPX Uring period -50 KPX V A -135 KPX V Aacute -135 KPX V Abreve -135 KPX V Acircumflex -135 KPX V Adieresis -135 KPX V Agrave -135 KPX V Amacron -135 KPX V Aogonek -135 KPX V Aring -135 KPX V Atilde -135 KPX V G -30 KPX V Gbreve -30 KPX V Gcommaaccent -30 KPX V O -45 KPX V Oacute -45 KPX V Ocircumflex -45 KPX V Odieresis -45 KPX V Ograve -45 KPX V Ohungarumlaut -45 KPX V Omacron -45 KPX V Oslash -45 KPX V Otilde -45 KPX V a -92 KPX V aacute -92 KPX V abreve -92 KPX V acircumflex -92 KPX V adieresis -92 KPX V agrave -92 KPX V amacron -92 KPX V aogonek -92 KPX V aring -92 KPX V atilde -92 KPX V colon -92 KPX V comma -129 KPX V e -100 KPX V eacute -100 KPX V ecaron -100 KPX V ecircumflex -100 KPX V edieresis -100 KPX V edotaccent -100 KPX V egrave -100 KPX V emacron -100 KPX V eogonek -100 KPX V hyphen -74 KPX V i -37 KPX V iacute -37 KPX V icircumflex -37 KPX V idieresis -37 KPX V igrave -37 KPX V imacron -37 KPX V iogonek -37 KPX V o -100 KPX V oacute -100 KPX V ocircumflex -100 KPX V odieresis -100 KPX V ograve -100 KPX V ohungarumlaut -100 KPX V omacron -100 KPX V oslash -100 KPX V otilde -100 KPX V period -145 KPX V semicolon -92 KPX V u -92 KPX V uacute -92 KPX V ucircumflex -92 KPX V udieresis -92 KPX V ugrave -92 KPX V uhungarumlaut -92 KPX V umacron -92 KPX V uogonek -92 KPX V uring -92 KPX W A -120 KPX W Aacute -120 KPX W Abreve -120 KPX W Acircumflex -120 KPX W Adieresis -120 KPX W Agrave -120 KPX W Amacron -120 KPX W Aogonek -120 KPX W Aring -120 KPX W Atilde -120 KPX W O -10 KPX W Oacute -10 KPX W Ocircumflex -10 KPX W Odieresis -10 KPX W Ograve -10 KPX W Ohungarumlaut -10 KPX W Omacron -10 KPX W Oslash -10 KPX W Otilde -10 KPX W a -65 KPX W aacute -65 KPX W abreve -65 KPX W acircumflex -65 KPX W adieresis -65 KPX W agrave -65 KPX W amacron -65 KPX W aogonek -65 KPX W aring -65 KPX W atilde -65 KPX W colon -55 KPX W comma -92 KPX W e -65 KPX W eacute -65 KPX W ecaron -65 KPX W ecircumflex -65 KPX W edieresis -65 KPX W edotaccent -65 KPX W egrave -65 KPX W emacron -65 KPX W eogonek -65 KPX W hyphen -37 KPX W i -18 KPX W iacute -18 KPX W iogonek -18 KPX W o -75 KPX W oacute -75 KPX W ocircumflex -75 KPX W odieresis -75 KPX W ograve -75 KPX W ohungarumlaut -75 KPX W omacron -75 KPX W oslash -75 KPX W otilde -75 KPX W period -92 KPX W semicolon -55 KPX W u -50 KPX W uacute -50 KPX W ucircumflex -50 KPX W udieresis -50 KPX W ugrave -50 KPX W uhungarumlaut -50 KPX W umacron -50 KPX W uogonek -50 KPX W uring -50 KPX W y -60 KPX W yacute -60 KPX W ydieresis -60 KPX Y A -110 KPX Y Aacute -110 KPX Y Abreve -110 KPX Y Acircumflex -110 KPX Y Adieresis -110 KPX Y Agrave -110 KPX Y Amacron -110 KPX Y Aogonek -110 KPX Y Aring -110 KPX Y Atilde -110 KPX Y O -35 KPX Y Oacute -35 KPX Y Ocircumflex -35 KPX Y Odieresis -35 KPX Y Ograve -35 KPX Y Ohungarumlaut -35 KPX Y Omacron -35 KPX Y Oslash -35 KPX Y Otilde -35 KPX Y a -85 KPX Y aacute -85 KPX Y abreve -85 KPX Y acircumflex -85 KPX Y adieresis -85 KPX Y agrave -85 KPX Y amacron -85 KPX Y aogonek -85 KPX Y aring -85 KPX Y atilde -85 KPX Y colon -92 KPX Y comma -92 KPX Y e -111 KPX Y eacute -111 KPX Y ecaron -111 KPX Y ecircumflex -111 KPX Y edieresis -71 KPX Y edotaccent -111 KPX Y egrave -71 KPX Y emacron -71 KPX Y eogonek -111 KPX Y hyphen -92 KPX Y i -37 KPX Y iacute -37 KPX Y iogonek -37 KPX Y o -111 KPX Y oacute -111 KPX Y ocircumflex -111 KPX Y odieresis -111 KPX Y ograve -111 KPX Y ohungarumlaut -111 KPX Y omacron -111 KPX Y oslash -111 KPX Y otilde -111 KPX Y period -92 KPX Y semicolon -92 KPX Y u -92 KPX Y uacute -92 KPX Y ucircumflex -92 KPX Y udieresis -92 KPX Y ugrave -92 KPX Y uhungarumlaut -92 KPX Y umacron -92 KPX Y uogonek -92 KPX Y uring -92 KPX Yacute A -110 KPX Yacute Aacute -110 KPX Yacute Abreve -110 KPX Yacute Acircumflex -110 KPX Yacute Adieresis -110 KPX Yacute Agrave -110 KPX Yacute Amacron -110 KPX Yacute Aogonek -110 KPX Yacute Aring -110 KPX Yacute Atilde -110 KPX Yacute O -35 KPX Yacute Oacute -35 KPX Yacute Ocircumflex -35 KPX Yacute Odieresis -35 KPX Yacute Ograve -35 KPX Yacute Ohungarumlaut -35 KPX Yacute Omacron -35 KPX Yacute Oslash -35 KPX Yacute Otilde -35 KPX Yacute a -85 KPX Yacute aacute -85 KPX Yacute abreve -85 KPX Yacute acircumflex -85 KPX Yacute adieresis -85 KPX Yacute agrave -85 KPX Yacute amacron -85 KPX Yacute aogonek -85 KPX Yacute aring -85 KPX Yacute atilde -85 KPX Yacute colon -92 KPX Yacute comma -92 KPX Yacute e -111 KPX Yacute eacute -111 KPX Yacute ecaron -111 KPX Yacute ecircumflex -111 KPX Yacute edieresis -71 KPX Yacute edotaccent -111 KPX Yacute egrave -71 KPX Yacute emacron -71 KPX Yacute eogonek -111 KPX Yacute hyphen -92 KPX Yacute i -37 KPX Yacute iacute -37 KPX Yacute iogonek -37 KPX Yacute o -111 KPX Yacute oacute -111 KPX Yacute ocircumflex -111 KPX Yacute odieresis -111 KPX Yacute ograve -111 KPX Yacute ohungarumlaut -111 KPX Yacute omacron -111 KPX Yacute oslash -111 KPX Yacute otilde -111 KPX Yacute period -92 KPX Yacute semicolon -92 KPX Yacute u -92 KPX Yacute uacute -92 KPX Yacute ucircumflex -92 KPX Yacute udieresis -92 KPX Yacute ugrave -92 KPX Yacute uhungarumlaut -92 KPX Yacute umacron -92 KPX Yacute uogonek -92 KPX Yacute uring -92 KPX Ydieresis A -110 KPX Ydieresis Aacute -110 KPX Ydieresis Abreve -110 KPX Ydieresis Acircumflex -110 KPX Ydieresis Adieresis -110 KPX Ydieresis Agrave -110 KPX Ydieresis Amacron -110 KPX Ydieresis Aogonek -110 KPX Ydieresis Aring -110 KPX Ydieresis Atilde -110 KPX Ydieresis O -35 KPX Ydieresis Oacute -35 KPX Ydieresis Ocircumflex -35 KPX Ydieresis Odieresis -35 KPX Ydieresis Ograve -35 KPX Ydieresis Ohungarumlaut -35 KPX Ydieresis Omacron -35 KPX Ydieresis Oslash -35 KPX Ydieresis Otilde -35 KPX Ydieresis a -85 KPX Ydieresis aacute -85 KPX Ydieresis abreve -85 KPX Ydieresis acircumflex -85 KPX Ydieresis adieresis -85 KPX Ydieresis agrave -85 KPX Ydieresis amacron -85 KPX Ydieresis aogonek -85 KPX Ydieresis aring -85 KPX Ydieresis atilde -85 KPX Ydieresis colon -92 KPX Ydieresis comma -92 KPX Ydieresis e -111 KPX Ydieresis eacute -111 KPX Ydieresis ecaron -111 KPX Ydieresis ecircumflex -111 KPX Ydieresis edieresis -71 KPX Ydieresis edotaccent -111 KPX Ydieresis egrave -71 KPX Ydieresis emacron -71 KPX Ydieresis eogonek -111 KPX Ydieresis hyphen -92 KPX Ydieresis i -37 KPX Ydieresis iacute -37 KPX Ydieresis iogonek -37 KPX Ydieresis o -111 KPX Ydieresis oacute -111 KPX Ydieresis ocircumflex -111 KPX Ydieresis odieresis -111 KPX Ydieresis ograve -111 KPX Ydieresis ohungarumlaut -111 KPX Ydieresis omacron -111 KPX Ydieresis oslash -111 KPX Ydieresis otilde -111 KPX Ydieresis period -92 KPX Ydieresis semicolon -92 KPX Ydieresis u -92 KPX Ydieresis uacute -92 KPX Ydieresis ucircumflex -92 KPX Ydieresis udieresis -92 KPX Ydieresis ugrave -92 KPX Ydieresis uhungarumlaut -92 KPX Ydieresis umacron -92 KPX Ydieresis uogonek -92 KPX Ydieresis uring -92 KPX a v -25 KPX aacute v -25 KPX abreve v -25 KPX acircumflex v -25 KPX adieresis v -25 KPX agrave v -25 KPX amacron v -25 KPX aogonek v -25 KPX aring v -25 KPX atilde v -25 KPX b b -10 KPX b period -40 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX b v -15 KPX comma quotedblright -45 KPX comma quoteright -55 KPX d w -15 KPX dcroat w -15 KPX e v -15 KPX eacute v -15 KPX ecaron v -15 KPX ecircumflex v -15 KPX edieresis v -15 KPX edotaccent v -15 KPX egrave v -15 KPX emacron v -15 KPX eogonek v -15 KPX f comma -15 KPX f dotlessi -35 KPX f i -25 KPX f o -25 KPX f oacute -25 KPX f ocircumflex -25 KPX f odieresis -25 KPX f ograve -25 KPX f ohungarumlaut -25 KPX f omacron -25 KPX f oslash -25 KPX f otilde -25 KPX f period -15 KPX f quotedblright 50 KPX f quoteright 55 KPX g period -15 KPX gbreve period -15 KPX gcommaaccent period -15 KPX h y -15 KPX h yacute -15 KPX h ydieresis -15 KPX i v -10 KPX iacute v -10 KPX icircumflex v -10 KPX idieresis v -10 KPX igrave v -10 KPX imacron v -10 KPX iogonek v -10 KPX k e -10 KPX k eacute -10 KPX k ecaron -10 KPX k ecircumflex -10 KPX k edieresis -10 KPX k edotaccent -10 KPX k egrave -10 KPX k emacron -10 KPX k eogonek -10 KPX k o -15 KPX k oacute -15 KPX k ocircumflex -15 KPX k odieresis -15 KPX k ograve -15 KPX k ohungarumlaut -15 KPX k omacron -15 KPX k oslash -15 KPX k otilde -15 KPX k y -15 KPX k yacute -15 KPX k ydieresis -15 KPX kcommaaccent e -10 KPX kcommaaccent eacute -10 KPX kcommaaccent ecaron -10 KPX kcommaaccent ecircumflex -10 KPX kcommaaccent edieresis -10 KPX kcommaaccent edotaccent -10 KPX kcommaaccent egrave -10 KPX kcommaaccent emacron -10 KPX kcommaaccent eogonek -10 KPX kcommaaccent o -15 KPX kcommaaccent oacute -15 KPX kcommaaccent ocircumflex -15 KPX kcommaaccent odieresis -15 KPX kcommaaccent ograve -15 KPX kcommaaccent ohungarumlaut -15 KPX kcommaaccent omacron -15 KPX kcommaaccent oslash -15 KPX kcommaaccent otilde -15 KPX kcommaaccent y -15 KPX kcommaaccent yacute -15 KPX kcommaaccent ydieresis -15 KPX n v -40 KPX nacute v -40 KPX ncaron v -40 KPX ncommaaccent v -40 KPX ntilde v -40 KPX o v -10 KPX o w -10 KPX oacute v -10 KPX oacute w -10 KPX ocircumflex v -10 KPX ocircumflex w -10 KPX odieresis v -10 KPX odieresis w -10 KPX ograve v -10 KPX ograve w -10 KPX ohungarumlaut v -10 KPX ohungarumlaut w -10 KPX omacron v -10 KPX omacron w -10 KPX oslash v -10 KPX oslash w -10 KPX otilde v -10 KPX otilde w -10 KPX period quotedblright -55 KPX period quoteright -55 KPX quotedblleft A -10 KPX quotedblleft Aacute -10 KPX quotedblleft Abreve -10 KPX quotedblleft Acircumflex -10 KPX quotedblleft Adieresis -10 KPX quotedblleft Agrave -10 KPX quotedblleft Amacron -10 KPX quotedblleft Aogonek -10 KPX quotedblleft Aring -10 KPX quotedblleft Atilde -10 KPX quoteleft A -10 KPX quoteleft Aacute -10 KPX quoteleft Abreve -10 KPX quoteleft Acircumflex -10 KPX quoteleft Adieresis -10 KPX quoteleft Agrave -10 KPX quoteleft Amacron -10 KPX quoteleft Aogonek -10 KPX quoteleft Aring -10 KPX quoteleft Atilde -10 KPX quoteleft quoteleft -63 KPX quoteright d -20 KPX quoteright dcroat -20 KPX quoteright quoteright -63 KPX quoteright r -20 KPX quoteright racute -20 KPX quoteright rcaron -20 KPX quoteright rcommaaccent -20 KPX quoteright s -37 KPX quoteright sacute -37 KPX quoteright scaron -37 KPX quoteright scedilla -37 KPX quoteright scommaaccent -37 KPX quoteright space -74 KPX quoteright v -20 KPX r c -18 KPX r cacute -18 KPX r ccaron -18 KPX r ccedilla -18 KPX r comma -92 KPX r e -18 KPX r eacute -18 KPX r ecaron -18 KPX r ecircumflex -18 KPX r edieresis -18 KPX r edotaccent -18 KPX r egrave -18 KPX r emacron -18 KPX r eogonek -18 KPX r g -10 KPX r gbreve -10 KPX r gcommaaccent -10 KPX r hyphen -37 KPX r n -15 KPX r nacute -15 KPX r ncaron -15 KPX r ncommaaccent -15 KPX r ntilde -15 KPX r o -18 KPX r oacute -18 KPX r ocircumflex -18 KPX r odieresis -18 KPX r ograve -18 KPX r ohungarumlaut -18 KPX r omacron -18 KPX r oslash -18 KPX r otilde -18 KPX r p -10 KPX r period -100 KPX r q -18 KPX r v -10 KPX racute c -18 KPX racute cacute -18 KPX racute ccaron -18 KPX racute ccedilla -18 KPX racute comma -92 KPX racute e -18 KPX racute eacute -18 KPX racute ecaron -18 KPX racute ecircumflex -18 KPX racute edieresis -18 KPX racute edotaccent -18 KPX racute egrave -18 KPX racute emacron -18 KPX racute eogonek -18 KPX racute g -10 KPX racute gbreve -10 KPX racute gcommaaccent -10 KPX racute hyphen -37 KPX racute n -15 KPX racute nacute -15 KPX racute ncaron -15 KPX racute ncommaaccent -15 KPX racute ntilde -15 KPX racute o -18 KPX racute oacute -18 KPX racute ocircumflex -18 KPX racute odieresis -18 KPX racute ograve -18 KPX racute ohungarumlaut -18 KPX racute omacron -18 KPX racute oslash -18 KPX racute otilde -18 KPX racute p -10 KPX racute period -100 KPX racute q -18 KPX racute v -10 KPX rcaron c -18 KPX rcaron cacute -18 KPX rcaron ccaron -18 KPX rcaron ccedilla -18 KPX rcaron comma -92 KPX rcaron e -18 KPX rcaron eacute -18 KPX rcaron ecaron -18 KPX rcaron ecircumflex -18 KPX rcaron edieresis -18 KPX rcaron edotaccent -18 KPX rcaron egrave -18 KPX rcaron emacron -18 KPX rcaron eogonek -18 KPX rcaron g -10 KPX rcaron gbreve -10 KPX rcaron gcommaaccent -10 KPX rcaron hyphen -37 KPX rcaron n -15 KPX rcaron nacute -15 KPX rcaron ncaron -15 KPX rcaron ncommaaccent -15 KPX rcaron ntilde -15 KPX rcaron o -18 KPX rcaron oacute -18 KPX rcaron ocircumflex -18 KPX rcaron odieresis -18 KPX rcaron ograve -18 KPX rcaron ohungarumlaut -18 KPX rcaron omacron -18 KPX rcaron oslash -18 KPX rcaron otilde -18 KPX rcaron p -10 KPX rcaron period -100 KPX rcaron q -18 KPX rcaron v -10 KPX rcommaaccent c -18 KPX rcommaaccent cacute -18 KPX rcommaaccent ccaron -18 KPX rcommaaccent ccedilla -18 KPX rcommaaccent comma -92 KPX rcommaaccent e -18 KPX rcommaaccent eacute -18 KPX rcommaaccent ecaron -18 KPX rcommaaccent ecircumflex -18 KPX rcommaaccent edieresis -18 KPX rcommaaccent edotaccent -18 KPX rcommaaccent egrave -18 KPX rcommaaccent emacron -18 KPX rcommaaccent eogonek -18 KPX rcommaaccent g -10 KPX rcommaaccent gbreve -10 KPX rcommaaccent gcommaaccent -10 KPX rcommaaccent hyphen -37 KPX rcommaaccent n -15 KPX rcommaaccent nacute -15 KPX rcommaaccent ncaron -15 KPX rcommaaccent ncommaaccent -15 KPX rcommaaccent ntilde -15 KPX rcommaaccent o -18 KPX rcommaaccent oacute -18 KPX rcommaaccent ocircumflex -18 KPX rcommaaccent odieresis -18 KPX rcommaaccent ograve -18 KPX rcommaaccent ohungarumlaut -18 KPX rcommaaccent omacron -18 KPX rcommaaccent oslash -18 KPX rcommaaccent otilde -18 KPX rcommaaccent p -10 KPX rcommaaccent period -100 KPX rcommaaccent q -18 KPX rcommaaccent v -10 KPX space A -55 KPX space Aacute -55 KPX space Abreve -55 KPX space Acircumflex -55 KPX space Adieresis -55 KPX space Agrave -55 KPX space Amacron -55 KPX space Aogonek -55 KPX space Aring -55 KPX space Atilde -55 KPX space T -30 KPX space Tcaron -30 KPX space Tcommaaccent -30 KPX space V -45 KPX space W -30 KPX space Y -55 KPX space Yacute -55 KPX space Ydieresis -55 KPX v a -10 KPX v aacute -10 KPX v abreve -10 KPX v acircumflex -10 KPX v adieresis -10 KPX v agrave -10 KPX v amacron -10 KPX v aogonek -10 KPX v aring -10 KPX v atilde -10 KPX v comma -55 KPX v e -10 KPX v eacute -10 KPX v ecaron -10 KPX v ecircumflex -10 KPX v edieresis -10 KPX v edotaccent -10 KPX v egrave -10 KPX v emacron -10 KPX v eogonek -10 KPX v o -10 KPX v oacute -10 KPX v ocircumflex -10 KPX v odieresis -10 KPX v ograve -10 KPX v ohungarumlaut -10 KPX v omacron -10 KPX v oslash -10 KPX v otilde -10 KPX v period -70 KPX w comma -55 KPX w o -10 KPX w oacute -10 KPX w ocircumflex -10 KPX w odieresis -10 KPX w ograve -10 KPX w ohungarumlaut -10 KPX w omacron -10 KPX w oslash -10 KPX w otilde -10 KPX w period -70 KPX y comma -55 KPX y e -10 KPX y eacute -10 KPX y ecaron -10 KPX y ecircumflex -10 KPX y edieresis -10 KPX y edotaccent -10 KPX y egrave -10 KPX y emacron -10 KPX y eogonek -10 KPX y o -25 KPX y oacute -25 KPX y ocircumflex -25 KPX y odieresis -25 KPX y ograve -25 KPX y ohungarumlaut -25 KPX y omacron -25 KPX y oslash -25 KPX y otilde -25 KPX y period -70 KPX yacute comma -55 KPX yacute e -10 KPX yacute eacute -10 KPX yacute ecaron -10 KPX yacute ecircumflex -10 KPX yacute edieresis -10 KPX yacute edotaccent -10 KPX yacute egrave -10 KPX yacute emacron -10 KPX yacute eogonek -10 KPX yacute o -25 KPX yacute oacute -25 KPX yacute ocircumflex -25 KPX yacute odieresis -25 KPX yacute ograve -25 KPX yacute ohungarumlaut -25 KPX yacute omacron -25 KPX yacute oslash -25 KPX yacute otilde -25 KPX yacute period -70 KPX ydieresis comma -55 KPX ydieresis e -10 KPX ydieresis eacute -10 KPX ydieresis ecaron -10 KPX ydieresis ecircumflex -10 KPX ydieresis edieresis -10 KPX ydieresis edotaccent -10 KPX ydieresis egrave -10 KPX ydieresis emacron -10 KPX ydieresis eogonek -10 KPX ydieresis o -25 KPX ydieresis oacute -25 KPX ydieresis ocircumflex -25 KPX ydieresis odieresis -25 KPX ydieresis ograve -25 KPX ydieresis ohungarumlaut -25 KPX ydieresis omacron -25 KPX ydieresis oslash -25 KPX ydieresis otilde -25 KPX ydieresis period -70 EndKernPairs EndKernData EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Times-BoldItalic.afm000066400000000000000000001711121320103431700303130ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 13:04:06 1997 Comment UniqueID 43066 Comment VMusage 45874 56899 FontName Times-BoldItalic FullName Times Bold Italic FamilyName Times Weight Bold ItalicAngle -15 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -200 -218 996 921 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme AdobeStandardEncoding CapHeight 669 XHeight 462 Ascender 683 Descender -217 StdHW 42 StdVW 121 StartCharMetrics 315 C 32 ; WX 250 ; N space ; B 0 0 0 0 ; C 33 ; WX 389 ; N exclam ; B 67 -13 370 684 ; C 34 ; WX 555 ; N quotedbl ; B 136 398 536 685 ; C 35 ; WX 500 ; N numbersign ; B -33 0 533 700 ; C 36 ; WX 500 ; N dollar ; B -20 -100 497 733 ; C 37 ; WX 833 ; N percent ; B 39 -10 793 692 ; C 38 ; WX 778 ; N ampersand ; B 5 -19 699 682 ; C 39 ; WX 333 ; N quoteright ; B 98 369 302 685 ; C 40 ; WX 333 ; N parenleft ; B 28 -179 344 685 ; C 41 ; WX 333 ; N parenright ; B -44 -179 271 685 ; C 42 ; WX 500 ; N asterisk ; B 65 249 456 685 ; C 43 ; WX 570 ; N plus ; B 33 0 537 506 ; C 44 ; WX 250 ; N comma ; B -60 -182 144 134 ; C 45 ; WX 333 ; N hyphen ; B 2 166 271 282 ; C 46 ; WX 250 ; N period ; B -9 -13 139 135 ; C 47 ; WX 278 ; N slash ; B -64 -18 342 685 ; C 48 ; WX 500 ; N zero ; B 17 -14 477 683 ; C 49 ; WX 500 ; N one ; B 5 0 419 683 ; C 50 ; WX 500 ; N two ; B -27 0 446 683 ; C 51 ; WX 500 ; N three ; B -15 -13 450 683 ; C 52 ; WX 500 ; N four ; B -15 0 503 683 ; C 53 ; WX 500 ; N five ; B -11 -13 487 669 ; C 54 ; WX 500 ; N six ; B 23 -15 509 679 ; C 55 ; WX 500 ; N seven ; B 52 0 525 669 ; C 56 ; WX 500 ; N eight ; B 3 -13 476 683 ; C 57 ; WX 500 ; N nine ; B -12 -10 475 683 ; C 58 ; WX 333 ; N colon ; B 23 -13 264 459 ; C 59 ; WX 333 ; N semicolon ; B -25 -183 264 459 ; C 60 ; WX 570 ; N less ; B 31 -8 539 514 ; C 61 ; WX 570 ; N equal ; B 33 107 537 399 ; C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ; C 63 ; WX 500 ; N question ; B 79 -13 470 684 ; C 64 ; WX 832 ; N at ; B 63 -18 770 685 ; C 65 ; WX 667 ; N A ; B -67 0 593 683 ; C 66 ; WX 667 ; N B ; B -24 0 624 669 ; C 67 ; WX 667 ; N C ; B 32 -18 677 685 ; C 68 ; WX 722 ; N D ; B -46 0 685 669 ; C 69 ; WX 667 ; N E ; B -27 0 653 669 ; C 70 ; WX 667 ; N F ; B -13 0 660 669 ; C 71 ; WX 722 ; N G ; B 21 -18 706 685 ; C 72 ; WX 778 ; N H ; B -24 0 799 669 ; C 73 ; WX 389 ; N I ; B -32 0 406 669 ; C 74 ; WX 500 ; N J ; B -46 -99 524 669 ; C 75 ; WX 667 ; N K ; B -21 0 702 669 ; C 76 ; WX 611 ; N L ; B -22 0 590 669 ; C 77 ; WX 889 ; N M ; B -29 -12 917 669 ; C 78 ; WX 722 ; N N ; B -27 -15 748 669 ; C 79 ; WX 722 ; N O ; B 27 -18 691 685 ; C 80 ; WX 611 ; N P ; B -27 0 613 669 ; C 81 ; WX 722 ; N Q ; B 27 -208 691 685 ; C 82 ; WX 667 ; N R ; B -29 0 623 669 ; C 83 ; WX 556 ; N S ; B 2 -18 526 685 ; C 84 ; WX 611 ; N T ; B 50 0 650 669 ; C 85 ; WX 722 ; N U ; B 67 -18 744 669 ; C 86 ; WX 667 ; N V ; B 65 -18 715 669 ; C 87 ; WX 889 ; N W ; B 65 -18 940 669 ; C 88 ; WX 667 ; N X ; B -24 0 694 669 ; C 89 ; WX 611 ; N Y ; B 73 0 659 669 ; C 90 ; WX 611 ; N Z ; B -11 0 590 669 ; C 91 ; WX 333 ; N bracketleft ; B -37 -159 362 674 ; C 92 ; WX 278 ; N backslash ; B -1 -18 279 685 ; C 93 ; WX 333 ; N bracketright ; B -56 -157 343 674 ; C 94 ; WX 570 ; N asciicircum ; B 67 304 503 669 ; C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; C 96 ; WX 333 ; N quoteleft ; B 128 369 332 685 ; C 97 ; WX 500 ; N a ; B -21 -14 455 462 ; C 98 ; WX 500 ; N b ; B -14 -13 444 699 ; C 99 ; WX 444 ; N c ; B -5 -13 392 462 ; C 100 ; WX 500 ; N d ; B -21 -13 517 699 ; C 101 ; WX 444 ; N e ; B 5 -13 398 462 ; C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ; C 103 ; WX 500 ; N g ; B -52 -203 478 462 ; C 104 ; WX 556 ; N h ; B -13 -9 498 699 ; C 105 ; WX 278 ; N i ; B 2 -9 263 684 ; C 106 ; WX 278 ; N j ; B -189 -207 279 684 ; C 107 ; WX 500 ; N k ; B -23 -8 483 699 ; C 108 ; WX 278 ; N l ; B 2 -9 290 699 ; C 109 ; WX 778 ; N m ; B -14 -9 722 462 ; C 110 ; WX 556 ; N n ; B -6 -9 493 462 ; C 111 ; WX 500 ; N o ; B -3 -13 441 462 ; C 112 ; WX 500 ; N p ; B -120 -205 446 462 ; C 113 ; WX 500 ; N q ; B 1 -205 471 462 ; C 114 ; WX 389 ; N r ; B -21 0 389 462 ; C 115 ; WX 389 ; N s ; B -19 -13 333 462 ; C 116 ; WX 278 ; N t ; B -11 -9 281 594 ; C 117 ; WX 556 ; N u ; B 15 -9 492 462 ; C 118 ; WX 444 ; N v ; B 16 -13 401 462 ; C 119 ; WX 667 ; N w ; B 16 -13 614 462 ; C 120 ; WX 500 ; N x ; B -46 -13 469 462 ; C 121 ; WX 444 ; N y ; B -94 -205 392 462 ; C 122 ; WX 389 ; N z ; B -43 -78 368 449 ; C 123 ; WX 348 ; N braceleft ; B 5 -187 436 686 ; C 124 ; WX 220 ; N bar ; B 66 -218 154 782 ; C 125 ; WX 348 ; N braceright ; B -129 -187 302 686 ; C 126 ; WX 570 ; N asciitilde ; B 54 173 516 333 ; C 161 ; WX 389 ; N exclamdown ; B 19 -205 322 492 ; C 162 ; WX 500 ; N cent ; B 42 -143 439 576 ; C 163 ; WX 500 ; N sterling ; B -32 -12 510 683 ; C 164 ; WX 167 ; N fraction ; B -169 -14 324 683 ; C 165 ; WX 500 ; N yen ; B 33 0 628 669 ; C 166 ; WX 500 ; N florin ; B -87 -156 537 707 ; C 167 ; WX 500 ; N section ; B 36 -143 459 685 ; C 168 ; WX 500 ; N currency ; B -26 34 526 586 ; C 169 ; WX 278 ; N quotesingle ; B 128 398 268 685 ; C 170 ; WX 500 ; N quotedblleft ; B 53 369 513 685 ; C 171 ; WX 500 ; N guillemotleft ; B 12 32 468 415 ; C 172 ; WX 333 ; N guilsinglleft ; B 32 32 303 415 ; C 173 ; WX 333 ; N guilsinglright ; B 10 32 281 415 ; C 174 ; WX 556 ; N fi ; B -188 -205 514 703 ; C 175 ; WX 556 ; N fl ; B -186 -205 553 704 ; C 177 ; WX 500 ; N endash ; B -40 178 477 269 ; C 178 ; WX 500 ; N dagger ; B 91 -145 494 685 ; C 179 ; WX 500 ; N daggerdbl ; B 10 -139 493 685 ; C 180 ; WX 250 ; N periodcentered ; B 51 257 199 405 ; C 182 ; WX 500 ; N paragraph ; B -57 -193 562 669 ; C 183 ; WX 350 ; N bullet ; B 0 175 350 525 ; C 184 ; WX 333 ; N quotesinglbase ; B -5 -182 199 134 ; C 185 ; WX 500 ; N quotedblbase ; B -57 -182 403 134 ; C 186 ; WX 500 ; N quotedblright ; B 53 369 513 685 ; C 187 ; WX 500 ; N guillemotright ; B 12 32 468 415 ; C 188 ; WX 1000 ; N ellipsis ; B 40 -13 852 135 ; C 189 ; WX 1000 ; N perthousand ; B 7 -29 996 706 ; C 191 ; WX 500 ; N questiondown ; B 30 -205 421 492 ; C 193 ; WX 333 ; N grave ; B 85 516 297 697 ; C 194 ; WX 333 ; N acute ; B 139 516 379 697 ; C 195 ; WX 333 ; N circumflex ; B 40 516 367 690 ; C 196 ; WX 333 ; N tilde ; B 48 536 407 655 ; C 197 ; WX 333 ; N macron ; B 51 553 393 623 ; C 198 ; WX 333 ; N breve ; B 71 516 387 678 ; C 199 ; WX 333 ; N dotaccent ; B 163 550 298 684 ; C 200 ; WX 333 ; N dieresis ; B 55 550 402 684 ; C 202 ; WX 333 ; N ring ; B 127 516 340 729 ; C 203 ; WX 333 ; N cedilla ; B -80 -218 156 5 ; C 205 ; WX 333 ; N hungarumlaut ; B 69 516 498 697 ; C 206 ; WX 333 ; N ogonek ; B 15 -183 244 34 ; C 207 ; WX 333 ; N caron ; B 79 516 411 690 ; C 208 ; WX 1000 ; N emdash ; B -40 178 977 269 ; C 225 ; WX 944 ; N AE ; B -64 0 918 669 ; C 227 ; WX 266 ; N ordfeminine ; B 16 399 330 685 ; C 232 ; WX 611 ; N Lslash ; B -22 0 590 669 ; C 233 ; WX 722 ; N Oslash ; B 27 -125 691 764 ; C 234 ; WX 944 ; N OE ; B 23 -8 946 677 ; C 235 ; WX 300 ; N ordmasculine ; B 56 400 347 685 ; C 241 ; WX 722 ; N ae ; B -5 -13 673 462 ; C 245 ; WX 278 ; N dotlessi ; B 2 -9 238 462 ; C 248 ; WX 278 ; N lslash ; B -7 -9 307 699 ; C 249 ; WX 500 ; N oslash ; B -3 -119 441 560 ; C 250 ; WX 722 ; N oe ; B 6 -13 674 462 ; C 251 ; WX 500 ; N germandbls ; B -200 -200 473 705 ; C -1 ; WX 389 ; N Idieresis ; B -32 0 450 862 ; C -1 ; WX 444 ; N eacute ; B 5 -13 435 697 ; C -1 ; WX 500 ; N abreve ; B -21 -14 471 678 ; C -1 ; WX 556 ; N uhungarumlaut ; B 15 -9 610 697 ; C -1 ; WX 444 ; N ecaron ; B 5 -13 467 690 ; C -1 ; WX 611 ; N Ydieresis ; B 73 0 659 862 ; C -1 ; WX 570 ; N divide ; B 33 -29 537 535 ; C -1 ; WX 611 ; N Yacute ; B 73 0 659 904 ; C -1 ; WX 667 ; N Acircumflex ; B -67 0 593 897 ; C -1 ; WX 500 ; N aacute ; B -21 -14 463 697 ; C -1 ; WX 722 ; N Ucircumflex ; B 67 -18 744 897 ; C -1 ; WX 444 ; N yacute ; B -94 -205 435 697 ; C -1 ; WX 389 ; N scommaaccent ; B -19 -218 333 462 ; C -1 ; WX 444 ; N ecircumflex ; B 5 -13 423 690 ; C -1 ; WX 722 ; N Uring ; B 67 -18 744 921 ; C -1 ; WX 722 ; N Udieresis ; B 67 -18 744 862 ; C -1 ; WX 500 ; N aogonek ; B -21 -183 455 462 ; C -1 ; WX 722 ; N Uacute ; B 67 -18 744 904 ; C -1 ; WX 556 ; N uogonek ; B 15 -183 492 462 ; C -1 ; WX 667 ; N Edieresis ; B -27 0 653 862 ; C -1 ; WX 722 ; N Dcroat ; B -31 0 700 669 ; C -1 ; WX 250 ; N commaaccent ; B -36 -218 131 -50 ; C -1 ; WX 747 ; N copyright ; B 30 -18 718 685 ; C -1 ; WX 667 ; N Emacron ; B -27 0 653 830 ; C -1 ; WX 444 ; N ccaron ; B -5 -13 467 690 ; C -1 ; WX 500 ; N aring ; B -21 -14 455 729 ; C -1 ; WX 722 ; N Ncommaaccent ; B -27 -218 748 669 ; C -1 ; WX 278 ; N lacute ; B 2 -9 392 904 ; C -1 ; WX 500 ; N agrave ; B -21 -14 455 697 ; C -1 ; WX 611 ; N Tcommaaccent ; B 50 -218 650 669 ; C -1 ; WX 667 ; N Cacute ; B 32 -18 677 904 ; C -1 ; WX 500 ; N atilde ; B -21 -14 491 655 ; C -1 ; WX 667 ; N Edotaccent ; B -27 0 653 862 ; C -1 ; WX 389 ; N scaron ; B -19 -13 424 690 ; C -1 ; WX 389 ; N scedilla ; B -19 -218 333 462 ; C -1 ; WX 278 ; N iacute ; B 2 -9 352 697 ; C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ; C -1 ; WX 667 ; N Rcaron ; B -29 0 623 897 ; C -1 ; WX 722 ; N Gcommaaccent ; B 21 -218 706 685 ; C -1 ; WX 556 ; N ucircumflex ; B 15 -9 492 690 ; C -1 ; WX 500 ; N acircumflex ; B -21 -14 455 690 ; C -1 ; WX 667 ; N Amacron ; B -67 0 593 830 ; C -1 ; WX 389 ; N rcaron ; B -21 0 424 690 ; C -1 ; WX 444 ; N ccedilla ; B -5 -218 392 462 ; C -1 ; WX 611 ; N Zdotaccent ; B -11 0 590 862 ; C -1 ; WX 611 ; N Thorn ; B -27 0 573 669 ; C -1 ; WX 722 ; N Omacron ; B 27 -18 691 830 ; C -1 ; WX 667 ; N Racute ; B -29 0 623 904 ; C -1 ; WX 556 ; N Sacute ; B 2 -18 531 904 ; C -1 ; WX 608 ; N dcaron ; B -21 -13 675 708 ; C -1 ; WX 722 ; N Umacron ; B 67 -18 744 830 ; C -1 ; WX 556 ; N uring ; B 15 -9 492 729 ; C -1 ; WX 300 ; N threesuperior ; B 17 265 321 683 ; C -1 ; WX 722 ; N Ograve ; B 27 -18 691 904 ; C -1 ; WX 667 ; N Agrave ; B -67 0 593 904 ; C -1 ; WX 667 ; N Abreve ; B -67 0 593 885 ; C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ; C -1 ; WX 556 ; N uacute ; B 15 -9 492 697 ; C -1 ; WX 611 ; N Tcaron ; B 50 0 650 897 ; C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ; C -1 ; WX 444 ; N ydieresis ; B -94 -205 443 655 ; C -1 ; WX 722 ; N Nacute ; B -27 -15 748 904 ; C -1 ; WX 278 ; N icircumflex ; B -3 -9 324 690 ; C -1 ; WX 667 ; N Ecircumflex ; B -27 0 653 897 ; C -1 ; WX 500 ; N adieresis ; B -21 -14 476 655 ; C -1 ; WX 444 ; N edieresis ; B 5 -13 448 655 ; C -1 ; WX 444 ; N cacute ; B -5 -13 435 697 ; C -1 ; WX 556 ; N nacute ; B -6 -9 493 697 ; C -1 ; WX 556 ; N umacron ; B 15 -9 492 623 ; C -1 ; WX 722 ; N Ncaron ; B -27 -15 748 897 ; C -1 ; WX 389 ; N Iacute ; B -32 0 432 904 ; C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ; C -1 ; WX 220 ; N brokenbar ; B 66 -143 154 707 ; C -1 ; WX 747 ; N registered ; B 30 -18 718 685 ; C -1 ; WX 722 ; N Gbreve ; B 21 -18 706 885 ; C -1 ; WX 389 ; N Idotaccent ; B -32 0 406 862 ; C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ; C -1 ; WX 667 ; N Egrave ; B -27 0 653 904 ; C -1 ; WX 389 ; N racute ; B -21 0 407 697 ; C -1 ; WX 500 ; N omacron ; B -3 -13 462 623 ; C -1 ; WX 611 ; N Zacute ; B -11 0 590 904 ; C -1 ; WX 611 ; N Zcaron ; B -11 0 590 897 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ; C -1 ; WX 722 ; N Eth ; B -31 0 700 669 ; C -1 ; WX 667 ; N Ccedilla ; B 32 -218 677 685 ; C -1 ; WX 278 ; N lcommaaccent ; B -42 -218 290 699 ; C -1 ; WX 366 ; N tcaron ; B -11 -9 434 754 ; C -1 ; WX 444 ; N eogonek ; B 5 -183 398 462 ; C -1 ; WX 722 ; N Uogonek ; B 67 -183 744 669 ; C -1 ; WX 667 ; N Aacute ; B -67 0 593 904 ; C -1 ; WX 667 ; N Adieresis ; B -67 0 593 862 ; C -1 ; WX 444 ; N egrave ; B 5 -13 398 697 ; C -1 ; WX 389 ; N zacute ; B -43 -78 407 697 ; C -1 ; WX 278 ; N iogonek ; B -20 -183 263 684 ; C -1 ; WX 722 ; N Oacute ; B 27 -18 691 904 ; C -1 ; WX 500 ; N oacute ; B -3 -13 463 697 ; C -1 ; WX 500 ; N amacron ; B -21 -14 467 623 ; C -1 ; WX 389 ; N sacute ; B -19 -13 407 697 ; C -1 ; WX 278 ; N idieresis ; B 2 -9 364 655 ; C -1 ; WX 722 ; N Ocircumflex ; B 27 -18 691 897 ; C -1 ; WX 722 ; N Ugrave ; B 67 -18 744 904 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C -1 ; WX 500 ; N thorn ; B -120 -205 446 699 ; C -1 ; WX 300 ; N twosuperior ; B 2 274 313 683 ; C -1 ; WX 722 ; N Odieresis ; B 27 -18 691 862 ; C -1 ; WX 576 ; N mu ; B -60 -207 516 449 ; C -1 ; WX 278 ; N igrave ; B 2 -9 259 697 ; C -1 ; WX 500 ; N ohungarumlaut ; B -3 -13 582 697 ; C -1 ; WX 667 ; N Eogonek ; B -27 -183 653 669 ; C -1 ; WX 500 ; N dcroat ; B -21 -13 552 699 ; C -1 ; WX 750 ; N threequarters ; B 7 -14 726 683 ; C -1 ; WX 556 ; N Scedilla ; B 2 -218 526 685 ; C -1 ; WX 382 ; N lcaron ; B 2 -9 448 708 ; C -1 ; WX 667 ; N Kcommaaccent ; B -21 -218 702 669 ; C -1 ; WX 611 ; N Lacute ; B -22 0 590 904 ; C -1 ; WX 1000 ; N trademark ; B 32 263 968 669 ; C -1 ; WX 444 ; N edotaccent ; B 5 -13 398 655 ; C -1 ; WX 389 ; N Igrave ; B -32 0 406 904 ; C -1 ; WX 389 ; N Imacron ; B -32 0 461 830 ; C -1 ; WX 611 ; N Lcaron ; B -22 0 671 718 ; C -1 ; WX 750 ; N onehalf ; B -9 -14 723 683 ; C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ; C -1 ; WX 500 ; N ocircumflex ; B -3 -13 451 690 ; C -1 ; WX 556 ; N ntilde ; B -6 -9 504 655 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 67 -18 744 904 ; C -1 ; WX 667 ; N Eacute ; B -27 0 653 904 ; C -1 ; WX 444 ; N emacron ; B 5 -13 439 623 ; C -1 ; WX 500 ; N gbreve ; B -52 -203 478 678 ; C -1 ; WX 750 ; N onequarter ; B 7 -14 721 683 ; C -1 ; WX 556 ; N Scaron ; B 2 -18 553 897 ; C -1 ; WX 556 ; N Scommaaccent ; B 2 -218 526 685 ; C -1 ; WX 722 ; N Ohungarumlaut ; B 27 -18 723 904 ; C -1 ; WX 400 ; N degree ; B 83 397 369 683 ; C -1 ; WX 500 ; N ograve ; B -3 -13 441 697 ; C -1 ; WX 667 ; N Ccaron ; B 32 -18 677 897 ; C -1 ; WX 556 ; N ugrave ; B 15 -9 492 697 ; C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ; C -1 ; WX 722 ; N Dcaron ; B -46 0 685 897 ; C -1 ; WX 389 ; N rcommaaccent ; B -67 -218 389 462 ; C -1 ; WX 722 ; N Ntilde ; B -27 -15 748 862 ; C -1 ; WX 500 ; N otilde ; B -3 -13 491 655 ; C -1 ; WX 667 ; N Rcommaaccent ; B -29 -218 623 669 ; C -1 ; WX 611 ; N Lcommaaccent ; B -22 -218 590 669 ; C -1 ; WX 667 ; N Atilde ; B -67 0 593 862 ; C -1 ; WX 667 ; N Aogonek ; B -67 -183 604 683 ; C -1 ; WX 667 ; N Aring ; B -67 0 593 921 ; C -1 ; WX 722 ; N Otilde ; B 27 -18 691 862 ; C -1 ; WX 389 ; N zdotaccent ; B -43 -78 368 655 ; C -1 ; WX 667 ; N Ecaron ; B -27 0 653 897 ; C -1 ; WX 389 ; N Iogonek ; B -32 -183 406 669 ; C -1 ; WX 500 ; N kcommaaccent ; B -23 -218 483 699 ; C -1 ; WX 606 ; N minus ; B 51 209 555 297 ; C -1 ; WX 389 ; N Icircumflex ; B -32 0 450 897 ; C -1 ; WX 556 ; N ncaron ; B -6 -9 523 690 ; C -1 ; WX 278 ; N tcommaaccent ; B -62 -218 281 594 ; C -1 ; WX 606 ; N logicalnot ; B 51 108 555 399 ; C -1 ; WX 500 ; N odieresis ; B -3 -13 471 655 ; C -1 ; WX 556 ; N udieresis ; B 15 -9 499 655 ; C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ; C -1 ; WX 500 ; N gcommaaccent ; B -52 -203 478 767 ; C -1 ; WX 500 ; N eth ; B -3 -13 454 699 ; C -1 ; WX 389 ; N zcaron ; B -43 -78 424 690 ; C -1 ; WX 556 ; N ncommaaccent ; B -6 -218 493 462 ; C -1 ; WX 300 ; N onesuperior ; B 30 274 301 683 ; C -1 ; WX 278 ; N imacron ; B 2 -9 294 623 ; C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2038 KPX A C -65 KPX A Cacute -65 KPX A Ccaron -65 KPX A Ccedilla -65 KPX A G -60 KPX A Gbreve -60 KPX A Gcommaaccent -60 KPX A O -50 KPX A Oacute -50 KPX A Ocircumflex -50 KPX A Odieresis -50 KPX A Ograve -50 KPX A Ohungarumlaut -50 KPX A Omacron -50 KPX A Oslash -50 KPX A Otilde -50 KPX A Q -55 KPX A T -55 KPX A Tcaron -55 KPX A Tcommaaccent -55 KPX A U -50 KPX A Uacute -50 KPX A Ucircumflex -50 KPX A Udieresis -50 KPX A Ugrave -50 KPX A Uhungarumlaut -50 KPX A Umacron -50 KPX A Uogonek -50 KPX A Uring -50 KPX A V -95 KPX A W -100 KPX A Y -70 KPX A Yacute -70 KPX A Ydieresis -70 KPX A quoteright -74 KPX A u -30 KPX A uacute -30 KPX A ucircumflex -30 KPX A udieresis -30 KPX A ugrave -30 KPX A uhungarumlaut -30 KPX A umacron -30 KPX A uogonek -30 KPX A uring -30 KPX A v -74 KPX A w -74 KPX A y -74 KPX A yacute -74 KPX A ydieresis -74 KPX Aacute C -65 KPX Aacute Cacute -65 KPX Aacute Ccaron -65 KPX Aacute Ccedilla -65 KPX Aacute G -60 KPX Aacute Gbreve -60 KPX Aacute Gcommaaccent -60 KPX Aacute O -50 KPX Aacute Oacute -50 KPX Aacute Ocircumflex -50 KPX Aacute Odieresis -50 KPX Aacute Ograve -50 KPX Aacute Ohungarumlaut -50 KPX Aacute Omacron -50 KPX Aacute Oslash -50 KPX Aacute Otilde -50 KPX Aacute Q -55 KPX Aacute T -55 KPX Aacute Tcaron -55 KPX Aacute Tcommaaccent -55 KPX Aacute U -50 KPX Aacute Uacute -50 KPX Aacute Ucircumflex -50 KPX Aacute Udieresis -50 KPX Aacute Ugrave -50 KPX Aacute Uhungarumlaut -50 KPX Aacute Umacron -50 KPX Aacute Uogonek -50 KPX Aacute Uring -50 KPX Aacute V -95 KPX Aacute W -100 KPX Aacute Y -70 KPX Aacute Yacute -70 KPX Aacute Ydieresis -70 KPX Aacute quoteright -74 KPX Aacute u -30 KPX Aacute uacute -30 KPX Aacute ucircumflex -30 KPX Aacute udieresis -30 KPX Aacute ugrave -30 KPX Aacute uhungarumlaut -30 KPX Aacute umacron -30 KPX Aacute uogonek -30 KPX Aacute uring -30 KPX Aacute v -74 KPX Aacute w -74 KPX Aacute y -74 KPX Aacute yacute -74 KPX Aacute ydieresis -74 KPX Abreve C -65 KPX Abreve Cacute -65 KPX Abreve Ccaron -65 KPX Abreve Ccedilla -65 KPX Abreve G -60 KPX Abreve Gbreve -60 KPX Abreve Gcommaaccent -60 KPX Abreve O -50 KPX Abreve Oacute -50 KPX Abreve Ocircumflex -50 KPX Abreve Odieresis -50 KPX Abreve Ograve -50 KPX Abreve Ohungarumlaut -50 KPX Abreve Omacron -50 KPX Abreve Oslash -50 KPX Abreve Otilde -50 KPX Abreve Q -55 KPX Abreve T -55 KPX Abreve Tcaron -55 KPX Abreve Tcommaaccent -55 KPX Abreve U -50 KPX Abreve Uacute -50 KPX Abreve Ucircumflex -50 KPX Abreve Udieresis -50 KPX Abreve Ugrave -50 KPX Abreve Uhungarumlaut -50 KPX Abreve Umacron -50 KPX Abreve Uogonek -50 KPX Abreve Uring -50 KPX Abreve V -95 KPX Abreve W -100 KPX Abreve Y -70 KPX Abreve Yacute -70 KPX Abreve Ydieresis -70 KPX Abreve quoteright -74 KPX Abreve u -30 KPX Abreve uacute -30 KPX Abreve ucircumflex -30 KPX Abreve udieresis -30 KPX Abreve ugrave -30 KPX Abreve uhungarumlaut -30 KPX Abreve umacron -30 KPX Abreve uogonek -30 KPX Abreve uring -30 KPX Abreve v -74 KPX Abreve w -74 KPX Abreve y -74 KPX Abreve yacute -74 KPX Abreve ydieresis -74 KPX Acircumflex C -65 KPX Acircumflex Cacute -65 KPX Acircumflex Ccaron -65 KPX Acircumflex Ccedilla -65 KPX Acircumflex G -60 KPX Acircumflex Gbreve -60 KPX Acircumflex Gcommaaccent -60 KPX Acircumflex O -50 KPX Acircumflex Oacute -50 KPX Acircumflex Ocircumflex -50 KPX Acircumflex Odieresis -50 KPX Acircumflex Ograve -50 KPX Acircumflex Ohungarumlaut -50 KPX Acircumflex Omacron -50 KPX Acircumflex Oslash -50 KPX Acircumflex Otilde -50 KPX Acircumflex Q -55 KPX Acircumflex T -55 KPX Acircumflex Tcaron -55 KPX Acircumflex Tcommaaccent -55 KPX Acircumflex U -50 KPX Acircumflex Uacute -50 KPX Acircumflex Ucircumflex -50 KPX Acircumflex Udieresis -50 KPX Acircumflex Ugrave -50 KPX Acircumflex Uhungarumlaut -50 KPX Acircumflex Umacron -50 KPX Acircumflex Uogonek -50 KPX Acircumflex Uring -50 KPX Acircumflex V -95 KPX Acircumflex W -100 KPX Acircumflex Y -70 KPX Acircumflex Yacute -70 KPX Acircumflex Ydieresis -70 KPX Acircumflex quoteright -74 KPX Acircumflex u -30 KPX Acircumflex uacute -30 KPX Acircumflex ucircumflex -30 KPX Acircumflex udieresis -30 KPX Acircumflex ugrave -30 KPX Acircumflex uhungarumlaut -30 KPX Acircumflex umacron -30 KPX Acircumflex uogonek -30 KPX Acircumflex uring -30 KPX Acircumflex v -74 KPX Acircumflex w -74 KPX Acircumflex y -74 KPX Acircumflex yacute -74 KPX Acircumflex ydieresis -74 KPX Adieresis C -65 KPX Adieresis Cacute -65 KPX Adieresis Ccaron -65 KPX Adieresis Ccedilla -65 KPX Adieresis G -60 KPX Adieresis Gbreve -60 KPX Adieresis Gcommaaccent -60 KPX Adieresis O -50 KPX Adieresis Oacute -50 KPX Adieresis Ocircumflex -50 KPX Adieresis Odieresis -50 KPX Adieresis Ograve -50 KPX Adieresis Ohungarumlaut -50 KPX Adieresis Omacron -50 KPX Adieresis Oslash -50 KPX Adieresis Otilde -50 KPX Adieresis Q -55 KPX Adieresis T -55 KPX Adieresis Tcaron -55 KPX Adieresis Tcommaaccent -55 KPX Adieresis U -50 KPX Adieresis Uacute -50 KPX Adieresis Ucircumflex -50 KPX Adieresis Udieresis -50 KPX Adieresis Ugrave -50 KPX Adieresis Uhungarumlaut -50 KPX Adieresis Umacron -50 KPX Adieresis Uogonek -50 KPX Adieresis Uring -50 KPX Adieresis V -95 KPX Adieresis W -100 KPX Adieresis Y -70 KPX Adieresis Yacute -70 KPX Adieresis Ydieresis -70 KPX Adieresis quoteright -74 KPX Adieresis u -30 KPX Adieresis uacute -30 KPX Adieresis ucircumflex -30 KPX Adieresis udieresis -30 KPX Adieresis ugrave -30 KPX Adieresis uhungarumlaut -30 KPX Adieresis umacron -30 KPX Adieresis uogonek -30 KPX Adieresis uring -30 KPX Adieresis v -74 KPX Adieresis w -74 KPX Adieresis y -74 KPX Adieresis yacute -74 KPX Adieresis ydieresis -74 KPX Agrave C -65 KPX Agrave Cacute -65 KPX Agrave Ccaron -65 KPX Agrave Ccedilla -65 KPX Agrave G -60 KPX Agrave Gbreve -60 KPX Agrave Gcommaaccent -60 KPX Agrave O -50 KPX Agrave Oacute -50 KPX Agrave Ocircumflex -50 KPX Agrave Odieresis -50 KPX Agrave Ograve -50 KPX Agrave Ohungarumlaut -50 KPX Agrave Omacron -50 KPX Agrave Oslash -50 KPX Agrave Otilde -50 KPX Agrave Q -55 KPX Agrave T -55 KPX Agrave Tcaron -55 KPX Agrave Tcommaaccent -55 KPX Agrave U -50 KPX Agrave Uacute -50 KPX Agrave Ucircumflex -50 KPX Agrave Udieresis -50 KPX Agrave Ugrave -50 KPX Agrave Uhungarumlaut -50 KPX Agrave Umacron -50 KPX Agrave Uogonek -50 KPX Agrave Uring -50 KPX Agrave V -95 KPX Agrave W -100 KPX Agrave Y -70 KPX Agrave Yacute -70 KPX Agrave Ydieresis -70 KPX Agrave quoteright -74 KPX Agrave u -30 KPX Agrave uacute -30 KPX Agrave ucircumflex -30 KPX Agrave udieresis -30 KPX Agrave ugrave -30 KPX Agrave uhungarumlaut -30 KPX Agrave umacron -30 KPX Agrave uogonek -30 KPX Agrave uring -30 KPX Agrave v -74 KPX Agrave w -74 KPX Agrave y -74 KPX Agrave yacute -74 KPX Agrave ydieresis -74 KPX Amacron C -65 KPX Amacron Cacute -65 KPX Amacron Ccaron -65 KPX Amacron Ccedilla -65 KPX Amacron G -60 KPX Amacron Gbreve -60 KPX Amacron Gcommaaccent -60 KPX Amacron O -50 KPX Amacron Oacute -50 KPX Amacron Ocircumflex -50 KPX Amacron Odieresis -50 KPX Amacron Ograve -50 KPX Amacron Ohungarumlaut -50 KPX Amacron Omacron -50 KPX Amacron Oslash -50 KPX Amacron Otilde -50 KPX Amacron Q -55 KPX Amacron T -55 KPX Amacron Tcaron -55 KPX Amacron Tcommaaccent -55 KPX Amacron U -50 KPX Amacron Uacute -50 KPX Amacron Ucircumflex -50 KPX Amacron Udieresis -50 KPX Amacron Ugrave -50 KPX Amacron Uhungarumlaut -50 KPX Amacron Umacron -50 KPX Amacron Uogonek -50 KPX Amacron Uring -50 KPX Amacron V -95 KPX Amacron W -100 KPX Amacron Y -70 KPX Amacron Yacute -70 KPX Amacron Ydieresis -70 KPX Amacron quoteright -74 KPX Amacron u -30 KPX Amacron uacute -30 KPX Amacron ucircumflex -30 KPX Amacron udieresis -30 KPX Amacron ugrave -30 KPX Amacron uhungarumlaut -30 KPX Amacron umacron -30 KPX Amacron uogonek -30 KPX Amacron uring -30 KPX Amacron v -74 KPX Amacron w -74 KPX Amacron y -74 KPX Amacron yacute -74 KPX Amacron ydieresis -74 KPX Aogonek C -65 KPX Aogonek Cacute -65 KPX Aogonek Ccaron -65 KPX Aogonek Ccedilla -65 KPX Aogonek G -60 KPX Aogonek Gbreve -60 KPX Aogonek Gcommaaccent -60 KPX Aogonek O -50 KPX Aogonek Oacute -50 KPX Aogonek Ocircumflex -50 KPX Aogonek Odieresis -50 KPX Aogonek Ograve -50 KPX Aogonek Ohungarumlaut -50 KPX Aogonek Omacron -50 KPX Aogonek Oslash -50 KPX Aogonek Otilde -50 KPX Aogonek Q -55 KPX Aogonek T -55 KPX Aogonek Tcaron -55 KPX Aogonek Tcommaaccent -55 KPX Aogonek U -50 KPX Aogonek Uacute -50 KPX Aogonek Ucircumflex -50 KPX Aogonek Udieresis -50 KPX Aogonek Ugrave -50 KPX Aogonek Uhungarumlaut -50 KPX Aogonek Umacron -50 KPX Aogonek Uogonek -50 KPX Aogonek Uring -50 KPX Aogonek V -95 KPX Aogonek W -100 KPX Aogonek Y -70 KPX Aogonek Yacute -70 KPX Aogonek Ydieresis -70 KPX Aogonek quoteright -74 KPX Aogonek u -30 KPX Aogonek uacute -30 KPX Aogonek ucircumflex -30 KPX Aogonek udieresis -30 KPX Aogonek ugrave -30 KPX Aogonek uhungarumlaut -30 KPX Aogonek umacron -30 KPX Aogonek uogonek -30 KPX Aogonek uring -30 KPX Aogonek v -74 KPX Aogonek w -74 KPX Aogonek y -34 KPX Aogonek yacute -34 KPX Aogonek ydieresis -34 KPX Aring C -65 KPX Aring Cacute -65 KPX Aring Ccaron -65 KPX Aring Ccedilla -65 KPX Aring G -60 KPX Aring Gbreve -60 KPX Aring Gcommaaccent -60 KPX Aring O -50 KPX Aring Oacute -50 KPX Aring Ocircumflex -50 KPX Aring Odieresis -50 KPX Aring Ograve -50 KPX Aring Ohungarumlaut -50 KPX Aring Omacron -50 KPX Aring Oslash -50 KPX Aring Otilde -50 KPX Aring Q -55 KPX Aring T -55 KPX Aring Tcaron -55 KPX Aring Tcommaaccent -55 KPX Aring U -50 KPX Aring Uacute -50 KPX Aring Ucircumflex -50 KPX Aring Udieresis -50 KPX Aring Ugrave -50 KPX Aring Uhungarumlaut -50 KPX Aring Umacron -50 KPX Aring Uogonek -50 KPX Aring Uring -50 KPX Aring V -95 KPX Aring W -100 KPX Aring Y -70 KPX Aring Yacute -70 KPX Aring Ydieresis -70 KPX Aring quoteright -74 KPX Aring u -30 KPX Aring uacute -30 KPX Aring ucircumflex -30 KPX Aring udieresis -30 KPX Aring ugrave -30 KPX Aring uhungarumlaut -30 KPX Aring umacron -30 KPX Aring uogonek -30 KPX Aring uring -30 KPX Aring v -74 KPX Aring w -74 KPX Aring y -74 KPX Aring yacute -74 KPX Aring ydieresis -74 KPX Atilde C -65 KPX Atilde Cacute -65 KPX Atilde Ccaron -65 KPX Atilde Ccedilla -65 KPX Atilde G -60 KPX Atilde Gbreve -60 KPX Atilde Gcommaaccent -60 KPX Atilde O -50 KPX Atilde Oacute -50 KPX Atilde Ocircumflex -50 KPX Atilde Odieresis -50 KPX Atilde Ograve -50 KPX Atilde Ohungarumlaut -50 KPX Atilde Omacron -50 KPX Atilde Oslash -50 KPX Atilde Otilde -50 KPX Atilde Q -55 KPX Atilde T -55 KPX Atilde Tcaron -55 KPX Atilde Tcommaaccent -55 KPX Atilde U -50 KPX Atilde Uacute -50 KPX Atilde Ucircumflex -50 KPX Atilde Udieresis -50 KPX Atilde Ugrave -50 KPX Atilde Uhungarumlaut -50 KPX Atilde Umacron -50 KPX Atilde Uogonek -50 KPX Atilde Uring -50 KPX Atilde V -95 KPX Atilde W -100 KPX Atilde Y -70 KPX Atilde Yacute -70 KPX Atilde Ydieresis -70 KPX Atilde quoteright -74 KPX Atilde u -30 KPX Atilde uacute -30 KPX Atilde ucircumflex -30 KPX Atilde udieresis -30 KPX Atilde ugrave -30 KPX Atilde uhungarumlaut -30 KPX Atilde umacron -30 KPX Atilde uogonek -30 KPX Atilde uring -30 KPX Atilde v -74 KPX Atilde w -74 KPX Atilde y -74 KPX Atilde yacute -74 KPX Atilde ydieresis -74 KPX B A -25 KPX B Aacute -25 KPX B Abreve -25 KPX B Acircumflex -25 KPX B Adieresis -25 KPX B Agrave -25 KPX B Amacron -25 KPX B Aogonek -25 KPX B Aring -25 KPX B Atilde -25 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX D A -25 KPX D Aacute -25 KPX D Abreve -25 KPX D Acircumflex -25 KPX D Adieresis -25 KPX D Agrave -25 KPX D Amacron -25 KPX D Aogonek -25 KPX D Aring -25 KPX D Atilde -25 KPX D V -50 KPX D W -40 KPX D Y -50 KPX D Yacute -50 KPX D Ydieresis -50 KPX Dcaron A -25 KPX Dcaron Aacute -25 KPX Dcaron Abreve -25 KPX Dcaron Acircumflex -25 KPX Dcaron Adieresis -25 KPX Dcaron Agrave -25 KPX Dcaron Amacron -25 KPX Dcaron Aogonek -25 KPX Dcaron Aring -25 KPX Dcaron Atilde -25 KPX Dcaron V -50 KPX Dcaron W -40 KPX Dcaron Y -50 KPX Dcaron Yacute -50 KPX Dcaron Ydieresis -50 KPX Dcroat A -25 KPX Dcroat Aacute -25 KPX Dcroat Abreve -25 KPX Dcroat Acircumflex -25 KPX Dcroat Adieresis -25 KPX Dcroat Agrave -25 KPX Dcroat Amacron -25 KPX Dcroat Aogonek -25 KPX Dcroat Aring -25 KPX Dcroat Atilde -25 KPX Dcroat V -50 KPX Dcroat W -40 KPX Dcroat Y -50 KPX Dcroat Yacute -50 KPX Dcroat Ydieresis -50 KPX F A -100 KPX F Aacute -100 KPX F Abreve -100 KPX F Acircumflex -100 KPX F Adieresis -100 KPX F Agrave -100 KPX F Amacron -100 KPX F Aogonek -100 KPX F Aring -100 KPX F Atilde -100 KPX F a -95 KPX F aacute -95 KPX F abreve -95 KPX F acircumflex -95 KPX F adieresis -95 KPX F agrave -95 KPX F amacron -95 KPX F aogonek -95 KPX F aring -95 KPX F atilde -95 KPX F comma -129 KPX F e -100 KPX F eacute -100 KPX F ecaron -100 KPX F ecircumflex -100 KPX F edieresis -100 KPX F edotaccent -100 KPX F egrave -100 KPX F emacron -100 KPX F eogonek -100 KPX F i -40 KPX F iacute -40 KPX F icircumflex -40 KPX F idieresis -40 KPX F igrave -40 KPX F imacron -40 KPX F iogonek -40 KPX F o -70 KPX F oacute -70 KPX F ocircumflex -70 KPX F odieresis -70 KPX F ograve -70 KPX F ohungarumlaut -70 KPX F omacron -70 KPX F oslash -70 KPX F otilde -70 KPX F period -129 KPX F r -50 KPX F racute -50 KPX F rcaron -50 KPX F rcommaaccent -50 KPX J A -25 KPX J Aacute -25 KPX J Abreve -25 KPX J Acircumflex -25 KPX J Adieresis -25 KPX J Agrave -25 KPX J Amacron -25 KPX J Aogonek -25 KPX J Aring -25 KPX J Atilde -25 KPX J a -40 KPX J aacute -40 KPX J abreve -40 KPX J acircumflex -40 KPX J adieresis -40 KPX J agrave -40 KPX J amacron -40 KPX J aogonek -40 KPX J aring -40 KPX J atilde -40 KPX J comma -10 KPX J e -40 KPX J eacute -40 KPX J ecaron -40 KPX J ecircumflex -40 KPX J edieresis -40 KPX J edotaccent -40 KPX J egrave -40 KPX J emacron -40 KPX J eogonek -40 KPX J o -40 KPX J oacute -40 KPX J ocircumflex -40 KPX J odieresis -40 KPX J ograve -40 KPX J ohungarumlaut -40 KPX J omacron -40 KPX J oslash -40 KPX J otilde -40 KPX J period -10 KPX J u -40 KPX J uacute -40 KPX J ucircumflex -40 KPX J udieresis -40 KPX J ugrave -40 KPX J uhungarumlaut -40 KPX J umacron -40 KPX J uogonek -40 KPX J uring -40 KPX K O -30 KPX K Oacute -30 KPX K Ocircumflex -30 KPX K Odieresis -30 KPX K Ograve -30 KPX K Ohungarumlaut -30 KPX K Omacron -30 KPX K Oslash -30 KPX K Otilde -30 KPX K e -25 KPX K eacute -25 KPX K ecaron -25 KPX K ecircumflex -25 KPX K edieresis -25 KPX K edotaccent -25 KPX K egrave -25 KPX K emacron -25 KPX K eogonek -25 KPX K o -25 KPX K oacute -25 KPX K ocircumflex -25 KPX K odieresis -25 KPX K ograve -25 KPX K ohungarumlaut -25 KPX K omacron -25 KPX K oslash -25 KPX K otilde -25 KPX K u -20 KPX K uacute -20 KPX K ucircumflex -20 KPX K udieresis -20 KPX K ugrave -20 KPX K uhungarumlaut -20 KPX K umacron -20 KPX K uogonek -20 KPX K uring -20 KPX K y -20 KPX K yacute -20 KPX K ydieresis -20 KPX Kcommaaccent O -30 KPX Kcommaaccent Oacute -30 KPX Kcommaaccent Ocircumflex -30 KPX Kcommaaccent Odieresis -30 KPX Kcommaaccent Ograve -30 KPX Kcommaaccent Ohungarumlaut -30 KPX Kcommaaccent Omacron -30 KPX Kcommaaccent Oslash -30 KPX Kcommaaccent Otilde -30 KPX Kcommaaccent e -25 KPX Kcommaaccent eacute -25 KPX Kcommaaccent ecaron -25 KPX Kcommaaccent ecircumflex -25 KPX Kcommaaccent edieresis -25 KPX Kcommaaccent edotaccent -25 KPX Kcommaaccent egrave -25 KPX Kcommaaccent emacron -25 KPX Kcommaaccent eogonek -25 KPX Kcommaaccent o -25 KPX Kcommaaccent oacute -25 KPX Kcommaaccent ocircumflex -25 KPX Kcommaaccent odieresis -25 KPX Kcommaaccent ograve -25 KPX Kcommaaccent ohungarumlaut -25 KPX Kcommaaccent omacron -25 KPX Kcommaaccent oslash -25 KPX Kcommaaccent otilde -25 KPX Kcommaaccent u -20 KPX Kcommaaccent uacute -20 KPX Kcommaaccent ucircumflex -20 KPX Kcommaaccent udieresis -20 KPX Kcommaaccent ugrave -20 KPX Kcommaaccent uhungarumlaut -20 KPX Kcommaaccent umacron -20 KPX Kcommaaccent uogonek -20 KPX Kcommaaccent uring -20 KPX Kcommaaccent y -20 KPX Kcommaaccent yacute -20 KPX Kcommaaccent ydieresis -20 KPX L T -18 KPX L Tcaron -18 KPX L Tcommaaccent -18 KPX L V -37 KPX L W -37 KPX L Y -37 KPX L Yacute -37 KPX L Ydieresis -37 KPX L quoteright -55 KPX L y -37 KPX L yacute -37 KPX L ydieresis -37 KPX Lacute T -18 KPX Lacute Tcaron -18 KPX Lacute Tcommaaccent -18 KPX Lacute V -37 KPX Lacute W -37 KPX Lacute Y -37 KPX Lacute Yacute -37 KPX Lacute Ydieresis -37 KPX Lacute quoteright -55 KPX Lacute y -37 KPX Lacute yacute -37 KPX Lacute ydieresis -37 KPX Lcommaaccent T -18 KPX Lcommaaccent Tcaron -18 KPX Lcommaaccent Tcommaaccent -18 KPX Lcommaaccent V -37 KPX Lcommaaccent W -37 KPX Lcommaaccent Y -37 KPX Lcommaaccent Yacute -37 KPX Lcommaaccent Ydieresis -37 KPX Lcommaaccent quoteright -55 KPX Lcommaaccent y -37 KPX Lcommaaccent yacute -37 KPX Lcommaaccent ydieresis -37 KPX Lslash T -18 KPX Lslash Tcaron -18 KPX Lslash Tcommaaccent -18 KPX Lslash V -37 KPX Lslash W -37 KPX Lslash Y -37 KPX Lslash Yacute -37 KPX Lslash Ydieresis -37 KPX Lslash quoteright -55 KPX Lslash y -37 KPX Lslash yacute -37 KPX Lslash ydieresis -37 KPX N A -30 KPX N Aacute -30 KPX N Abreve -30 KPX N Acircumflex -30 KPX N Adieresis -30 KPX N Agrave -30 KPX N Amacron -30 KPX N Aogonek -30 KPX N Aring -30 KPX N Atilde -30 KPX Nacute A -30 KPX Nacute Aacute -30 KPX Nacute Abreve -30 KPX Nacute Acircumflex -30 KPX Nacute Adieresis -30 KPX Nacute Agrave -30 KPX Nacute Amacron -30 KPX Nacute Aogonek -30 KPX Nacute Aring -30 KPX Nacute Atilde -30 KPX Ncaron A -30 KPX Ncaron Aacute -30 KPX Ncaron Abreve -30 KPX Ncaron Acircumflex -30 KPX Ncaron Adieresis -30 KPX Ncaron Agrave -30 KPX Ncaron Amacron -30 KPX Ncaron Aogonek -30 KPX Ncaron Aring -30 KPX Ncaron Atilde -30 KPX Ncommaaccent A -30 KPX Ncommaaccent Aacute -30 KPX Ncommaaccent Abreve -30 KPX Ncommaaccent Acircumflex -30 KPX Ncommaaccent Adieresis -30 KPX Ncommaaccent Agrave -30 KPX Ncommaaccent Amacron -30 KPX Ncommaaccent Aogonek -30 KPX Ncommaaccent Aring -30 KPX Ncommaaccent Atilde -30 KPX Ntilde A -30 KPX Ntilde Aacute -30 KPX Ntilde Abreve -30 KPX Ntilde Acircumflex -30 KPX Ntilde Adieresis -30 KPX Ntilde Agrave -30 KPX Ntilde Amacron -30 KPX Ntilde Aogonek -30 KPX Ntilde Aring -30 KPX Ntilde Atilde -30 KPX O A -40 KPX O Aacute -40 KPX O Abreve -40 KPX O Acircumflex -40 KPX O Adieresis -40 KPX O Agrave -40 KPX O Amacron -40 KPX O Aogonek -40 KPX O Aring -40 KPX O Atilde -40 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -50 KPX O X -40 KPX O Y -50 KPX O Yacute -50 KPX O Ydieresis -50 KPX Oacute A -40 KPX Oacute Aacute -40 KPX Oacute Abreve -40 KPX Oacute Acircumflex -40 KPX Oacute Adieresis -40 KPX Oacute Agrave -40 KPX Oacute Amacron -40 KPX Oacute Aogonek -40 KPX Oacute Aring -40 KPX Oacute Atilde -40 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -50 KPX Oacute X -40 KPX Oacute Y -50 KPX Oacute Yacute -50 KPX Oacute Ydieresis -50 KPX Ocircumflex A -40 KPX Ocircumflex Aacute -40 KPX Ocircumflex Abreve -40 KPX Ocircumflex Acircumflex -40 KPX Ocircumflex Adieresis -40 KPX Ocircumflex Agrave -40 KPX Ocircumflex Amacron -40 KPX Ocircumflex Aogonek -40 KPX Ocircumflex Aring -40 KPX Ocircumflex Atilde -40 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -50 KPX Ocircumflex X -40 KPX Ocircumflex Y -50 KPX Ocircumflex Yacute -50 KPX Ocircumflex Ydieresis -50 KPX Odieresis A -40 KPX Odieresis Aacute -40 KPX Odieresis Abreve -40 KPX Odieresis Acircumflex -40 KPX Odieresis Adieresis -40 KPX Odieresis Agrave -40 KPX Odieresis Amacron -40 KPX Odieresis Aogonek -40 KPX Odieresis Aring -40 KPX Odieresis Atilde -40 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -50 KPX Odieresis X -40 KPX Odieresis Y -50 KPX Odieresis Yacute -50 KPX Odieresis Ydieresis -50 KPX Ograve A -40 KPX Ograve Aacute -40 KPX Ograve Abreve -40 KPX Ograve Acircumflex -40 KPX Ograve Adieresis -40 KPX Ograve Agrave -40 KPX Ograve Amacron -40 KPX Ograve Aogonek -40 KPX Ograve Aring -40 KPX Ograve Atilde -40 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -50 KPX Ograve X -40 KPX Ograve Y -50 KPX Ograve Yacute -50 KPX Ograve Ydieresis -50 KPX Ohungarumlaut A -40 KPX Ohungarumlaut Aacute -40 KPX Ohungarumlaut Abreve -40 KPX Ohungarumlaut Acircumflex -40 KPX Ohungarumlaut Adieresis -40 KPX Ohungarumlaut Agrave -40 KPX Ohungarumlaut Amacron -40 KPX Ohungarumlaut Aogonek -40 KPX Ohungarumlaut Aring -40 KPX Ohungarumlaut Atilde -40 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -50 KPX Ohungarumlaut X -40 KPX Ohungarumlaut Y -50 KPX Ohungarumlaut Yacute -50 KPX Ohungarumlaut Ydieresis -50 KPX Omacron A -40 KPX Omacron Aacute -40 KPX Omacron Abreve -40 KPX Omacron Acircumflex -40 KPX Omacron Adieresis -40 KPX Omacron Agrave -40 KPX Omacron Amacron -40 KPX Omacron Aogonek -40 KPX Omacron Aring -40 KPX Omacron Atilde -40 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -50 KPX Omacron X -40 KPX Omacron Y -50 KPX Omacron Yacute -50 KPX Omacron Ydieresis -50 KPX Oslash A -40 KPX Oslash Aacute -40 KPX Oslash Abreve -40 KPX Oslash Acircumflex -40 KPX Oslash Adieresis -40 KPX Oslash Agrave -40 KPX Oslash Amacron -40 KPX Oslash Aogonek -40 KPX Oslash Aring -40 KPX Oslash Atilde -40 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -50 KPX Oslash X -40 KPX Oslash Y -50 KPX Oslash Yacute -50 KPX Oslash Ydieresis -50 KPX Otilde A -40 KPX Otilde Aacute -40 KPX Otilde Abreve -40 KPX Otilde Acircumflex -40 KPX Otilde Adieresis -40 KPX Otilde Agrave -40 KPX Otilde Amacron -40 KPX Otilde Aogonek -40 KPX Otilde Aring -40 KPX Otilde Atilde -40 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -50 KPX Otilde X -40 KPX Otilde Y -50 KPX Otilde Yacute -50 KPX Otilde Ydieresis -50 KPX P A -85 KPX P Aacute -85 KPX P Abreve -85 KPX P Acircumflex -85 KPX P Adieresis -85 KPX P Agrave -85 KPX P Amacron -85 KPX P Aogonek -85 KPX P Aring -85 KPX P Atilde -85 KPX P a -40 KPX P aacute -40 KPX P abreve -40 KPX P acircumflex -40 KPX P adieresis -40 KPX P agrave -40 KPX P amacron -40 KPX P aogonek -40 KPX P aring -40 KPX P atilde -40 KPX P comma -129 KPX P e -50 KPX P eacute -50 KPX P ecaron -50 KPX P ecircumflex -50 KPX P edieresis -50 KPX P edotaccent -50 KPX P egrave -50 KPX P emacron -50 KPX P eogonek -50 KPX P o -55 KPX P oacute -55 KPX P ocircumflex -55 KPX P odieresis -55 KPX P ograve -55 KPX P ohungarumlaut -55 KPX P omacron -55 KPX P oslash -55 KPX P otilde -55 KPX P period -129 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX R O -40 KPX R Oacute -40 KPX R Ocircumflex -40 KPX R Odieresis -40 KPX R Ograve -40 KPX R Ohungarumlaut -40 KPX R Omacron -40 KPX R Oslash -40 KPX R Otilde -40 KPX R T -30 KPX R Tcaron -30 KPX R Tcommaaccent -30 KPX R U -40 KPX R Uacute -40 KPX R Ucircumflex -40 KPX R Udieresis -40 KPX R Ugrave -40 KPX R Uhungarumlaut -40 KPX R Umacron -40 KPX R Uogonek -40 KPX R Uring -40 KPX R V -18 KPX R W -18 KPX R Y -18 KPX R Yacute -18 KPX R Ydieresis -18 KPX Racute O -40 KPX Racute Oacute -40 KPX Racute Ocircumflex -40 KPX Racute Odieresis -40 KPX Racute Ograve -40 KPX Racute Ohungarumlaut -40 KPX Racute Omacron -40 KPX Racute Oslash -40 KPX Racute Otilde -40 KPX Racute T -30 KPX Racute Tcaron -30 KPX Racute Tcommaaccent -30 KPX Racute U -40 KPX Racute Uacute -40 KPX Racute Ucircumflex -40 KPX Racute Udieresis -40 KPX Racute Ugrave -40 KPX Racute Uhungarumlaut -40 KPX Racute Umacron -40 KPX Racute Uogonek -40 KPX Racute Uring -40 KPX Racute V -18 KPX Racute W -18 KPX Racute Y -18 KPX Racute Yacute -18 KPX Racute Ydieresis -18 KPX Rcaron O -40 KPX Rcaron Oacute -40 KPX Rcaron Ocircumflex -40 KPX Rcaron Odieresis -40 KPX Rcaron Ograve -40 KPX Rcaron Ohungarumlaut -40 KPX Rcaron Omacron -40 KPX Rcaron Oslash -40 KPX Rcaron Otilde -40 KPX Rcaron T -30 KPX Rcaron Tcaron -30 KPX Rcaron Tcommaaccent -30 KPX Rcaron U -40 KPX Rcaron Uacute -40 KPX Rcaron Ucircumflex -40 KPX Rcaron Udieresis -40 KPX Rcaron Ugrave -40 KPX Rcaron Uhungarumlaut -40 KPX Rcaron Umacron -40 KPX Rcaron Uogonek -40 KPX Rcaron Uring -40 KPX Rcaron V -18 KPX Rcaron W -18 KPX Rcaron Y -18 KPX Rcaron Yacute -18 KPX Rcaron Ydieresis -18 KPX Rcommaaccent O -40 KPX Rcommaaccent Oacute -40 KPX Rcommaaccent Ocircumflex -40 KPX Rcommaaccent Odieresis -40 KPX Rcommaaccent Ograve -40 KPX Rcommaaccent Ohungarumlaut -40 KPX Rcommaaccent Omacron -40 KPX Rcommaaccent Oslash -40 KPX Rcommaaccent Otilde -40 KPX Rcommaaccent T -30 KPX Rcommaaccent Tcaron -30 KPX Rcommaaccent Tcommaaccent -30 KPX Rcommaaccent U -40 KPX Rcommaaccent Uacute -40 KPX Rcommaaccent Ucircumflex -40 KPX Rcommaaccent Udieresis -40 KPX Rcommaaccent Ugrave -40 KPX Rcommaaccent Uhungarumlaut -40 KPX Rcommaaccent Umacron -40 KPX Rcommaaccent Uogonek -40 KPX Rcommaaccent Uring -40 KPX Rcommaaccent V -18 KPX Rcommaaccent W -18 KPX Rcommaaccent Y -18 KPX Rcommaaccent Yacute -18 KPX Rcommaaccent Ydieresis -18 KPX T A -55 KPX T Aacute -55 KPX T Abreve -55 KPX T Acircumflex -55 KPX T Adieresis -55 KPX T Agrave -55 KPX T Amacron -55 KPX T Aogonek -55 KPX T Aring -55 KPX T Atilde -55 KPX T O -18 KPX T Oacute -18 KPX T Ocircumflex -18 KPX T Odieresis -18 KPX T Ograve -18 KPX T Ohungarumlaut -18 KPX T Omacron -18 KPX T Oslash -18 KPX T Otilde -18 KPX T a -92 KPX T aacute -92 KPX T abreve -92 KPX T acircumflex -92 KPX T adieresis -92 KPX T agrave -92 KPX T amacron -92 KPX T aogonek -92 KPX T aring -92 KPX T atilde -92 KPX T colon -74 KPX T comma -92 KPX T e -92 KPX T eacute -92 KPX T ecaron -92 KPX T ecircumflex -92 KPX T edieresis -52 KPX T edotaccent -92 KPX T egrave -52 KPX T emacron -52 KPX T eogonek -92 KPX T hyphen -92 KPX T i -37 KPX T iacute -37 KPX T iogonek -37 KPX T o -95 KPX T oacute -95 KPX T ocircumflex -95 KPX T odieresis -95 KPX T ograve -95 KPX T ohungarumlaut -95 KPX T omacron -95 KPX T oslash -95 KPX T otilde -95 KPX T period -92 KPX T r -37 KPX T racute -37 KPX T rcaron -37 KPX T rcommaaccent -37 KPX T semicolon -74 KPX T u -37 KPX T uacute -37 KPX T ucircumflex -37 KPX T udieresis -37 KPX T ugrave -37 KPX T uhungarumlaut -37 KPX T umacron -37 KPX T uogonek -37 KPX T uring -37 KPX T w -37 KPX T y -37 KPX T yacute -37 KPX T ydieresis -37 KPX Tcaron A -55 KPX Tcaron Aacute -55 KPX Tcaron Abreve -55 KPX Tcaron Acircumflex -55 KPX Tcaron Adieresis -55 KPX Tcaron Agrave -55 KPX Tcaron Amacron -55 KPX Tcaron Aogonek -55 KPX Tcaron Aring -55 KPX Tcaron Atilde -55 KPX Tcaron O -18 KPX Tcaron Oacute -18 KPX Tcaron Ocircumflex -18 KPX Tcaron Odieresis -18 KPX Tcaron Ograve -18 KPX Tcaron Ohungarumlaut -18 KPX Tcaron Omacron -18 KPX Tcaron Oslash -18 KPX Tcaron Otilde -18 KPX Tcaron a -92 KPX Tcaron aacute -92 KPX Tcaron abreve -92 KPX Tcaron acircumflex -92 KPX Tcaron adieresis -92 KPX Tcaron agrave -92 KPX Tcaron amacron -92 KPX Tcaron aogonek -92 KPX Tcaron aring -92 KPX Tcaron atilde -92 KPX Tcaron colon -74 KPX Tcaron comma -92 KPX Tcaron e -92 KPX Tcaron eacute -92 KPX Tcaron ecaron -92 KPX Tcaron ecircumflex -92 KPX Tcaron edieresis -52 KPX Tcaron edotaccent -92 KPX Tcaron egrave -52 KPX Tcaron emacron -52 KPX Tcaron eogonek -92 KPX Tcaron hyphen -92 KPX Tcaron i -37 KPX Tcaron iacute -37 KPX Tcaron iogonek -37 KPX Tcaron o -95 KPX Tcaron oacute -95 KPX Tcaron ocircumflex -95 KPX Tcaron odieresis -95 KPX Tcaron ograve -95 KPX Tcaron ohungarumlaut -95 KPX Tcaron omacron -95 KPX Tcaron oslash -95 KPX Tcaron otilde -95 KPX Tcaron period -92 KPX Tcaron r -37 KPX Tcaron racute -37 KPX Tcaron rcaron -37 KPX Tcaron rcommaaccent -37 KPX Tcaron semicolon -74 KPX Tcaron u -37 KPX Tcaron uacute -37 KPX Tcaron ucircumflex -37 KPX Tcaron udieresis -37 KPX Tcaron ugrave -37 KPX Tcaron uhungarumlaut -37 KPX Tcaron umacron -37 KPX Tcaron uogonek -37 KPX Tcaron uring -37 KPX Tcaron w -37 KPX Tcaron y -37 KPX Tcaron yacute -37 KPX Tcaron ydieresis -37 KPX Tcommaaccent A -55 KPX Tcommaaccent Aacute -55 KPX Tcommaaccent Abreve -55 KPX Tcommaaccent Acircumflex -55 KPX Tcommaaccent Adieresis -55 KPX Tcommaaccent Agrave -55 KPX Tcommaaccent Amacron -55 KPX Tcommaaccent Aogonek -55 KPX Tcommaaccent Aring -55 KPX Tcommaaccent Atilde -55 KPX Tcommaaccent O -18 KPX Tcommaaccent Oacute -18 KPX Tcommaaccent Ocircumflex -18 KPX Tcommaaccent Odieresis -18 KPX Tcommaaccent Ograve -18 KPX Tcommaaccent Ohungarumlaut -18 KPX Tcommaaccent Omacron -18 KPX Tcommaaccent Oslash -18 KPX Tcommaaccent Otilde -18 KPX Tcommaaccent a -92 KPX Tcommaaccent aacute -92 KPX Tcommaaccent abreve -92 KPX Tcommaaccent acircumflex -92 KPX Tcommaaccent adieresis -92 KPX Tcommaaccent agrave -92 KPX Tcommaaccent amacron -92 KPX Tcommaaccent aogonek -92 KPX Tcommaaccent aring -92 KPX Tcommaaccent atilde -92 KPX Tcommaaccent colon -74 KPX Tcommaaccent comma -92 KPX Tcommaaccent e -92 KPX Tcommaaccent eacute -92 KPX Tcommaaccent ecaron -92 KPX Tcommaaccent ecircumflex -92 KPX Tcommaaccent edieresis -52 KPX Tcommaaccent edotaccent -92 KPX Tcommaaccent egrave -52 KPX Tcommaaccent emacron -52 KPX Tcommaaccent eogonek -92 KPX Tcommaaccent hyphen -92 KPX Tcommaaccent i -37 KPX Tcommaaccent iacute -37 KPX Tcommaaccent iogonek -37 KPX Tcommaaccent o -95 KPX Tcommaaccent oacute -95 KPX Tcommaaccent ocircumflex -95 KPX Tcommaaccent odieresis -95 KPX Tcommaaccent ograve -95 KPX Tcommaaccent ohungarumlaut -95 KPX Tcommaaccent omacron -95 KPX Tcommaaccent oslash -95 KPX Tcommaaccent otilde -95 KPX Tcommaaccent period -92 KPX Tcommaaccent r -37 KPX Tcommaaccent racute -37 KPX Tcommaaccent rcaron -37 KPX Tcommaaccent rcommaaccent -37 KPX Tcommaaccent semicolon -74 KPX Tcommaaccent u -37 KPX Tcommaaccent uacute -37 KPX Tcommaaccent ucircumflex -37 KPX Tcommaaccent udieresis -37 KPX Tcommaaccent ugrave -37 KPX Tcommaaccent uhungarumlaut -37 KPX Tcommaaccent umacron -37 KPX Tcommaaccent uogonek -37 KPX Tcommaaccent uring -37 KPX Tcommaaccent w -37 KPX Tcommaaccent y -37 KPX Tcommaaccent yacute -37 KPX Tcommaaccent ydieresis -37 KPX U A -45 KPX U Aacute -45 KPX U Abreve -45 KPX U Acircumflex -45 KPX U Adieresis -45 KPX U Agrave -45 KPX U Amacron -45 KPX U Aogonek -45 KPX U Aring -45 KPX U Atilde -45 KPX Uacute A -45 KPX Uacute Aacute -45 KPX Uacute Abreve -45 KPX Uacute Acircumflex -45 KPX Uacute Adieresis -45 KPX Uacute Agrave -45 KPX Uacute Amacron -45 KPX Uacute Aogonek -45 KPX Uacute Aring -45 KPX Uacute Atilde -45 KPX Ucircumflex A -45 KPX Ucircumflex Aacute -45 KPX Ucircumflex Abreve -45 KPX Ucircumflex Acircumflex -45 KPX Ucircumflex Adieresis -45 KPX Ucircumflex Agrave -45 KPX Ucircumflex Amacron -45 KPX Ucircumflex Aogonek -45 KPX Ucircumflex Aring -45 KPX Ucircumflex Atilde -45 KPX Udieresis A -45 KPX Udieresis Aacute -45 KPX Udieresis Abreve -45 KPX Udieresis Acircumflex -45 KPX Udieresis Adieresis -45 KPX Udieresis Agrave -45 KPX Udieresis Amacron -45 KPX Udieresis Aogonek -45 KPX Udieresis Aring -45 KPX Udieresis Atilde -45 KPX Ugrave A -45 KPX Ugrave Aacute -45 KPX Ugrave Abreve -45 KPX Ugrave Acircumflex -45 KPX Ugrave Adieresis -45 KPX Ugrave Agrave -45 KPX Ugrave Amacron -45 KPX Ugrave Aogonek -45 KPX Ugrave Aring -45 KPX Ugrave Atilde -45 KPX Uhungarumlaut A -45 KPX Uhungarumlaut Aacute -45 KPX Uhungarumlaut Abreve -45 KPX Uhungarumlaut Acircumflex -45 KPX Uhungarumlaut Adieresis -45 KPX Uhungarumlaut Agrave -45 KPX Uhungarumlaut Amacron -45 KPX Uhungarumlaut Aogonek -45 KPX Uhungarumlaut Aring -45 KPX Uhungarumlaut Atilde -45 KPX Umacron A -45 KPX Umacron Aacute -45 KPX Umacron Abreve -45 KPX Umacron Acircumflex -45 KPX Umacron Adieresis -45 KPX Umacron Agrave -45 KPX Umacron Amacron -45 KPX Umacron Aogonek -45 KPX Umacron Aring -45 KPX Umacron Atilde -45 KPX Uogonek A -45 KPX Uogonek Aacute -45 KPX Uogonek Abreve -45 KPX Uogonek Acircumflex -45 KPX Uogonek Adieresis -45 KPX Uogonek Agrave -45 KPX Uogonek Amacron -45 KPX Uogonek Aogonek -45 KPX Uogonek Aring -45 KPX Uogonek Atilde -45 KPX Uring A -45 KPX Uring Aacute -45 KPX Uring Abreve -45 KPX Uring Acircumflex -45 KPX Uring Adieresis -45 KPX Uring Agrave -45 KPX Uring Amacron -45 KPX Uring Aogonek -45 KPX Uring Aring -45 KPX Uring Atilde -45 KPX V A -85 KPX V Aacute -85 KPX V Abreve -85 KPX V Acircumflex -85 KPX V Adieresis -85 KPX V Agrave -85 KPX V Amacron -85 KPX V Aogonek -85 KPX V Aring -85 KPX V Atilde -85 KPX V G -10 KPX V Gbreve -10 KPX V Gcommaaccent -10 KPX V O -30 KPX V Oacute -30 KPX V Ocircumflex -30 KPX V Odieresis -30 KPX V Ograve -30 KPX V Ohungarumlaut -30 KPX V Omacron -30 KPX V Oslash -30 KPX V Otilde -30 KPX V a -111 KPX V aacute -111 KPX V abreve -111 KPX V acircumflex -111 KPX V adieresis -111 KPX V agrave -111 KPX V amacron -111 KPX V aogonek -111 KPX V aring -111 KPX V atilde -111 KPX V colon -74 KPX V comma -129 KPX V e -111 KPX V eacute -111 KPX V ecaron -111 KPX V ecircumflex -111 KPX V edieresis -71 KPX V edotaccent -111 KPX V egrave -71 KPX V emacron -71 KPX V eogonek -111 KPX V hyphen -70 KPX V i -55 KPX V iacute -55 KPX V iogonek -55 KPX V o -111 KPX V oacute -111 KPX V ocircumflex -111 KPX V odieresis -111 KPX V ograve -111 KPX V ohungarumlaut -111 KPX V omacron -111 KPX V oslash -111 KPX V otilde -111 KPX V period -129 KPX V semicolon -74 KPX V u -55 KPX V uacute -55 KPX V ucircumflex -55 KPX V udieresis -55 KPX V ugrave -55 KPX V uhungarumlaut -55 KPX V umacron -55 KPX V uogonek -55 KPX V uring -55 KPX W A -74 KPX W Aacute -74 KPX W Abreve -74 KPX W Acircumflex -74 KPX W Adieresis -74 KPX W Agrave -74 KPX W Amacron -74 KPX W Aogonek -74 KPX W Aring -74 KPX W Atilde -74 KPX W O -15 KPX W Oacute -15 KPX W Ocircumflex -15 KPX W Odieresis -15 KPX W Ograve -15 KPX W Ohungarumlaut -15 KPX W Omacron -15 KPX W Oslash -15 KPX W Otilde -15 KPX W a -85 KPX W aacute -85 KPX W abreve -85 KPX W acircumflex -85 KPX W adieresis -85 KPX W agrave -85 KPX W amacron -85 KPX W aogonek -85 KPX W aring -85 KPX W atilde -85 KPX W colon -55 KPX W comma -74 KPX W e -90 KPX W eacute -90 KPX W ecaron -90 KPX W ecircumflex -90 KPX W edieresis -50 KPX W edotaccent -90 KPX W egrave -50 KPX W emacron -50 KPX W eogonek -90 KPX W hyphen -50 KPX W i -37 KPX W iacute -37 KPX W iogonek -37 KPX W o -80 KPX W oacute -80 KPX W ocircumflex -80 KPX W odieresis -80 KPX W ograve -80 KPX W ohungarumlaut -80 KPX W omacron -80 KPX W oslash -80 KPX W otilde -80 KPX W period -74 KPX W semicolon -55 KPX W u -55 KPX W uacute -55 KPX W ucircumflex -55 KPX W udieresis -55 KPX W ugrave -55 KPX W uhungarumlaut -55 KPX W umacron -55 KPX W uogonek -55 KPX W uring -55 KPX W y -55 KPX W yacute -55 KPX W ydieresis -55 KPX Y A -74 KPX Y Aacute -74 KPX Y Abreve -74 KPX Y Acircumflex -74 KPX Y Adieresis -74 KPX Y Agrave -74 KPX Y Amacron -74 KPX Y Aogonek -74 KPX Y Aring -74 KPX Y Atilde -74 KPX Y O -25 KPX Y Oacute -25 KPX Y Ocircumflex -25 KPX Y Odieresis -25 KPX Y Ograve -25 KPX Y Ohungarumlaut -25 KPX Y Omacron -25 KPX Y Oslash -25 KPX Y Otilde -25 KPX Y a -92 KPX Y aacute -92 KPX Y abreve -92 KPX Y acircumflex -92 KPX Y adieresis -92 KPX Y agrave -92 KPX Y amacron -92 KPX Y aogonek -92 KPX Y aring -92 KPX Y atilde -92 KPX Y colon -92 KPX Y comma -92 KPX Y e -111 KPX Y eacute -111 KPX Y ecaron -111 KPX Y ecircumflex -71 KPX Y edieresis -71 KPX Y edotaccent -111 KPX Y egrave -71 KPX Y emacron -71 KPX Y eogonek -111 KPX Y hyphen -92 KPX Y i -55 KPX Y iacute -55 KPX Y iogonek -55 KPX Y o -111 KPX Y oacute -111 KPX Y ocircumflex -111 KPX Y odieresis -111 KPX Y ograve -111 KPX Y ohungarumlaut -111 KPX Y omacron -111 KPX Y oslash -111 KPX Y otilde -111 KPX Y period -74 KPX Y semicolon -92 KPX Y u -92 KPX Y uacute -92 KPX Y ucircumflex -92 KPX Y udieresis -92 KPX Y ugrave -92 KPX Y uhungarumlaut -92 KPX Y umacron -92 KPX Y uogonek -92 KPX Y uring -92 KPX Yacute A -74 KPX Yacute Aacute -74 KPX Yacute Abreve -74 KPX Yacute Acircumflex -74 KPX Yacute Adieresis -74 KPX Yacute Agrave -74 KPX Yacute Amacron -74 KPX Yacute Aogonek -74 KPX Yacute Aring -74 KPX Yacute Atilde -74 KPX Yacute O -25 KPX Yacute Oacute -25 KPX Yacute Ocircumflex -25 KPX Yacute Odieresis -25 KPX Yacute Ograve -25 KPX Yacute Ohungarumlaut -25 KPX Yacute Omacron -25 KPX Yacute Oslash -25 KPX Yacute Otilde -25 KPX Yacute a -92 KPX Yacute aacute -92 KPX Yacute abreve -92 KPX Yacute acircumflex -92 KPX Yacute adieresis -92 KPX Yacute agrave -92 KPX Yacute amacron -92 KPX Yacute aogonek -92 KPX Yacute aring -92 KPX Yacute atilde -92 KPX Yacute colon -92 KPX Yacute comma -92 KPX Yacute e -111 KPX Yacute eacute -111 KPX Yacute ecaron -111 KPX Yacute ecircumflex -71 KPX Yacute edieresis -71 KPX Yacute edotaccent -111 KPX Yacute egrave -71 KPX Yacute emacron -71 KPX Yacute eogonek -111 KPX Yacute hyphen -92 KPX Yacute i -55 KPX Yacute iacute -55 KPX Yacute iogonek -55 KPX Yacute o -111 KPX Yacute oacute -111 KPX Yacute ocircumflex -111 KPX Yacute odieresis -111 KPX Yacute ograve -111 KPX Yacute ohungarumlaut -111 KPX Yacute omacron -111 KPX Yacute oslash -111 KPX Yacute otilde -111 KPX Yacute period -74 KPX Yacute semicolon -92 KPX Yacute u -92 KPX Yacute uacute -92 KPX Yacute ucircumflex -92 KPX Yacute udieresis -92 KPX Yacute ugrave -92 KPX Yacute uhungarumlaut -92 KPX Yacute umacron -92 KPX Yacute uogonek -92 KPX Yacute uring -92 KPX Ydieresis A -74 KPX Ydieresis Aacute -74 KPX Ydieresis Abreve -74 KPX Ydieresis Acircumflex -74 KPX Ydieresis Adieresis -74 KPX Ydieresis Agrave -74 KPX Ydieresis Amacron -74 KPX Ydieresis Aogonek -74 KPX Ydieresis Aring -74 KPX Ydieresis Atilde -74 KPX Ydieresis O -25 KPX Ydieresis Oacute -25 KPX Ydieresis Ocircumflex -25 KPX Ydieresis Odieresis -25 KPX Ydieresis Ograve -25 KPX Ydieresis Ohungarumlaut -25 KPX Ydieresis Omacron -25 KPX Ydieresis Oslash -25 KPX Ydieresis Otilde -25 KPX Ydieresis a -92 KPX Ydieresis aacute -92 KPX Ydieresis abreve -92 KPX Ydieresis acircumflex -92 KPX Ydieresis adieresis -92 KPX Ydieresis agrave -92 KPX Ydieresis amacron -92 KPX Ydieresis aogonek -92 KPX Ydieresis aring -92 KPX Ydieresis atilde -92 KPX Ydieresis colon -92 KPX Ydieresis comma -92 KPX Ydieresis e -111 KPX Ydieresis eacute -111 KPX Ydieresis ecaron -111 KPX Ydieresis ecircumflex -71 KPX Ydieresis edieresis -71 KPX Ydieresis edotaccent -111 KPX Ydieresis egrave -71 KPX Ydieresis emacron -71 KPX Ydieresis eogonek -111 KPX Ydieresis hyphen -92 KPX Ydieresis i -55 KPX Ydieresis iacute -55 KPX Ydieresis iogonek -55 KPX Ydieresis o -111 KPX Ydieresis oacute -111 KPX Ydieresis ocircumflex -111 KPX Ydieresis odieresis -111 KPX Ydieresis ograve -111 KPX Ydieresis ohungarumlaut -111 KPX Ydieresis omacron -111 KPX Ydieresis oslash -111 KPX Ydieresis otilde -111 KPX Ydieresis period -74 KPX Ydieresis semicolon -92 KPX Ydieresis u -92 KPX Ydieresis uacute -92 KPX Ydieresis ucircumflex -92 KPX Ydieresis udieresis -92 KPX Ydieresis ugrave -92 KPX Ydieresis uhungarumlaut -92 KPX Ydieresis umacron -92 KPX Ydieresis uogonek -92 KPX Ydieresis uring -92 KPX b b -10 KPX b period -40 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX c h -10 KPX c k -10 KPX c kcommaaccent -10 KPX cacute h -10 KPX cacute k -10 KPX cacute kcommaaccent -10 KPX ccaron h -10 KPX ccaron k -10 KPX ccaron kcommaaccent -10 KPX ccedilla h -10 KPX ccedilla k -10 KPX ccedilla kcommaaccent -10 KPX comma quotedblright -95 KPX comma quoteright -95 KPX e b -10 KPX eacute b -10 KPX ecaron b -10 KPX ecircumflex b -10 KPX edieresis b -10 KPX edotaccent b -10 KPX egrave b -10 KPX emacron b -10 KPX eogonek b -10 KPX f comma -10 KPX f dotlessi -30 KPX f e -10 KPX f eacute -10 KPX f edotaccent -10 KPX f eogonek -10 KPX f f -18 KPX f o -10 KPX f oacute -10 KPX f ocircumflex -10 KPX f ograve -10 KPX f ohungarumlaut -10 KPX f oslash -10 KPX f otilde -10 KPX f period -10 KPX f quoteright 55 KPX k e -30 KPX k eacute -30 KPX k ecaron -30 KPX k ecircumflex -30 KPX k edieresis -30 KPX k edotaccent -30 KPX k egrave -30 KPX k emacron -30 KPX k eogonek -30 KPX k o -10 KPX k oacute -10 KPX k ocircumflex -10 KPX k odieresis -10 KPX k ograve -10 KPX k ohungarumlaut -10 KPX k omacron -10 KPX k oslash -10 KPX k otilde -10 KPX kcommaaccent e -30 KPX kcommaaccent eacute -30 KPX kcommaaccent ecaron -30 KPX kcommaaccent ecircumflex -30 KPX kcommaaccent edieresis -30 KPX kcommaaccent edotaccent -30 KPX kcommaaccent egrave -30 KPX kcommaaccent emacron -30 KPX kcommaaccent eogonek -30 KPX kcommaaccent o -10 KPX kcommaaccent oacute -10 KPX kcommaaccent ocircumflex -10 KPX kcommaaccent odieresis -10 KPX kcommaaccent ograve -10 KPX kcommaaccent ohungarumlaut -10 KPX kcommaaccent omacron -10 KPX kcommaaccent oslash -10 KPX kcommaaccent otilde -10 KPX n v -40 KPX nacute v -40 KPX ncaron v -40 KPX ncommaaccent v -40 KPX ntilde v -40 KPX o v -15 KPX o w -25 KPX o x -10 KPX o y -10 KPX o yacute -10 KPX o ydieresis -10 KPX oacute v -15 KPX oacute w -25 KPX oacute x -10 KPX oacute y -10 KPX oacute yacute -10 KPX oacute ydieresis -10 KPX ocircumflex v -15 KPX ocircumflex w -25 KPX ocircumflex x -10 KPX ocircumflex y -10 KPX ocircumflex yacute -10 KPX ocircumflex ydieresis -10 KPX odieresis v -15 KPX odieresis w -25 KPX odieresis x -10 KPX odieresis y -10 KPX odieresis yacute -10 KPX odieresis ydieresis -10 KPX ograve v -15 KPX ograve w -25 KPX ograve x -10 KPX ograve y -10 KPX ograve yacute -10 KPX ograve ydieresis -10 KPX ohungarumlaut v -15 KPX ohungarumlaut w -25 KPX ohungarumlaut x -10 KPX ohungarumlaut y -10 KPX ohungarumlaut yacute -10 KPX ohungarumlaut ydieresis -10 KPX omacron v -15 KPX omacron w -25 KPX omacron x -10 KPX omacron y -10 KPX omacron yacute -10 KPX omacron ydieresis -10 KPX oslash v -15 KPX oslash w -25 KPX oslash x -10 KPX oslash y -10 KPX oslash yacute -10 KPX oslash ydieresis -10 KPX otilde v -15 KPX otilde w -25 KPX otilde x -10 KPX otilde y -10 KPX otilde yacute -10 KPX otilde ydieresis -10 KPX period quotedblright -95 KPX period quoteright -95 KPX quoteleft quoteleft -74 KPX quoteright d -15 KPX quoteright dcroat -15 KPX quoteright quoteright -74 KPX quoteright r -15 KPX quoteright racute -15 KPX quoteright rcaron -15 KPX quoteright rcommaaccent -15 KPX quoteright s -74 KPX quoteright sacute -74 KPX quoteright scaron -74 KPX quoteright scedilla -74 KPX quoteright scommaaccent -74 KPX quoteright space -74 KPX quoteright t -37 KPX quoteright tcommaaccent -37 KPX quoteright v -15 KPX r comma -65 KPX r period -65 KPX racute comma -65 KPX racute period -65 KPX rcaron comma -65 KPX rcaron period -65 KPX rcommaaccent comma -65 KPX rcommaaccent period -65 KPX space A -37 KPX space Aacute -37 KPX space Abreve -37 KPX space Acircumflex -37 KPX space Adieresis -37 KPX space Agrave -37 KPX space Amacron -37 KPX space Aogonek -37 KPX space Aring -37 KPX space Atilde -37 KPX space V -70 KPX space W -70 KPX space Y -70 KPX space Yacute -70 KPX space Ydieresis -70 KPX v comma -37 KPX v e -15 KPX v eacute -15 KPX v ecaron -15 KPX v ecircumflex -15 KPX v edieresis -15 KPX v edotaccent -15 KPX v egrave -15 KPX v emacron -15 KPX v eogonek -15 KPX v o -15 KPX v oacute -15 KPX v ocircumflex -15 KPX v odieresis -15 KPX v ograve -15 KPX v ohungarumlaut -15 KPX v omacron -15 KPX v oslash -15 KPX v otilde -15 KPX v period -37 KPX w a -10 KPX w aacute -10 KPX w abreve -10 KPX w acircumflex -10 KPX w adieresis -10 KPX w agrave -10 KPX w amacron -10 KPX w aogonek -10 KPX w aring -10 KPX w atilde -10 KPX w comma -37 KPX w e -10 KPX w eacute -10 KPX w ecaron -10 KPX w ecircumflex -10 KPX w edieresis -10 KPX w edotaccent -10 KPX w egrave -10 KPX w emacron -10 KPX w eogonek -10 KPX w o -15 KPX w oacute -15 KPX w ocircumflex -15 KPX w odieresis -15 KPX w ograve -15 KPX w ohungarumlaut -15 KPX w omacron -15 KPX w oslash -15 KPX w otilde -15 KPX w period -37 KPX x e -10 KPX x eacute -10 KPX x ecaron -10 KPX x ecircumflex -10 KPX x edieresis -10 KPX x edotaccent -10 KPX x egrave -10 KPX x emacron -10 KPX x eogonek -10 KPX y comma -37 KPX y period -37 KPX yacute comma -37 KPX yacute period -37 KPX ydieresis comma -37 KPX ydieresis period -37 EndKernPairs EndKernData EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Times-Italic.afm000066400000000000000000002066031320103431700275160ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 12:56:55 1997 Comment UniqueID 43067 Comment VMusage 47727 58752 FontName Times-Italic FullName Times Italic FamilyName Times Weight Medium ItalicAngle -15.5 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -169 -217 1010 883 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme AdobeStandardEncoding CapHeight 653 XHeight 441 Ascender 683 Descender -217 StdHW 32 StdVW 76 StartCharMetrics 315 C 32 ; WX 250 ; N space ; B 0 0 0 0 ; C 33 ; WX 333 ; N exclam ; B 39 -11 302 667 ; C 34 ; WX 420 ; N quotedbl ; B 144 421 432 666 ; C 35 ; WX 500 ; N numbersign ; B 2 0 540 676 ; C 36 ; WX 500 ; N dollar ; B 31 -89 497 731 ; C 37 ; WX 833 ; N percent ; B 79 -13 790 676 ; C 38 ; WX 778 ; N ampersand ; B 76 -18 723 666 ; C 39 ; WX 333 ; N quoteright ; B 151 436 290 666 ; C 40 ; WX 333 ; N parenleft ; B 42 -181 315 669 ; C 41 ; WX 333 ; N parenright ; B 16 -180 289 669 ; C 42 ; WX 500 ; N asterisk ; B 128 255 492 666 ; C 43 ; WX 675 ; N plus ; B 86 0 590 506 ; C 44 ; WX 250 ; N comma ; B -4 -129 135 101 ; C 45 ; WX 333 ; N hyphen ; B 49 192 282 255 ; C 46 ; WX 250 ; N period ; B 27 -11 138 100 ; C 47 ; WX 278 ; N slash ; B -65 -18 386 666 ; C 48 ; WX 500 ; N zero ; B 32 -7 497 676 ; C 49 ; WX 500 ; N one ; B 49 0 409 676 ; C 50 ; WX 500 ; N two ; B 12 0 452 676 ; C 51 ; WX 500 ; N three ; B 15 -7 465 676 ; C 52 ; WX 500 ; N four ; B 1 0 479 676 ; C 53 ; WX 500 ; N five ; B 15 -7 491 666 ; C 54 ; WX 500 ; N six ; B 30 -7 521 686 ; C 55 ; WX 500 ; N seven ; B 75 -8 537 666 ; C 56 ; WX 500 ; N eight ; B 30 -7 493 676 ; C 57 ; WX 500 ; N nine ; B 23 -17 492 676 ; C 58 ; WX 333 ; N colon ; B 50 -11 261 441 ; C 59 ; WX 333 ; N semicolon ; B 27 -129 261 441 ; C 60 ; WX 675 ; N less ; B 84 -8 592 514 ; C 61 ; WX 675 ; N equal ; B 86 120 590 386 ; C 62 ; WX 675 ; N greater ; B 84 -8 592 514 ; C 63 ; WX 500 ; N question ; B 132 -12 472 664 ; C 64 ; WX 920 ; N at ; B 118 -18 806 666 ; C 65 ; WX 611 ; N A ; B -51 0 564 668 ; C 66 ; WX 611 ; N B ; B -8 0 588 653 ; C 67 ; WX 667 ; N C ; B 66 -18 689 666 ; C 68 ; WX 722 ; N D ; B -8 0 700 653 ; C 69 ; WX 611 ; N E ; B -1 0 634 653 ; C 70 ; WX 611 ; N F ; B 8 0 645 653 ; C 71 ; WX 722 ; N G ; B 52 -18 722 666 ; C 72 ; WX 722 ; N H ; B -8 0 767 653 ; C 73 ; WX 333 ; N I ; B -8 0 384 653 ; C 74 ; WX 444 ; N J ; B -6 -18 491 653 ; C 75 ; WX 667 ; N K ; B 7 0 722 653 ; C 76 ; WX 556 ; N L ; B -8 0 559 653 ; C 77 ; WX 833 ; N M ; B -18 0 873 653 ; C 78 ; WX 667 ; N N ; B -20 -15 727 653 ; C 79 ; WX 722 ; N O ; B 60 -18 699 666 ; C 80 ; WX 611 ; N P ; B 0 0 605 653 ; C 81 ; WX 722 ; N Q ; B 59 -182 699 666 ; C 82 ; WX 611 ; N R ; B -13 0 588 653 ; C 83 ; WX 500 ; N S ; B 17 -18 508 667 ; C 84 ; WX 556 ; N T ; B 59 0 633 653 ; C 85 ; WX 722 ; N U ; B 102 -18 765 653 ; C 86 ; WX 611 ; N V ; B 76 -18 688 653 ; C 87 ; WX 833 ; N W ; B 71 -18 906 653 ; C 88 ; WX 611 ; N X ; B -29 0 655 653 ; C 89 ; WX 556 ; N Y ; B 78 0 633 653 ; C 90 ; WX 556 ; N Z ; B -6 0 606 653 ; C 91 ; WX 389 ; N bracketleft ; B 21 -153 391 663 ; C 92 ; WX 278 ; N backslash ; B -41 -18 319 666 ; C 93 ; WX 389 ; N bracketright ; B 12 -153 382 663 ; C 94 ; WX 422 ; N asciicircum ; B 0 301 422 666 ; C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; C 96 ; WX 333 ; N quoteleft ; B 171 436 310 666 ; C 97 ; WX 500 ; N a ; B 17 -11 476 441 ; C 98 ; WX 500 ; N b ; B 23 -11 473 683 ; C 99 ; WX 444 ; N c ; B 30 -11 425 441 ; C 100 ; WX 500 ; N d ; B 15 -13 527 683 ; C 101 ; WX 444 ; N e ; B 31 -11 412 441 ; C 102 ; WX 278 ; N f ; B -147 -207 424 678 ; L i fi ; L l fl ; C 103 ; WX 500 ; N g ; B 8 -206 472 441 ; C 104 ; WX 500 ; N h ; B 19 -9 478 683 ; C 105 ; WX 278 ; N i ; B 49 -11 264 654 ; C 106 ; WX 278 ; N j ; B -124 -207 276 654 ; C 107 ; WX 444 ; N k ; B 14 -11 461 683 ; C 108 ; WX 278 ; N l ; B 41 -11 279 683 ; C 109 ; WX 722 ; N m ; B 12 -9 704 441 ; C 110 ; WX 500 ; N n ; B 14 -9 474 441 ; C 111 ; WX 500 ; N o ; B 27 -11 468 441 ; C 112 ; WX 500 ; N p ; B -75 -205 469 441 ; C 113 ; WX 500 ; N q ; B 25 -209 483 441 ; C 114 ; WX 389 ; N r ; B 45 0 412 441 ; C 115 ; WX 389 ; N s ; B 16 -13 366 442 ; C 116 ; WX 278 ; N t ; B 37 -11 296 546 ; C 117 ; WX 500 ; N u ; B 42 -11 475 441 ; C 118 ; WX 444 ; N v ; B 21 -18 426 441 ; C 119 ; WX 667 ; N w ; B 16 -18 648 441 ; C 120 ; WX 444 ; N x ; B -27 -11 447 441 ; C 121 ; WX 444 ; N y ; B -24 -206 426 441 ; C 122 ; WX 389 ; N z ; B -2 -81 380 428 ; C 123 ; WX 400 ; N braceleft ; B 51 -177 407 687 ; C 124 ; WX 275 ; N bar ; B 105 -217 171 783 ; C 125 ; WX 400 ; N braceright ; B -7 -177 349 687 ; C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ; C 161 ; WX 389 ; N exclamdown ; B 59 -205 322 473 ; C 162 ; WX 500 ; N cent ; B 77 -143 472 560 ; C 163 ; WX 500 ; N sterling ; B 10 -6 517 670 ; C 164 ; WX 167 ; N fraction ; B -169 -10 337 676 ; C 165 ; WX 500 ; N yen ; B 27 0 603 653 ; C 166 ; WX 500 ; N florin ; B 25 -182 507 682 ; C 167 ; WX 500 ; N section ; B 53 -162 461 666 ; C 168 ; WX 500 ; N currency ; B -22 53 522 597 ; C 169 ; WX 214 ; N quotesingle ; B 132 421 241 666 ; C 170 ; WX 556 ; N quotedblleft ; B 166 436 514 666 ; C 171 ; WX 500 ; N guillemotleft ; B 53 37 445 403 ; C 172 ; WX 333 ; N guilsinglleft ; B 51 37 281 403 ; C 173 ; WX 333 ; N guilsinglright ; B 52 37 282 403 ; C 174 ; WX 500 ; N fi ; B -141 -207 481 681 ; C 175 ; WX 500 ; N fl ; B -141 -204 518 682 ; C 177 ; WX 500 ; N endash ; B -6 197 505 243 ; C 178 ; WX 500 ; N dagger ; B 101 -159 488 666 ; C 179 ; WX 500 ; N daggerdbl ; B 22 -143 491 666 ; C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ; C 182 ; WX 523 ; N paragraph ; B 55 -123 616 653 ; C 183 ; WX 350 ; N bullet ; B 40 191 310 461 ; C 184 ; WX 333 ; N quotesinglbase ; B 44 -129 183 101 ; C 185 ; WX 556 ; N quotedblbase ; B 57 -129 405 101 ; C 186 ; WX 556 ; N quotedblright ; B 151 436 499 666 ; C 187 ; WX 500 ; N guillemotright ; B 55 37 447 403 ; C 188 ; WX 889 ; N ellipsis ; B 57 -11 762 100 ; C 189 ; WX 1000 ; N perthousand ; B 25 -19 1010 706 ; C 191 ; WX 500 ; N questiondown ; B 28 -205 368 471 ; C 193 ; WX 333 ; N grave ; B 121 492 311 664 ; C 194 ; WX 333 ; N acute ; B 180 494 403 664 ; C 195 ; WX 333 ; N circumflex ; B 91 492 385 661 ; C 196 ; WX 333 ; N tilde ; B 100 517 427 624 ; C 197 ; WX 333 ; N macron ; B 99 532 411 583 ; C 198 ; WX 333 ; N breve ; B 117 492 418 650 ; C 199 ; WX 333 ; N dotaccent ; B 207 548 305 646 ; C 200 ; WX 333 ; N dieresis ; B 107 548 405 646 ; C 202 ; WX 333 ; N ring ; B 155 492 355 691 ; C 203 ; WX 333 ; N cedilla ; B -30 -217 182 0 ; C 205 ; WX 333 ; N hungarumlaut ; B 93 494 486 664 ; C 206 ; WX 333 ; N ogonek ; B 20 -169 203 40 ; C 207 ; WX 333 ; N caron ; B 121 492 426 661 ; C 208 ; WX 889 ; N emdash ; B -6 197 894 243 ; C 225 ; WX 889 ; N AE ; B -27 0 911 653 ; C 227 ; WX 276 ; N ordfeminine ; B 42 406 352 676 ; C 232 ; WX 556 ; N Lslash ; B -8 0 559 653 ; C 233 ; WX 722 ; N Oslash ; B 60 -105 699 722 ; C 234 ; WX 944 ; N OE ; B 49 -8 964 666 ; C 235 ; WX 310 ; N ordmasculine ; B 67 406 362 676 ; C 241 ; WX 667 ; N ae ; B 23 -11 640 441 ; C 245 ; WX 278 ; N dotlessi ; B 49 -11 235 441 ; C 248 ; WX 278 ; N lslash ; B 41 -11 312 683 ; C 249 ; WX 500 ; N oslash ; B 28 -135 469 554 ; C 250 ; WX 667 ; N oe ; B 20 -12 646 441 ; C 251 ; WX 500 ; N germandbls ; B -168 -207 493 679 ; C -1 ; WX 333 ; N Idieresis ; B -8 0 435 818 ; C -1 ; WX 444 ; N eacute ; B 31 -11 459 664 ; C -1 ; WX 500 ; N abreve ; B 17 -11 502 650 ; C -1 ; WX 500 ; N uhungarumlaut ; B 42 -11 580 664 ; C -1 ; WX 444 ; N ecaron ; B 31 -11 482 661 ; C -1 ; WX 556 ; N Ydieresis ; B 78 0 633 818 ; C -1 ; WX 675 ; N divide ; B 86 -11 590 517 ; C -1 ; WX 556 ; N Yacute ; B 78 0 633 876 ; C -1 ; WX 611 ; N Acircumflex ; B -51 0 564 873 ; C -1 ; WX 500 ; N aacute ; B 17 -11 487 664 ; C -1 ; WX 722 ; N Ucircumflex ; B 102 -18 765 873 ; C -1 ; WX 444 ; N yacute ; B -24 -206 459 664 ; C -1 ; WX 389 ; N scommaaccent ; B 16 -217 366 442 ; C -1 ; WX 444 ; N ecircumflex ; B 31 -11 441 661 ; C -1 ; WX 722 ; N Uring ; B 102 -18 765 883 ; C -1 ; WX 722 ; N Udieresis ; B 102 -18 765 818 ; C -1 ; WX 500 ; N aogonek ; B 17 -169 476 441 ; C -1 ; WX 722 ; N Uacute ; B 102 -18 765 876 ; C -1 ; WX 500 ; N uogonek ; B 42 -169 477 441 ; C -1 ; WX 611 ; N Edieresis ; B -1 0 634 818 ; C -1 ; WX 722 ; N Dcroat ; B -8 0 700 653 ; C -1 ; WX 250 ; N commaaccent ; B 8 -217 133 -50 ; C -1 ; WX 760 ; N copyright ; B 41 -18 719 666 ; C -1 ; WX 611 ; N Emacron ; B -1 0 634 795 ; C -1 ; WX 444 ; N ccaron ; B 30 -11 482 661 ; C -1 ; WX 500 ; N aring ; B 17 -11 476 691 ; C -1 ; WX 667 ; N Ncommaaccent ; B -20 -187 727 653 ; C -1 ; WX 278 ; N lacute ; B 41 -11 395 876 ; C -1 ; WX 500 ; N agrave ; B 17 -11 476 664 ; C -1 ; WX 556 ; N Tcommaaccent ; B 59 -217 633 653 ; C -1 ; WX 667 ; N Cacute ; B 66 -18 690 876 ; C -1 ; WX 500 ; N atilde ; B 17 -11 511 624 ; C -1 ; WX 611 ; N Edotaccent ; B -1 0 634 818 ; C -1 ; WX 389 ; N scaron ; B 16 -13 454 661 ; C -1 ; WX 389 ; N scedilla ; B 16 -217 366 442 ; C -1 ; WX 278 ; N iacute ; B 49 -11 355 664 ; C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ; C -1 ; WX 611 ; N Rcaron ; B -13 0 588 873 ; C -1 ; WX 722 ; N Gcommaaccent ; B 52 -217 722 666 ; C -1 ; WX 500 ; N ucircumflex ; B 42 -11 475 661 ; C -1 ; WX 500 ; N acircumflex ; B 17 -11 476 661 ; C -1 ; WX 611 ; N Amacron ; B -51 0 564 795 ; C -1 ; WX 389 ; N rcaron ; B 45 0 434 661 ; C -1 ; WX 444 ; N ccedilla ; B 30 -217 425 441 ; C -1 ; WX 556 ; N Zdotaccent ; B -6 0 606 818 ; C -1 ; WX 611 ; N Thorn ; B 0 0 569 653 ; C -1 ; WX 722 ; N Omacron ; B 60 -18 699 795 ; C -1 ; WX 611 ; N Racute ; B -13 0 588 876 ; C -1 ; WX 500 ; N Sacute ; B 17 -18 508 876 ; C -1 ; WX 544 ; N dcaron ; B 15 -13 658 683 ; C -1 ; WX 722 ; N Umacron ; B 102 -18 765 795 ; C -1 ; WX 500 ; N uring ; B 42 -11 475 691 ; C -1 ; WX 300 ; N threesuperior ; B 43 268 339 676 ; C -1 ; WX 722 ; N Ograve ; B 60 -18 699 876 ; C -1 ; WX 611 ; N Agrave ; B -51 0 564 876 ; C -1 ; WX 611 ; N Abreve ; B -51 0 564 862 ; C -1 ; WX 675 ; N multiply ; B 93 8 582 497 ; C -1 ; WX 500 ; N uacute ; B 42 -11 477 664 ; C -1 ; WX 556 ; N Tcaron ; B 59 0 633 873 ; C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ; C -1 ; WX 444 ; N ydieresis ; B -24 -206 441 606 ; C -1 ; WX 667 ; N Nacute ; B -20 -15 727 876 ; C -1 ; WX 278 ; N icircumflex ; B 33 -11 327 661 ; C -1 ; WX 611 ; N Ecircumflex ; B -1 0 634 873 ; C -1 ; WX 500 ; N adieresis ; B 17 -11 489 606 ; C -1 ; WX 444 ; N edieresis ; B 31 -11 451 606 ; C -1 ; WX 444 ; N cacute ; B 30 -11 459 664 ; C -1 ; WX 500 ; N nacute ; B 14 -9 477 664 ; C -1 ; WX 500 ; N umacron ; B 42 -11 485 583 ; C -1 ; WX 667 ; N Ncaron ; B -20 -15 727 873 ; C -1 ; WX 333 ; N Iacute ; B -8 0 433 876 ; C -1 ; WX 675 ; N plusminus ; B 86 0 590 506 ; C -1 ; WX 275 ; N brokenbar ; B 105 -142 171 708 ; C -1 ; WX 760 ; N registered ; B 41 -18 719 666 ; C -1 ; WX 722 ; N Gbreve ; B 52 -18 722 862 ; C -1 ; WX 333 ; N Idotaccent ; B -8 0 384 818 ; C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ; C -1 ; WX 611 ; N Egrave ; B -1 0 634 876 ; C -1 ; WX 389 ; N racute ; B 45 0 431 664 ; C -1 ; WX 500 ; N omacron ; B 27 -11 495 583 ; C -1 ; WX 556 ; N Zacute ; B -6 0 606 876 ; C -1 ; WX 556 ; N Zcaron ; B -6 0 606 873 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 523 658 ; C -1 ; WX 722 ; N Eth ; B -8 0 700 653 ; C -1 ; WX 667 ; N Ccedilla ; B 66 -217 689 666 ; C -1 ; WX 278 ; N lcommaaccent ; B 22 -217 279 683 ; C -1 ; WX 300 ; N tcaron ; B 37 -11 407 681 ; C -1 ; WX 444 ; N eogonek ; B 31 -169 412 441 ; C -1 ; WX 722 ; N Uogonek ; B 102 -184 765 653 ; C -1 ; WX 611 ; N Aacute ; B -51 0 564 876 ; C -1 ; WX 611 ; N Adieresis ; B -51 0 564 818 ; C -1 ; WX 444 ; N egrave ; B 31 -11 412 664 ; C -1 ; WX 389 ; N zacute ; B -2 -81 431 664 ; C -1 ; WX 278 ; N iogonek ; B 49 -169 264 654 ; C -1 ; WX 722 ; N Oacute ; B 60 -18 699 876 ; C -1 ; WX 500 ; N oacute ; B 27 -11 487 664 ; C -1 ; WX 500 ; N amacron ; B 17 -11 495 583 ; C -1 ; WX 389 ; N sacute ; B 16 -13 431 664 ; C -1 ; WX 278 ; N idieresis ; B 49 -11 352 606 ; C -1 ; WX 722 ; N Ocircumflex ; B 60 -18 699 873 ; C -1 ; WX 722 ; N Ugrave ; B 102 -18 765 876 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C -1 ; WX 500 ; N thorn ; B -75 -205 469 683 ; C -1 ; WX 300 ; N twosuperior ; B 33 271 324 676 ; C -1 ; WX 722 ; N Odieresis ; B 60 -18 699 818 ; C -1 ; WX 500 ; N mu ; B -30 -209 497 428 ; C -1 ; WX 278 ; N igrave ; B 49 -11 284 664 ; C -1 ; WX 500 ; N ohungarumlaut ; B 27 -11 590 664 ; C -1 ; WX 611 ; N Eogonek ; B -1 -169 634 653 ; C -1 ; WX 500 ; N dcroat ; B 15 -13 572 683 ; C -1 ; WX 750 ; N threequarters ; B 23 -10 736 676 ; C -1 ; WX 500 ; N Scedilla ; B 17 -217 508 667 ; C -1 ; WX 300 ; N lcaron ; B 41 -11 407 683 ; C -1 ; WX 667 ; N Kcommaaccent ; B 7 -217 722 653 ; C -1 ; WX 556 ; N Lacute ; B -8 0 559 876 ; C -1 ; WX 980 ; N trademark ; B 30 247 957 653 ; C -1 ; WX 444 ; N edotaccent ; B 31 -11 412 606 ; C -1 ; WX 333 ; N Igrave ; B -8 0 384 876 ; C -1 ; WX 333 ; N Imacron ; B -8 0 441 795 ; C -1 ; WX 611 ; N Lcaron ; B -8 0 586 653 ; C -1 ; WX 750 ; N onehalf ; B 34 -10 749 676 ; C -1 ; WX 549 ; N lessequal ; B 26 0 523 658 ; C -1 ; WX 500 ; N ocircumflex ; B 27 -11 468 661 ; C -1 ; WX 500 ; N ntilde ; B 14 -9 476 624 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 102 -18 765 876 ; C -1 ; WX 611 ; N Eacute ; B -1 0 634 876 ; C -1 ; WX 444 ; N emacron ; B 31 -11 457 583 ; C -1 ; WX 500 ; N gbreve ; B 8 -206 487 650 ; C -1 ; WX 750 ; N onequarter ; B 33 -10 736 676 ; C -1 ; WX 500 ; N Scaron ; B 17 -18 520 873 ; C -1 ; WX 500 ; N Scommaaccent ; B 17 -217 508 667 ; C -1 ; WX 722 ; N Ohungarumlaut ; B 60 -18 699 876 ; C -1 ; WX 400 ; N degree ; B 101 390 387 676 ; C -1 ; WX 500 ; N ograve ; B 27 -11 468 664 ; C -1 ; WX 667 ; N Ccaron ; B 66 -18 689 873 ; C -1 ; WX 500 ; N ugrave ; B 42 -11 475 664 ; C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ; C -1 ; WX 722 ; N Dcaron ; B -8 0 700 873 ; C -1 ; WX 389 ; N rcommaaccent ; B -3 -217 412 441 ; C -1 ; WX 667 ; N Ntilde ; B -20 -15 727 836 ; C -1 ; WX 500 ; N otilde ; B 27 -11 496 624 ; C -1 ; WX 611 ; N Rcommaaccent ; B -13 -187 588 653 ; C -1 ; WX 556 ; N Lcommaaccent ; B -8 -217 559 653 ; C -1 ; WX 611 ; N Atilde ; B -51 0 566 836 ; C -1 ; WX 611 ; N Aogonek ; B -51 -169 566 668 ; C -1 ; WX 611 ; N Aring ; B -51 0 564 883 ; C -1 ; WX 722 ; N Otilde ; B 60 -18 699 836 ; C -1 ; WX 389 ; N zdotaccent ; B -2 -81 380 606 ; C -1 ; WX 611 ; N Ecaron ; B -1 0 634 873 ; C -1 ; WX 333 ; N Iogonek ; B -8 -169 384 653 ; C -1 ; WX 444 ; N kcommaaccent ; B 14 -187 461 683 ; C -1 ; WX 675 ; N minus ; B 86 220 590 286 ; C -1 ; WX 333 ; N Icircumflex ; B -8 0 425 873 ; C -1 ; WX 500 ; N ncaron ; B 14 -9 510 661 ; C -1 ; WX 278 ; N tcommaaccent ; B 2 -217 296 546 ; C -1 ; WX 675 ; N logicalnot ; B 86 108 590 386 ; C -1 ; WX 500 ; N odieresis ; B 27 -11 489 606 ; C -1 ; WX 500 ; N udieresis ; B 42 -11 479 606 ; C -1 ; WX 549 ; N notequal ; B 12 -29 537 541 ; C -1 ; WX 500 ; N gcommaaccent ; B 8 -206 472 706 ; C -1 ; WX 500 ; N eth ; B 27 -11 482 683 ; C -1 ; WX 389 ; N zcaron ; B -2 -81 434 661 ; C -1 ; WX 500 ; N ncommaaccent ; B 14 -187 474 441 ; C -1 ; WX 300 ; N onesuperior ; B 43 271 284 676 ; C -1 ; WX 278 ; N imacron ; B 46 -11 311 583 ; C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2321 KPX A C -30 KPX A Cacute -30 KPX A Ccaron -30 KPX A Ccedilla -30 KPX A G -35 KPX A Gbreve -35 KPX A Gcommaaccent -35 KPX A O -40 KPX A Oacute -40 KPX A Ocircumflex -40 KPX A Odieresis -40 KPX A Ograve -40 KPX A Ohungarumlaut -40 KPX A Omacron -40 KPX A Oslash -40 KPX A Otilde -40 KPX A Q -40 KPX A T -37 KPX A Tcaron -37 KPX A Tcommaaccent -37 KPX A U -50 KPX A Uacute -50 KPX A Ucircumflex -50 KPX A Udieresis -50 KPX A Ugrave -50 KPX A Uhungarumlaut -50 KPX A Umacron -50 KPX A Uogonek -50 KPX A Uring -50 KPX A V -105 KPX A W -95 KPX A Y -55 KPX A Yacute -55 KPX A Ydieresis -55 KPX A quoteright -37 KPX A u -20 KPX A uacute -20 KPX A ucircumflex -20 KPX A udieresis -20 KPX A ugrave -20 KPX A uhungarumlaut -20 KPX A umacron -20 KPX A uogonek -20 KPX A uring -20 KPX A v -55 KPX A w -55 KPX A y -55 KPX A yacute -55 KPX A ydieresis -55 KPX Aacute C -30 KPX Aacute Cacute -30 KPX Aacute Ccaron -30 KPX Aacute Ccedilla -30 KPX Aacute G -35 KPX Aacute Gbreve -35 KPX Aacute Gcommaaccent -35 KPX Aacute O -40 KPX Aacute Oacute -40 KPX Aacute Ocircumflex -40 KPX Aacute Odieresis -40 KPX Aacute Ograve -40 KPX Aacute Ohungarumlaut -40 KPX Aacute Omacron -40 KPX Aacute Oslash -40 KPX Aacute Otilde -40 KPX Aacute Q -40 KPX Aacute T -37 KPX Aacute Tcaron -37 KPX Aacute Tcommaaccent -37 KPX Aacute U -50 KPX Aacute Uacute -50 KPX Aacute Ucircumflex -50 KPX Aacute Udieresis -50 KPX Aacute Ugrave -50 KPX Aacute Uhungarumlaut -50 KPX Aacute Umacron -50 KPX Aacute Uogonek -50 KPX Aacute Uring -50 KPX Aacute V -105 KPX Aacute W -95 KPX Aacute Y -55 KPX Aacute Yacute -55 KPX Aacute Ydieresis -55 KPX Aacute quoteright -37 KPX Aacute u -20 KPX Aacute uacute -20 KPX Aacute ucircumflex -20 KPX Aacute udieresis -20 KPX Aacute ugrave -20 KPX Aacute uhungarumlaut -20 KPX Aacute umacron -20 KPX Aacute uogonek -20 KPX Aacute uring -20 KPX Aacute v -55 KPX Aacute w -55 KPX Aacute y -55 KPX Aacute yacute -55 KPX Aacute ydieresis -55 KPX Abreve C -30 KPX Abreve Cacute -30 KPX Abreve Ccaron -30 KPX Abreve Ccedilla -30 KPX Abreve G -35 KPX Abreve Gbreve -35 KPX Abreve Gcommaaccent -35 KPX Abreve O -40 KPX Abreve Oacute -40 KPX Abreve Ocircumflex -40 KPX Abreve Odieresis -40 KPX Abreve Ograve -40 KPX Abreve Ohungarumlaut -40 KPX Abreve Omacron -40 KPX Abreve Oslash -40 KPX Abreve Otilde -40 KPX Abreve Q -40 KPX Abreve T -37 KPX Abreve Tcaron -37 KPX Abreve Tcommaaccent -37 KPX Abreve U -50 KPX Abreve Uacute -50 KPX Abreve Ucircumflex -50 KPX Abreve Udieresis -50 KPX Abreve Ugrave -50 KPX Abreve Uhungarumlaut -50 KPX Abreve Umacron -50 KPX Abreve Uogonek -50 KPX Abreve Uring -50 KPX Abreve V -105 KPX Abreve W -95 KPX Abreve Y -55 KPX Abreve Yacute -55 KPX Abreve Ydieresis -55 KPX Abreve quoteright -37 KPX Abreve u -20 KPX Abreve uacute -20 KPX Abreve ucircumflex -20 KPX Abreve udieresis -20 KPX Abreve ugrave -20 KPX Abreve uhungarumlaut -20 KPX Abreve umacron -20 KPX Abreve uogonek -20 KPX Abreve uring -20 KPX Abreve v -55 KPX Abreve w -55 KPX Abreve y -55 KPX Abreve yacute -55 KPX Abreve ydieresis -55 KPX Acircumflex C -30 KPX Acircumflex Cacute -30 KPX Acircumflex Ccaron -30 KPX Acircumflex Ccedilla -30 KPX Acircumflex G -35 KPX Acircumflex Gbreve -35 KPX Acircumflex Gcommaaccent -35 KPX Acircumflex O -40 KPX Acircumflex Oacute -40 KPX Acircumflex Ocircumflex -40 KPX Acircumflex Odieresis -40 KPX Acircumflex Ograve -40 KPX Acircumflex Ohungarumlaut -40 KPX Acircumflex Omacron -40 KPX Acircumflex Oslash -40 KPX Acircumflex Otilde -40 KPX Acircumflex Q -40 KPX Acircumflex T -37 KPX Acircumflex Tcaron -37 KPX Acircumflex Tcommaaccent -37 KPX Acircumflex U -50 KPX Acircumflex Uacute -50 KPX Acircumflex Ucircumflex -50 KPX Acircumflex Udieresis -50 KPX Acircumflex Ugrave -50 KPX Acircumflex Uhungarumlaut -50 KPX Acircumflex Umacron -50 KPX Acircumflex Uogonek -50 KPX Acircumflex Uring -50 KPX Acircumflex V -105 KPX Acircumflex W -95 KPX Acircumflex Y -55 KPX Acircumflex Yacute -55 KPX Acircumflex Ydieresis -55 KPX Acircumflex quoteright -37 KPX Acircumflex u -20 KPX Acircumflex uacute -20 KPX Acircumflex ucircumflex -20 KPX Acircumflex udieresis -20 KPX Acircumflex ugrave -20 KPX Acircumflex uhungarumlaut -20 KPX Acircumflex umacron -20 KPX Acircumflex uogonek -20 KPX Acircumflex uring -20 KPX Acircumflex v -55 KPX Acircumflex w -55 KPX Acircumflex y -55 KPX Acircumflex yacute -55 KPX Acircumflex ydieresis -55 KPX Adieresis C -30 KPX Adieresis Cacute -30 KPX Adieresis Ccaron -30 KPX Adieresis Ccedilla -30 KPX Adieresis G -35 KPX Adieresis Gbreve -35 KPX Adieresis Gcommaaccent -35 KPX Adieresis O -40 KPX Adieresis Oacute -40 KPX Adieresis Ocircumflex -40 KPX Adieresis Odieresis -40 KPX Adieresis Ograve -40 KPX Adieresis Ohungarumlaut -40 KPX Adieresis Omacron -40 KPX Adieresis Oslash -40 KPX Adieresis Otilde -40 KPX Adieresis Q -40 KPX Adieresis T -37 KPX Adieresis Tcaron -37 KPX Adieresis Tcommaaccent -37 KPX Adieresis U -50 KPX Adieresis Uacute -50 KPX Adieresis Ucircumflex -50 KPX Adieresis Udieresis -50 KPX Adieresis Ugrave -50 KPX Adieresis Uhungarumlaut -50 KPX Adieresis Umacron -50 KPX Adieresis Uogonek -50 KPX Adieresis Uring -50 KPX Adieresis V -105 KPX Adieresis W -95 KPX Adieresis Y -55 KPX Adieresis Yacute -55 KPX Adieresis Ydieresis -55 KPX Adieresis quoteright -37 KPX Adieresis u -20 KPX Adieresis uacute -20 KPX Adieresis ucircumflex -20 KPX Adieresis udieresis -20 KPX Adieresis ugrave -20 KPX Adieresis uhungarumlaut -20 KPX Adieresis umacron -20 KPX Adieresis uogonek -20 KPX Adieresis uring -20 KPX Adieresis v -55 KPX Adieresis w -55 KPX Adieresis y -55 KPX Adieresis yacute -55 KPX Adieresis ydieresis -55 KPX Agrave C -30 KPX Agrave Cacute -30 KPX Agrave Ccaron -30 KPX Agrave Ccedilla -30 KPX Agrave G -35 KPX Agrave Gbreve -35 KPX Agrave Gcommaaccent -35 KPX Agrave O -40 KPX Agrave Oacute -40 KPX Agrave Ocircumflex -40 KPX Agrave Odieresis -40 KPX Agrave Ograve -40 KPX Agrave Ohungarumlaut -40 KPX Agrave Omacron -40 KPX Agrave Oslash -40 KPX Agrave Otilde -40 KPX Agrave Q -40 KPX Agrave T -37 KPX Agrave Tcaron -37 KPX Agrave Tcommaaccent -37 KPX Agrave U -50 KPX Agrave Uacute -50 KPX Agrave Ucircumflex -50 KPX Agrave Udieresis -50 KPX Agrave Ugrave -50 KPX Agrave Uhungarumlaut -50 KPX Agrave Umacron -50 KPX Agrave Uogonek -50 KPX Agrave Uring -50 KPX Agrave V -105 KPX Agrave W -95 KPX Agrave Y -55 KPX Agrave Yacute -55 KPX Agrave Ydieresis -55 KPX Agrave quoteright -37 KPX Agrave u -20 KPX Agrave uacute -20 KPX Agrave ucircumflex -20 KPX Agrave udieresis -20 KPX Agrave ugrave -20 KPX Agrave uhungarumlaut -20 KPX Agrave umacron -20 KPX Agrave uogonek -20 KPX Agrave uring -20 KPX Agrave v -55 KPX Agrave w -55 KPX Agrave y -55 KPX Agrave yacute -55 KPX Agrave ydieresis -55 KPX Amacron C -30 KPX Amacron Cacute -30 KPX Amacron Ccaron -30 KPX Amacron Ccedilla -30 KPX Amacron G -35 KPX Amacron Gbreve -35 KPX Amacron Gcommaaccent -35 KPX Amacron O -40 KPX Amacron Oacute -40 KPX Amacron Ocircumflex -40 KPX Amacron Odieresis -40 KPX Amacron Ograve -40 KPX Amacron Ohungarumlaut -40 KPX Amacron Omacron -40 KPX Amacron Oslash -40 KPX Amacron Otilde -40 KPX Amacron Q -40 KPX Amacron T -37 KPX Amacron Tcaron -37 KPX Amacron Tcommaaccent -37 KPX Amacron U -50 KPX Amacron Uacute -50 KPX Amacron Ucircumflex -50 KPX Amacron Udieresis -50 KPX Amacron Ugrave -50 KPX Amacron Uhungarumlaut -50 KPX Amacron Umacron -50 KPX Amacron Uogonek -50 KPX Amacron Uring -50 KPX Amacron V -105 KPX Amacron W -95 KPX Amacron Y -55 KPX Amacron Yacute -55 KPX Amacron Ydieresis -55 KPX Amacron quoteright -37 KPX Amacron u -20 KPX Amacron uacute -20 KPX Amacron ucircumflex -20 KPX Amacron udieresis -20 KPX Amacron ugrave -20 KPX Amacron uhungarumlaut -20 KPX Amacron umacron -20 KPX Amacron uogonek -20 KPX Amacron uring -20 KPX Amacron v -55 KPX Amacron w -55 KPX Amacron y -55 KPX Amacron yacute -55 KPX Amacron ydieresis -55 KPX Aogonek C -30 KPX Aogonek Cacute -30 KPX Aogonek Ccaron -30 KPX Aogonek Ccedilla -30 KPX Aogonek G -35 KPX Aogonek Gbreve -35 KPX Aogonek Gcommaaccent -35 KPX Aogonek O -40 KPX Aogonek Oacute -40 KPX Aogonek Ocircumflex -40 KPX Aogonek Odieresis -40 KPX Aogonek Ograve -40 KPX Aogonek Ohungarumlaut -40 KPX Aogonek Omacron -40 KPX Aogonek Oslash -40 KPX Aogonek Otilde -40 KPX Aogonek Q -40 KPX Aogonek T -37 KPX Aogonek Tcaron -37 KPX Aogonek Tcommaaccent -37 KPX Aogonek U -50 KPX Aogonek Uacute -50 KPX Aogonek Ucircumflex -50 KPX Aogonek Udieresis -50 KPX Aogonek Ugrave -50 KPX Aogonek Uhungarumlaut -50 KPX Aogonek Umacron -50 KPX Aogonek Uogonek -50 KPX Aogonek Uring -50 KPX Aogonek V -105 KPX Aogonek W -95 KPX Aogonek Y -55 KPX Aogonek Yacute -55 KPX Aogonek Ydieresis -55 KPX Aogonek quoteright -37 KPX Aogonek u -20 KPX Aogonek uacute -20 KPX Aogonek ucircumflex -20 KPX Aogonek udieresis -20 KPX Aogonek ugrave -20 KPX Aogonek uhungarumlaut -20 KPX Aogonek umacron -20 KPX Aogonek uogonek -20 KPX Aogonek uring -20 KPX Aogonek v -55 KPX Aogonek w -55 KPX Aogonek y -55 KPX Aogonek yacute -55 KPX Aogonek ydieresis -55 KPX Aring C -30 KPX Aring Cacute -30 KPX Aring Ccaron -30 KPX Aring Ccedilla -30 KPX Aring G -35 KPX Aring Gbreve -35 KPX Aring Gcommaaccent -35 KPX Aring O -40 KPX Aring Oacute -40 KPX Aring Ocircumflex -40 KPX Aring Odieresis -40 KPX Aring Ograve -40 KPX Aring Ohungarumlaut -40 KPX Aring Omacron -40 KPX Aring Oslash -40 KPX Aring Otilde -40 KPX Aring Q -40 KPX Aring T -37 KPX Aring Tcaron -37 KPX Aring Tcommaaccent -37 KPX Aring U -50 KPX Aring Uacute -50 KPX Aring Ucircumflex -50 KPX Aring Udieresis -50 KPX Aring Ugrave -50 KPX Aring Uhungarumlaut -50 KPX Aring Umacron -50 KPX Aring Uogonek -50 KPX Aring Uring -50 KPX Aring V -105 KPX Aring W -95 KPX Aring Y -55 KPX Aring Yacute -55 KPX Aring Ydieresis -55 KPX Aring quoteright -37 KPX Aring u -20 KPX Aring uacute -20 KPX Aring ucircumflex -20 KPX Aring udieresis -20 KPX Aring ugrave -20 KPX Aring uhungarumlaut -20 KPX Aring umacron -20 KPX Aring uogonek -20 KPX Aring uring -20 KPX Aring v -55 KPX Aring w -55 KPX Aring y -55 KPX Aring yacute -55 KPX Aring ydieresis -55 KPX Atilde C -30 KPX Atilde Cacute -30 KPX Atilde Ccaron -30 KPX Atilde Ccedilla -30 KPX Atilde G -35 KPX Atilde Gbreve -35 KPX Atilde Gcommaaccent -35 KPX Atilde O -40 KPX Atilde Oacute -40 KPX Atilde Ocircumflex -40 KPX Atilde Odieresis -40 KPX Atilde Ograve -40 KPX Atilde Ohungarumlaut -40 KPX Atilde Omacron -40 KPX Atilde Oslash -40 KPX Atilde Otilde -40 KPX Atilde Q -40 KPX Atilde T -37 KPX Atilde Tcaron -37 KPX Atilde Tcommaaccent -37 KPX Atilde U -50 KPX Atilde Uacute -50 KPX Atilde Ucircumflex -50 KPX Atilde Udieresis -50 KPX Atilde Ugrave -50 KPX Atilde Uhungarumlaut -50 KPX Atilde Umacron -50 KPX Atilde Uogonek -50 KPX Atilde Uring -50 KPX Atilde V -105 KPX Atilde W -95 KPX Atilde Y -55 KPX Atilde Yacute -55 KPX Atilde Ydieresis -55 KPX Atilde quoteright -37 KPX Atilde u -20 KPX Atilde uacute -20 KPX Atilde ucircumflex -20 KPX Atilde udieresis -20 KPX Atilde ugrave -20 KPX Atilde uhungarumlaut -20 KPX Atilde umacron -20 KPX Atilde uogonek -20 KPX Atilde uring -20 KPX Atilde v -55 KPX Atilde w -55 KPX Atilde y -55 KPX Atilde yacute -55 KPX Atilde ydieresis -55 KPX B A -25 KPX B Aacute -25 KPX B Abreve -25 KPX B Acircumflex -25 KPX B Adieresis -25 KPX B Agrave -25 KPX B Amacron -25 KPX B Aogonek -25 KPX B Aring -25 KPX B Atilde -25 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX D A -35 KPX D Aacute -35 KPX D Abreve -35 KPX D Acircumflex -35 KPX D Adieresis -35 KPX D Agrave -35 KPX D Amacron -35 KPX D Aogonek -35 KPX D Aring -35 KPX D Atilde -35 KPX D V -40 KPX D W -40 KPX D Y -40 KPX D Yacute -40 KPX D Ydieresis -40 KPX Dcaron A -35 KPX Dcaron Aacute -35 KPX Dcaron Abreve -35 KPX Dcaron Acircumflex -35 KPX Dcaron Adieresis -35 KPX Dcaron Agrave -35 KPX Dcaron Amacron -35 KPX Dcaron Aogonek -35 KPX Dcaron Aring -35 KPX Dcaron Atilde -35 KPX Dcaron V -40 KPX Dcaron W -40 KPX Dcaron Y -40 KPX Dcaron Yacute -40 KPX Dcaron Ydieresis -40 KPX Dcroat A -35 KPX Dcroat Aacute -35 KPX Dcroat Abreve -35 KPX Dcroat Acircumflex -35 KPX Dcroat Adieresis -35 KPX Dcroat Agrave -35 KPX Dcroat Amacron -35 KPX Dcroat Aogonek -35 KPX Dcroat Aring -35 KPX Dcroat Atilde -35 KPX Dcroat V -40 KPX Dcroat W -40 KPX Dcroat Y -40 KPX Dcroat Yacute -40 KPX Dcroat Ydieresis -40 KPX F A -115 KPX F Aacute -115 KPX F Abreve -115 KPX F Acircumflex -115 KPX F Adieresis -115 KPX F Agrave -115 KPX F Amacron -115 KPX F Aogonek -115 KPX F Aring -115 KPX F Atilde -115 KPX F a -75 KPX F aacute -75 KPX F abreve -75 KPX F acircumflex -75 KPX F adieresis -75 KPX F agrave -75 KPX F amacron -75 KPX F aogonek -75 KPX F aring -75 KPX F atilde -75 KPX F comma -135 KPX F e -75 KPX F eacute -75 KPX F ecaron -75 KPX F ecircumflex -75 KPX F edieresis -75 KPX F edotaccent -75 KPX F egrave -75 KPX F emacron -75 KPX F eogonek -75 KPX F i -45 KPX F iacute -45 KPX F icircumflex -45 KPX F idieresis -45 KPX F igrave -45 KPX F imacron -45 KPX F iogonek -45 KPX F o -105 KPX F oacute -105 KPX F ocircumflex -105 KPX F odieresis -105 KPX F ograve -105 KPX F ohungarumlaut -105 KPX F omacron -105 KPX F oslash -105 KPX F otilde -105 KPX F period -135 KPX F r -55 KPX F racute -55 KPX F rcaron -55 KPX F rcommaaccent -55 KPX J A -40 KPX J Aacute -40 KPX J Abreve -40 KPX J Acircumflex -40 KPX J Adieresis -40 KPX J Agrave -40 KPX J Amacron -40 KPX J Aogonek -40 KPX J Aring -40 KPX J Atilde -40 KPX J a -35 KPX J aacute -35 KPX J abreve -35 KPX J acircumflex -35 KPX J adieresis -35 KPX J agrave -35 KPX J amacron -35 KPX J aogonek -35 KPX J aring -35 KPX J atilde -35 KPX J comma -25 KPX J e -25 KPX J eacute -25 KPX J ecaron -25 KPX J ecircumflex -25 KPX J edieresis -25 KPX J edotaccent -25 KPX J egrave -25 KPX J emacron -25 KPX J eogonek -25 KPX J o -25 KPX J oacute -25 KPX J ocircumflex -25 KPX J odieresis -25 KPX J ograve -25 KPX J ohungarumlaut -25 KPX J omacron -25 KPX J oslash -25 KPX J otilde -25 KPX J period -25 KPX J u -35 KPX J uacute -35 KPX J ucircumflex -35 KPX J udieresis -35 KPX J ugrave -35 KPX J uhungarumlaut -35 KPX J umacron -35 KPX J uogonek -35 KPX J uring -35 KPX K O -50 KPX K Oacute -50 KPX K Ocircumflex -50 KPX K Odieresis -50 KPX K Ograve -50 KPX K Ohungarumlaut -50 KPX K Omacron -50 KPX K Oslash -50 KPX K Otilde -50 KPX K e -35 KPX K eacute -35 KPX K ecaron -35 KPX K ecircumflex -35 KPX K edieresis -35 KPX K edotaccent -35 KPX K egrave -35 KPX K emacron -35 KPX K eogonek -35 KPX K o -40 KPX K oacute -40 KPX K ocircumflex -40 KPX K odieresis -40 KPX K ograve -40 KPX K ohungarumlaut -40 KPX K omacron -40 KPX K oslash -40 KPX K otilde -40 KPX K u -40 KPX K uacute -40 KPX K ucircumflex -40 KPX K udieresis -40 KPX K ugrave -40 KPX K uhungarumlaut -40 KPX K umacron -40 KPX K uogonek -40 KPX K uring -40 KPX K y -40 KPX K yacute -40 KPX K ydieresis -40 KPX Kcommaaccent O -50 KPX Kcommaaccent Oacute -50 KPX Kcommaaccent Ocircumflex -50 KPX Kcommaaccent Odieresis -50 KPX Kcommaaccent Ograve -50 KPX Kcommaaccent Ohungarumlaut -50 KPX Kcommaaccent Omacron -50 KPX Kcommaaccent Oslash -50 KPX Kcommaaccent Otilde -50 KPX Kcommaaccent e -35 KPX Kcommaaccent eacute -35 KPX Kcommaaccent ecaron -35 KPX Kcommaaccent ecircumflex -35 KPX Kcommaaccent edieresis -35 KPX Kcommaaccent edotaccent -35 KPX Kcommaaccent egrave -35 KPX Kcommaaccent emacron -35 KPX Kcommaaccent eogonek -35 KPX Kcommaaccent o -40 KPX Kcommaaccent oacute -40 KPX Kcommaaccent ocircumflex -40 KPX Kcommaaccent odieresis -40 KPX Kcommaaccent ograve -40 KPX Kcommaaccent ohungarumlaut -40 KPX Kcommaaccent omacron -40 KPX Kcommaaccent oslash -40 KPX Kcommaaccent otilde -40 KPX Kcommaaccent u -40 KPX Kcommaaccent uacute -40 KPX Kcommaaccent ucircumflex -40 KPX Kcommaaccent udieresis -40 KPX Kcommaaccent ugrave -40 KPX Kcommaaccent uhungarumlaut -40 KPX Kcommaaccent umacron -40 KPX Kcommaaccent uogonek -40 KPX Kcommaaccent uring -40 KPX Kcommaaccent y -40 KPX Kcommaaccent yacute -40 KPX Kcommaaccent ydieresis -40 KPX L T -20 KPX L Tcaron -20 KPX L Tcommaaccent -20 KPX L V -55 KPX L W -55 KPX L Y -20 KPX L Yacute -20 KPX L Ydieresis -20 KPX L quoteright -37 KPX L y -30 KPX L yacute -30 KPX L ydieresis -30 KPX Lacute T -20 KPX Lacute Tcaron -20 KPX Lacute Tcommaaccent -20 KPX Lacute V -55 KPX Lacute W -55 KPX Lacute Y -20 KPX Lacute Yacute -20 KPX Lacute Ydieresis -20 KPX Lacute quoteright -37 KPX Lacute y -30 KPX Lacute yacute -30 KPX Lacute ydieresis -30 KPX Lcommaaccent T -20 KPX Lcommaaccent Tcaron -20 KPX Lcommaaccent Tcommaaccent -20 KPX Lcommaaccent V -55 KPX Lcommaaccent W -55 KPX Lcommaaccent Y -20 KPX Lcommaaccent Yacute -20 KPX Lcommaaccent Ydieresis -20 KPX Lcommaaccent quoteright -37 KPX Lcommaaccent y -30 KPX Lcommaaccent yacute -30 KPX Lcommaaccent ydieresis -30 KPX Lslash T -20 KPX Lslash Tcaron -20 KPX Lslash Tcommaaccent -20 KPX Lslash V -55 KPX Lslash W -55 KPX Lslash Y -20 KPX Lslash Yacute -20 KPX Lslash Ydieresis -20 KPX Lslash quoteright -37 KPX Lslash y -30 KPX Lslash yacute -30 KPX Lslash ydieresis -30 KPX N A -27 KPX N Aacute -27 KPX N Abreve -27 KPX N Acircumflex -27 KPX N Adieresis -27 KPX N Agrave -27 KPX N Amacron -27 KPX N Aogonek -27 KPX N Aring -27 KPX N Atilde -27 KPX Nacute A -27 KPX Nacute Aacute -27 KPX Nacute Abreve -27 KPX Nacute Acircumflex -27 KPX Nacute Adieresis -27 KPX Nacute Agrave -27 KPX Nacute Amacron -27 KPX Nacute Aogonek -27 KPX Nacute Aring -27 KPX Nacute Atilde -27 KPX Ncaron A -27 KPX Ncaron Aacute -27 KPX Ncaron Abreve -27 KPX Ncaron Acircumflex -27 KPX Ncaron Adieresis -27 KPX Ncaron Agrave -27 KPX Ncaron Amacron -27 KPX Ncaron Aogonek -27 KPX Ncaron Aring -27 KPX Ncaron Atilde -27 KPX Ncommaaccent A -27 KPX Ncommaaccent Aacute -27 KPX Ncommaaccent Abreve -27 KPX Ncommaaccent Acircumflex -27 KPX Ncommaaccent Adieresis -27 KPX Ncommaaccent Agrave -27 KPX Ncommaaccent Amacron -27 KPX Ncommaaccent Aogonek -27 KPX Ncommaaccent Aring -27 KPX Ncommaaccent Atilde -27 KPX Ntilde A -27 KPX Ntilde Aacute -27 KPX Ntilde Abreve -27 KPX Ntilde Acircumflex -27 KPX Ntilde Adieresis -27 KPX Ntilde Agrave -27 KPX Ntilde Amacron -27 KPX Ntilde Aogonek -27 KPX Ntilde Aring -27 KPX Ntilde Atilde -27 KPX O A -55 KPX O Aacute -55 KPX O Abreve -55 KPX O Acircumflex -55 KPX O Adieresis -55 KPX O Agrave -55 KPX O Amacron -55 KPX O Aogonek -55 KPX O Aring -55 KPX O Atilde -55 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -50 KPX O X -40 KPX O Y -50 KPX O Yacute -50 KPX O Ydieresis -50 KPX Oacute A -55 KPX Oacute Aacute -55 KPX Oacute Abreve -55 KPX Oacute Acircumflex -55 KPX Oacute Adieresis -55 KPX Oacute Agrave -55 KPX Oacute Amacron -55 KPX Oacute Aogonek -55 KPX Oacute Aring -55 KPX Oacute Atilde -55 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -50 KPX Oacute X -40 KPX Oacute Y -50 KPX Oacute Yacute -50 KPX Oacute Ydieresis -50 KPX Ocircumflex A -55 KPX Ocircumflex Aacute -55 KPX Ocircumflex Abreve -55 KPX Ocircumflex Acircumflex -55 KPX Ocircumflex Adieresis -55 KPX Ocircumflex Agrave -55 KPX Ocircumflex Amacron -55 KPX Ocircumflex Aogonek -55 KPX Ocircumflex Aring -55 KPX Ocircumflex Atilde -55 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -50 KPX Ocircumflex X -40 KPX Ocircumflex Y -50 KPX Ocircumflex Yacute -50 KPX Ocircumflex Ydieresis -50 KPX Odieresis A -55 KPX Odieresis Aacute -55 KPX Odieresis Abreve -55 KPX Odieresis Acircumflex -55 KPX Odieresis Adieresis -55 KPX Odieresis Agrave -55 KPX Odieresis Amacron -55 KPX Odieresis Aogonek -55 KPX Odieresis Aring -55 KPX Odieresis Atilde -55 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -50 KPX Odieresis X -40 KPX Odieresis Y -50 KPX Odieresis Yacute -50 KPX Odieresis Ydieresis -50 KPX Ograve A -55 KPX Ograve Aacute -55 KPX Ograve Abreve -55 KPX Ograve Acircumflex -55 KPX Ograve Adieresis -55 KPX Ograve Agrave -55 KPX Ograve Amacron -55 KPX Ograve Aogonek -55 KPX Ograve Aring -55 KPX Ograve Atilde -55 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -50 KPX Ograve X -40 KPX Ograve Y -50 KPX Ograve Yacute -50 KPX Ograve Ydieresis -50 KPX Ohungarumlaut A -55 KPX Ohungarumlaut Aacute -55 KPX Ohungarumlaut Abreve -55 KPX Ohungarumlaut Acircumflex -55 KPX Ohungarumlaut Adieresis -55 KPX Ohungarumlaut Agrave -55 KPX Ohungarumlaut Amacron -55 KPX Ohungarumlaut Aogonek -55 KPX Ohungarumlaut Aring -55 KPX Ohungarumlaut Atilde -55 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -50 KPX Ohungarumlaut X -40 KPX Ohungarumlaut Y -50 KPX Ohungarumlaut Yacute -50 KPX Ohungarumlaut Ydieresis -50 KPX Omacron A -55 KPX Omacron Aacute -55 KPX Omacron Abreve -55 KPX Omacron Acircumflex -55 KPX Omacron Adieresis -55 KPX Omacron Agrave -55 KPX Omacron Amacron -55 KPX Omacron Aogonek -55 KPX Omacron Aring -55 KPX Omacron Atilde -55 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -50 KPX Omacron X -40 KPX Omacron Y -50 KPX Omacron Yacute -50 KPX Omacron Ydieresis -50 KPX Oslash A -55 KPX Oslash Aacute -55 KPX Oslash Abreve -55 KPX Oslash Acircumflex -55 KPX Oslash Adieresis -55 KPX Oslash Agrave -55 KPX Oslash Amacron -55 KPX Oslash Aogonek -55 KPX Oslash Aring -55 KPX Oslash Atilde -55 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -50 KPX Oslash X -40 KPX Oslash Y -50 KPX Oslash Yacute -50 KPX Oslash Ydieresis -50 KPX Otilde A -55 KPX Otilde Aacute -55 KPX Otilde Abreve -55 KPX Otilde Acircumflex -55 KPX Otilde Adieresis -55 KPX Otilde Agrave -55 KPX Otilde Amacron -55 KPX Otilde Aogonek -55 KPX Otilde Aring -55 KPX Otilde Atilde -55 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -50 KPX Otilde X -40 KPX Otilde Y -50 KPX Otilde Yacute -50 KPX Otilde Ydieresis -50 KPX P A -90 KPX P Aacute -90 KPX P Abreve -90 KPX P Acircumflex -90 KPX P Adieresis -90 KPX P Agrave -90 KPX P Amacron -90 KPX P Aogonek -90 KPX P Aring -90 KPX P Atilde -90 KPX P a -80 KPX P aacute -80 KPX P abreve -80 KPX P acircumflex -80 KPX P adieresis -80 KPX P agrave -80 KPX P amacron -80 KPX P aogonek -80 KPX P aring -80 KPX P atilde -80 KPX P comma -135 KPX P e -80 KPX P eacute -80 KPX P ecaron -80 KPX P ecircumflex -80 KPX P edieresis -80 KPX P edotaccent -80 KPX P egrave -80 KPX P emacron -80 KPX P eogonek -80 KPX P o -80 KPX P oacute -80 KPX P ocircumflex -80 KPX P odieresis -80 KPX P ograve -80 KPX P ohungarumlaut -80 KPX P omacron -80 KPX P oslash -80 KPX P otilde -80 KPX P period -135 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX R O -40 KPX R Oacute -40 KPX R Ocircumflex -40 KPX R Odieresis -40 KPX R Ograve -40 KPX R Ohungarumlaut -40 KPX R Omacron -40 KPX R Oslash -40 KPX R Otilde -40 KPX R U -40 KPX R Uacute -40 KPX R Ucircumflex -40 KPX R Udieresis -40 KPX R Ugrave -40 KPX R Uhungarumlaut -40 KPX R Umacron -40 KPX R Uogonek -40 KPX R Uring -40 KPX R V -18 KPX R W -18 KPX R Y -18 KPX R Yacute -18 KPX R Ydieresis -18 KPX Racute O -40 KPX Racute Oacute -40 KPX Racute Ocircumflex -40 KPX Racute Odieresis -40 KPX Racute Ograve -40 KPX Racute Ohungarumlaut -40 KPX Racute Omacron -40 KPX Racute Oslash -40 KPX Racute Otilde -40 KPX Racute U -40 KPX Racute Uacute -40 KPX Racute Ucircumflex -40 KPX Racute Udieresis -40 KPX Racute Ugrave -40 KPX Racute Uhungarumlaut -40 KPX Racute Umacron -40 KPX Racute Uogonek -40 KPX Racute Uring -40 KPX Racute V -18 KPX Racute W -18 KPX Racute Y -18 KPX Racute Yacute -18 KPX Racute Ydieresis -18 KPX Rcaron O -40 KPX Rcaron Oacute -40 KPX Rcaron Ocircumflex -40 KPX Rcaron Odieresis -40 KPX Rcaron Ograve -40 KPX Rcaron Ohungarumlaut -40 KPX Rcaron Omacron -40 KPX Rcaron Oslash -40 KPX Rcaron Otilde -40 KPX Rcaron U -40 KPX Rcaron Uacute -40 KPX Rcaron Ucircumflex -40 KPX Rcaron Udieresis -40 KPX Rcaron Ugrave -40 KPX Rcaron Uhungarumlaut -40 KPX Rcaron Umacron -40 KPX Rcaron Uogonek -40 KPX Rcaron Uring -40 KPX Rcaron V -18 KPX Rcaron W -18 KPX Rcaron Y -18 KPX Rcaron Yacute -18 KPX Rcaron Ydieresis -18 KPX Rcommaaccent O -40 KPX Rcommaaccent Oacute -40 KPX Rcommaaccent Ocircumflex -40 KPX Rcommaaccent Odieresis -40 KPX Rcommaaccent Ograve -40 KPX Rcommaaccent Ohungarumlaut -40 KPX Rcommaaccent Omacron -40 KPX Rcommaaccent Oslash -40 KPX Rcommaaccent Otilde -40 KPX Rcommaaccent U -40 KPX Rcommaaccent Uacute -40 KPX Rcommaaccent Ucircumflex -40 KPX Rcommaaccent Udieresis -40 KPX Rcommaaccent Ugrave -40 KPX Rcommaaccent Uhungarumlaut -40 KPX Rcommaaccent Umacron -40 KPX Rcommaaccent Uogonek -40 KPX Rcommaaccent Uring -40 KPX Rcommaaccent V -18 KPX Rcommaaccent W -18 KPX Rcommaaccent Y -18 KPX Rcommaaccent Yacute -18 KPX Rcommaaccent Ydieresis -18 KPX T A -50 KPX T Aacute -50 KPX T Abreve -50 KPX T Acircumflex -50 KPX T Adieresis -50 KPX T Agrave -50 KPX T Amacron -50 KPX T Aogonek -50 KPX T Aring -50 KPX T Atilde -50 KPX T O -18 KPX T Oacute -18 KPX T Ocircumflex -18 KPX T Odieresis -18 KPX T Ograve -18 KPX T Ohungarumlaut -18 KPX T Omacron -18 KPX T Oslash -18 KPX T Otilde -18 KPX T a -92 KPX T aacute -92 KPX T abreve -92 KPX T acircumflex -92 KPX T adieresis -92 KPX T agrave -92 KPX T amacron -92 KPX T aogonek -92 KPX T aring -92 KPX T atilde -92 KPX T colon -55 KPX T comma -74 KPX T e -92 KPX T eacute -92 KPX T ecaron -92 KPX T ecircumflex -52 KPX T edieresis -52 KPX T edotaccent -92 KPX T egrave -52 KPX T emacron -52 KPX T eogonek -92 KPX T hyphen -74 KPX T i -55 KPX T iacute -55 KPX T iogonek -55 KPX T o -92 KPX T oacute -92 KPX T ocircumflex -92 KPX T odieresis -92 KPX T ograve -92 KPX T ohungarumlaut -92 KPX T omacron -92 KPX T oslash -92 KPX T otilde -92 KPX T period -74 KPX T r -55 KPX T racute -55 KPX T rcaron -55 KPX T rcommaaccent -55 KPX T semicolon -65 KPX T u -55 KPX T uacute -55 KPX T ucircumflex -55 KPX T udieresis -55 KPX T ugrave -55 KPX T uhungarumlaut -55 KPX T umacron -55 KPX T uogonek -55 KPX T uring -55 KPX T w -74 KPX T y -74 KPX T yacute -74 KPX T ydieresis -34 KPX Tcaron A -50 KPX Tcaron Aacute -50 KPX Tcaron Abreve -50 KPX Tcaron Acircumflex -50 KPX Tcaron Adieresis -50 KPX Tcaron Agrave -50 KPX Tcaron Amacron -50 KPX Tcaron Aogonek -50 KPX Tcaron Aring -50 KPX Tcaron Atilde -50 KPX Tcaron O -18 KPX Tcaron Oacute -18 KPX Tcaron Ocircumflex -18 KPX Tcaron Odieresis -18 KPX Tcaron Ograve -18 KPX Tcaron Ohungarumlaut -18 KPX Tcaron Omacron -18 KPX Tcaron Oslash -18 KPX Tcaron Otilde -18 KPX Tcaron a -92 KPX Tcaron aacute -92 KPX Tcaron abreve -92 KPX Tcaron acircumflex -92 KPX Tcaron adieresis -92 KPX Tcaron agrave -92 KPX Tcaron amacron -92 KPX Tcaron aogonek -92 KPX Tcaron aring -92 KPX Tcaron atilde -92 KPX Tcaron colon -55 KPX Tcaron comma -74 KPX Tcaron e -92 KPX Tcaron eacute -92 KPX Tcaron ecaron -92 KPX Tcaron ecircumflex -52 KPX Tcaron edieresis -52 KPX Tcaron edotaccent -92 KPX Tcaron egrave -52 KPX Tcaron emacron -52 KPX Tcaron eogonek -92 KPX Tcaron hyphen -74 KPX Tcaron i -55 KPX Tcaron iacute -55 KPX Tcaron iogonek -55 KPX Tcaron o -92 KPX Tcaron oacute -92 KPX Tcaron ocircumflex -92 KPX Tcaron odieresis -92 KPX Tcaron ograve -92 KPX Tcaron ohungarumlaut -92 KPX Tcaron omacron -92 KPX Tcaron oslash -92 KPX Tcaron otilde -92 KPX Tcaron period -74 KPX Tcaron r -55 KPX Tcaron racute -55 KPX Tcaron rcaron -55 KPX Tcaron rcommaaccent -55 KPX Tcaron semicolon -65 KPX Tcaron u -55 KPX Tcaron uacute -55 KPX Tcaron ucircumflex -55 KPX Tcaron udieresis -55 KPX Tcaron ugrave -55 KPX Tcaron uhungarumlaut -55 KPX Tcaron umacron -55 KPX Tcaron uogonek -55 KPX Tcaron uring -55 KPX Tcaron w -74 KPX Tcaron y -74 KPX Tcaron yacute -74 KPX Tcaron ydieresis -34 KPX Tcommaaccent A -50 KPX Tcommaaccent Aacute -50 KPX Tcommaaccent Abreve -50 KPX Tcommaaccent Acircumflex -50 KPX Tcommaaccent Adieresis -50 KPX Tcommaaccent Agrave -50 KPX Tcommaaccent Amacron -50 KPX Tcommaaccent Aogonek -50 KPX Tcommaaccent Aring -50 KPX Tcommaaccent Atilde -50 KPX Tcommaaccent O -18 KPX Tcommaaccent Oacute -18 KPX Tcommaaccent Ocircumflex -18 KPX Tcommaaccent Odieresis -18 KPX Tcommaaccent Ograve -18 KPX Tcommaaccent Ohungarumlaut -18 KPX Tcommaaccent Omacron -18 KPX Tcommaaccent Oslash -18 KPX Tcommaaccent Otilde -18 KPX Tcommaaccent a -92 KPX Tcommaaccent aacute -92 KPX Tcommaaccent abreve -92 KPX Tcommaaccent acircumflex -92 KPX Tcommaaccent adieresis -92 KPX Tcommaaccent agrave -92 KPX Tcommaaccent amacron -92 KPX Tcommaaccent aogonek -92 KPX Tcommaaccent aring -92 KPX Tcommaaccent atilde -92 KPX Tcommaaccent colon -55 KPX Tcommaaccent comma -74 KPX Tcommaaccent e -92 KPX Tcommaaccent eacute -92 KPX Tcommaaccent ecaron -92 KPX Tcommaaccent ecircumflex -52 KPX Tcommaaccent edieresis -52 KPX Tcommaaccent edotaccent -92 KPX Tcommaaccent egrave -52 KPX Tcommaaccent emacron -52 KPX Tcommaaccent eogonek -92 KPX Tcommaaccent hyphen -74 KPX Tcommaaccent i -55 KPX Tcommaaccent iacute -55 KPX Tcommaaccent iogonek -55 KPX Tcommaaccent o -92 KPX Tcommaaccent oacute -92 KPX Tcommaaccent ocircumflex -92 KPX Tcommaaccent odieresis -92 KPX Tcommaaccent ograve -92 KPX Tcommaaccent ohungarumlaut -92 KPX Tcommaaccent omacron -92 KPX Tcommaaccent oslash -92 KPX Tcommaaccent otilde -92 KPX Tcommaaccent period -74 KPX Tcommaaccent r -55 KPX Tcommaaccent racute -55 KPX Tcommaaccent rcaron -55 KPX Tcommaaccent rcommaaccent -55 KPX Tcommaaccent semicolon -65 KPX Tcommaaccent u -55 KPX Tcommaaccent uacute -55 KPX Tcommaaccent ucircumflex -55 KPX Tcommaaccent udieresis -55 KPX Tcommaaccent ugrave -55 KPX Tcommaaccent uhungarumlaut -55 KPX Tcommaaccent umacron -55 KPX Tcommaaccent uogonek -55 KPX Tcommaaccent uring -55 KPX Tcommaaccent w -74 KPX Tcommaaccent y -74 KPX Tcommaaccent yacute -74 KPX Tcommaaccent ydieresis -34 KPX U A -40 KPX U Aacute -40 KPX U Abreve -40 KPX U Acircumflex -40 KPX U Adieresis -40 KPX U Agrave -40 KPX U Amacron -40 KPX U Aogonek -40 KPX U Aring -40 KPX U Atilde -40 KPX U comma -25 KPX U period -25 KPX Uacute A -40 KPX Uacute Aacute -40 KPX Uacute Abreve -40 KPX Uacute Acircumflex -40 KPX Uacute Adieresis -40 KPX Uacute Agrave -40 KPX Uacute Amacron -40 KPX Uacute Aogonek -40 KPX Uacute Aring -40 KPX Uacute Atilde -40 KPX Uacute comma -25 KPX Uacute period -25 KPX Ucircumflex A -40 KPX Ucircumflex Aacute -40 KPX Ucircumflex Abreve -40 KPX Ucircumflex Acircumflex -40 KPX Ucircumflex Adieresis -40 KPX Ucircumflex Agrave -40 KPX Ucircumflex Amacron -40 KPX Ucircumflex Aogonek -40 KPX Ucircumflex Aring -40 KPX Ucircumflex Atilde -40 KPX Ucircumflex comma -25 KPX Ucircumflex period -25 KPX Udieresis A -40 KPX Udieresis Aacute -40 KPX Udieresis Abreve -40 KPX Udieresis Acircumflex -40 KPX Udieresis Adieresis -40 KPX Udieresis Agrave -40 KPX Udieresis Amacron -40 KPX Udieresis Aogonek -40 KPX Udieresis Aring -40 KPX Udieresis Atilde -40 KPX Udieresis comma -25 KPX Udieresis period -25 KPX Ugrave A -40 KPX Ugrave Aacute -40 KPX Ugrave Abreve -40 KPX Ugrave Acircumflex -40 KPX Ugrave Adieresis -40 KPX Ugrave Agrave -40 KPX Ugrave Amacron -40 KPX Ugrave Aogonek -40 KPX Ugrave Aring -40 KPX Ugrave Atilde -40 KPX Ugrave comma -25 KPX Ugrave period -25 KPX Uhungarumlaut A -40 KPX Uhungarumlaut Aacute -40 KPX Uhungarumlaut Abreve -40 KPX Uhungarumlaut Acircumflex -40 KPX Uhungarumlaut Adieresis -40 KPX Uhungarumlaut Agrave -40 KPX Uhungarumlaut Amacron -40 KPX Uhungarumlaut Aogonek -40 KPX Uhungarumlaut Aring -40 KPX Uhungarumlaut Atilde -40 KPX Uhungarumlaut comma -25 KPX Uhungarumlaut period -25 KPX Umacron A -40 KPX Umacron Aacute -40 KPX Umacron Abreve -40 KPX Umacron Acircumflex -40 KPX Umacron Adieresis -40 KPX Umacron Agrave -40 KPX Umacron Amacron -40 KPX Umacron Aogonek -40 KPX Umacron Aring -40 KPX Umacron Atilde -40 KPX Umacron comma -25 KPX Umacron period -25 KPX Uogonek A -40 KPX Uogonek Aacute -40 KPX Uogonek Abreve -40 KPX Uogonek Acircumflex -40 KPX Uogonek Adieresis -40 KPX Uogonek Agrave -40 KPX Uogonek Amacron -40 KPX Uogonek Aogonek -40 KPX Uogonek Aring -40 KPX Uogonek Atilde -40 KPX Uogonek comma -25 KPX Uogonek period -25 KPX Uring A -40 KPX Uring Aacute -40 KPX Uring Abreve -40 KPX Uring Acircumflex -40 KPX Uring Adieresis -40 KPX Uring Agrave -40 KPX Uring Amacron -40 KPX Uring Aogonek -40 KPX Uring Aring -40 KPX Uring Atilde -40 KPX Uring comma -25 KPX Uring period -25 KPX V A -60 KPX V Aacute -60 KPX V Abreve -60 KPX V Acircumflex -60 KPX V Adieresis -60 KPX V Agrave -60 KPX V Amacron -60 KPX V Aogonek -60 KPX V Aring -60 KPX V Atilde -60 KPX V O -30 KPX V Oacute -30 KPX V Ocircumflex -30 KPX V Odieresis -30 KPX V Ograve -30 KPX V Ohungarumlaut -30 KPX V Omacron -30 KPX V Oslash -30 KPX V Otilde -30 KPX V a -111 KPX V aacute -111 KPX V abreve -111 KPX V acircumflex -111 KPX V adieresis -111 KPX V agrave -111 KPX V amacron -111 KPX V aogonek -111 KPX V aring -111 KPX V atilde -111 KPX V colon -65 KPX V comma -129 KPX V e -111 KPX V eacute -111 KPX V ecaron -111 KPX V ecircumflex -111 KPX V edieresis -71 KPX V edotaccent -111 KPX V egrave -71 KPX V emacron -71 KPX V eogonek -111 KPX V hyphen -55 KPX V i -74 KPX V iacute -74 KPX V icircumflex -34 KPX V idieresis -34 KPX V igrave -34 KPX V imacron -34 KPX V iogonek -74 KPX V o -111 KPX V oacute -111 KPX V ocircumflex -111 KPX V odieresis -111 KPX V ograve -111 KPX V ohungarumlaut -111 KPX V omacron -111 KPX V oslash -111 KPX V otilde -111 KPX V period -129 KPX V semicolon -74 KPX V u -74 KPX V uacute -74 KPX V ucircumflex -74 KPX V udieresis -74 KPX V ugrave -74 KPX V uhungarumlaut -74 KPX V umacron -74 KPX V uogonek -74 KPX V uring -74 KPX W A -60 KPX W Aacute -60 KPX W Abreve -60 KPX W Acircumflex -60 KPX W Adieresis -60 KPX W Agrave -60 KPX W Amacron -60 KPX W Aogonek -60 KPX W Aring -60 KPX W Atilde -60 KPX W O -25 KPX W Oacute -25 KPX W Ocircumflex -25 KPX W Odieresis -25 KPX W Ograve -25 KPX W Ohungarumlaut -25 KPX W Omacron -25 KPX W Oslash -25 KPX W Otilde -25 KPX W a -92 KPX W aacute -92 KPX W abreve -92 KPX W acircumflex -92 KPX W adieresis -92 KPX W agrave -92 KPX W amacron -92 KPX W aogonek -92 KPX W aring -92 KPX W atilde -92 KPX W colon -65 KPX W comma -92 KPX W e -92 KPX W eacute -92 KPX W ecaron -92 KPX W ecircumflex -92 KPX W edieresis -52 KPX W edotaccent -92 KPX W egrave -52 KPX W emacron -52 KPX W eogonek -92 KPX W hyphen -37 KPX W i -55 KPX W iacute -55 KPX W iogonek -55 KPX W o -92 KPX W oacute -92 KPX W ocircumflex -92 KPX W odieresis -92 KPX W ograve -92 KPX W ohungarumlaut -92 KPX W omacron -92 KPX W oslash -92 KPX W otilde -92 KPX W period -92 KPX W semicolon -65 KPX W u -55 KPX W uacute -55 KPX W ucircumflex -55 KPX W udieresis -55 KPX W ugrave -55 KPX W uhungarumlaut -55 KPX W umacron -55 KPX W uogonek -55 KPX W uring -55 KPX W y -70 KPX W yacute -70 KPX W ydieresis -70 KPX Y A -50 KPX Y Aacute -50 KPX Y Abreve -50 KPX Y Acircumflex -50 KPX Y Adieresis -50 KPX Y Agrave -50 KPX Y Amacron -50 KPX Y Aogonek -50 KPX Y Aring -50 KPX Y Atilde -50 KPX Y O -15 KPX Y Oacute -15 KPX Y Ocircumflex -15 KPX Y Odieresis -15 KPX Y Ograve -15 KPX Y Ohungarumlaut -15 KPX Y Omacron -15 KPX Y Oslash -15 KPX Y Otilde -15 KPX Y a -92 KPX Y aacute -92 KPX Y abreve -92 KPX Y acircumflex -92 KPX Y adieresis -92 KPX Y agrave -92 KPX Y amacron -92 KPX Y aogonek -92 KPX Y aring -92 KPX Y atilde -92 KPX Y colon -65 KPX Y comma -92 KPX Y e -92 KPX Y eacute -92 KPX Y ecaron -92 KPX Y ecircumflex -92 KPX Y edieresis -52 KPX Y edotaccent -92 KPX Y egrave -52 KPX Y emacron -52 KPX Y eogonek -92 KPX Y hyphen -74 KPX Y i -74 KPX Y iacute -74 KPX Y icircumflex -34 KPX Y idieresis -34 KPX Y igrave -34 KPX Y imacron -34 KPX Y iogonek -74 KPX Y o -92 KPX Y oacute -92 KPX Y ocircumflex -92 KPX Y odieresis -92 KPX Y ograve -92 KPX Y ohungarumlaut -92 KPX Y omacron -92 KPX Y oslash -92 KPX Y otilde -92 KPX Y period -92 KPX Y semicolon -65 KPX Y u -92 KPX Y uacute -92 KPX Y ucircumflex -92 KPX Y udieresis -92 KPX Y ugrave -92 KPX Y uhungarumlaut -92 KPX Y umacron -92 KPX Y uogonek -92 KPX Y uring -92 KPX Yacute A -50 KPX Yacute Aacute -50 KPX Yacute Abreve -50 KPX Yacute Acircumflex -50 KPX Yacute Adieresis -50 KPX Yacute Agrave -50 KPX Yacute Amacron -50 KPX Yacute Aogonek -50 KPX Yacute Aring -50 KPX Yacute Atilde -50 KPX Yacute O -15 KPX Yacute Oacute -15 KPX Yacute Ocircumflex -15 KPX Yacute Odieresis -15 KPX Yacute Ograve -15 KPX Yacute Ohungarumlaut -15 KPX Yacute Omacron -15 KPX Yacute Oslash -15 KPX Yacute Otilde -15 KPX Yacute a -92 KPX Yacute aacute -92 KPX Yacute abreve -92 KPX Yacute acircumflex -92 KPX Yacute adieresis -92 KPX Yacute agrave -92 KPX Yacute amacron -92 KPX Yacute aogonek -92 KPX Yacute aring -92 KPX Yacute atilde -92 KPX Yacute colon -65 KPX Yacute comma -92 KPX Yacute e -92 KPX Yacute eacute -92 KPX Yacute ecaron -92 KPX Yacute ecircumflex -92 KPX Yacute edieresis -52 KPX Yacute edotaccent -92 KPX Yacute egrave -52 KPX Yacute emacron -52 KPX Yacute eogonek -92 KPX Yacute hyphen -74 KPX Yacute i -74 KPX Yacute iacute -74 KPX Yacute icircumflex -34 KPX Yacute idieresis -34 KPX Yacute igrave -34 KPX Yacute imacron -34 KPX Yacute iogonek -74 KPX Yacute o -92 KPX Yacute oacute -92 KPX Yacute ocircumflex -92 KPX Yacute odieresis -92 KPX Yacute ograve -92 KPX Yacute ohungarumlaut -92 KPX Yacute omacron -92 KPX Yacute oslash -92 KPX Yacute otilde -92 KPX Yacute period -92 KPX Yacute semicolon -65 KPX Yacute u -92 KPX Yacute uacute -92 KPX Yacute ucircumflex -92 KPX Yacute udieresis -92 KPX Yacute ugrave -92 KPX Yacute uhungarumlaut -92 KPX Yacute umacron -92 KPX Yacute uogonek -92 KPX Yacute uring -92 KPX Ydieresis A -50 KPX Ydieresis Aacute -50 KPX Ydieresis Abreve -50 KPX Ydieresis Acircumflex -50 KPX Ydieresis Adieresis -50 KPX Ydieresis Agrave -50 KPX Ydieresis Amacron -50 KPX Ydieresis Aogonek -50 KPX Ydieresis Aring -50 KPX Ydieresis Atilde -50 KPX Ydieresis O -15 KPX Ydieresis Oacute -15 KPX Ydieresis Ocircumflex -15 KPX Ydieresis Odieresis -15 KPX Ydieresis Ograve -15 KPX Ydieresis Ohungarumlaut -15 KPX Ydieresis Omacron -15 KPX Ydieresis Oslash -15 KPX Ydieresis Otilde -15 KPX Ydieresis a -92 KPX Ydieresis aacute -92 KPX Ydieresis abreve -92 KPX Ydieresis acircumflex -92 KPX Ydieresis adieresis -92 KPX Ydieresis agrave -92 KPX Ydieresis amacron -92 KPX Ydieresis aogonek -92 KPX Ydieresis aring -92 KPX Ydieresis atilde -92 KPX Ydieresis colon -65 KPX Ydieresis comma -92 KPX Ydieresis e -92 KPX Ydieresis eacute -92 KPX Ydieresis ecaron -92 KPX Ydieresis ecircumflex -92 KPX Ydieresis edieresis -52 KPX Ydieresis edotaccent -92 KPX Ydieresis egrave -52 KPX Ydieresis emacron -52 KPX Ydieresis eogonek -92 KPX Ydieresis hyphen -74 KPX Ydieresis i -74 KPX Ydieresis iacute -74 KPX Ydieresis icircumflex -34 KPX Ydieresis idieresis -34 KPX Ydieresis igrave -34 KPX Ydieresis imacron -34 KPX Ydieresis iogonek -74 KPX Ydieresis o -92 KPX Ydieresis oacute -92 KPX Ydieresis ocircumflex -92 KPX Ydieresis odieresis -92 KPX Ydieresis ograve -92 KPX Ydieresis ohungarumlaut -92 KPX Ydieresis omacron -92 KPX Ydieresis oslash -92 KPX Ydieresis otilde -92 KPX Ydieresis period -92 KPX Ydieresis semicolon -65 KPX Ydieresis u -92 KPX Ydieresis uacute -92 KPX Ydieresis ucircumflex -92 KPX Ydieresis udieresis -92 KPX Ydieresis ugrave -92 KPX Ydieresis uhungarumlaut -92 KPX Ydieresis umacron -92 KPX Ydieresis uogonek -92 KPX Ydieresis uring -92 KPX a g -10 KPX a gbreve -10 KPX a gcommaaccent -10 KPX aacute g -10 KPX aacute gbreve -10 KPX aacute gcommaaccent -10 KPX abreve g -10 KPX abreve gbreve -10 KPX abreve gcommaaccent -10 KPX acircumflex g -10 KPX acircumflex gbreve -10 KPX acircumflex gcommaaccent -10 KPX adieresis g -10 KPX adieresis gbreve -10 KPX adieresis gcommaaccent -10 KPX agrave g -10 KPX agrave gbreve -10 KPX agrave gcommaaccent -10 KPX amacron g -10 KPX amacron gbreve -10 KPX amacron gcommaaccent -10 KPX aogonek g -10 KPX aogonek gbreve -10 KPX aogonek gcommaaccent -10 KPX aring g -10 KPX aring gbreve -10 KPX aring gcommaaccent -10 KPX atilde g -10 KPX atilde gbreve -10 KPX atilde gcommaaccent -10 KPX b period -40 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX c h -15 KPX c k -20 KPX c kcommaaccent -20 KPX cacute h -15 KPX cacute k -20 KPX cacute kcommaaccent -20 KPX ccaron h -15 KPX ccaron k -20 KPX ccaron kcommaaccent -20 KPX ccedilla h -15 KPX ccedilla k -20 KPX ccedilla kcommaaccent -20 KPX comma quotedblright -140 KPX comma quoteright -140 KPX e comma -10 KPX e g -40 KPX e gbreve -40 KPX e gcommaaccent -40 KPX e period -15 KPX e v -15 KPX e w -15 KPX e x -20 KPX e y -30 KPX e yacute -30 KPX e ydieresis -30 KPX eacute comma -10 KPX eacute g -40 KPX eacute gbreve -40 KPX eacute gcommaaccent -40 KPX eacute period -15 KPX eacute v -15 KPX eacute w -15 KPX eacute x -20 KPX eacute y -30 KPX eacute yacute -30 KPX eacute ydieresis -30 KPX ecaron comma -10 KPX ecaron g -40 KPX ecaron gbreve -40 KPX ecaron gcommaaccent -40 KPX ecaron period -15 KPX ecaron v -15 KPX ecaron w -15 KPX ecaron x -20 KPX ecaron y -30 KPX ecaron yacute -30 KPX ecaron ydieresis -30 KPX ecircumflex comma -10 KPX ecircumflex g -40 KPX ecircumflex gbreve -40 KPX ecircumflex gcommaaccent -40 KPX ecircumflex period -15 KPX ecircumflex v -15 KPX ecircumflex w -15 KPX ecircumflex x -20 KPX ecircumflex y -30 KPX ecircumflex yacute -30 KPX ecircumflex ydieresis -30 KPX edieresis comma -10 KPX edieresis g -40 KPX edieresis gbreve -40 KPX edieresis gcommaaccent -40 KPX edieresis period -15 KPX edieresis v -15 KPX edieresis w -15 KPX edieresis x -20 KPX edieresis y -30 KPX edieresis yacute -30 KPX edieresis ydieresis -30 KPX edotaccent comma -10 KPX edotaccent g -40 KPX edotaccent gbreve -40 KPX edotaccent gcommaaccent -40 KPX edotaccent period -15 KPX edotaccent v -15 KPX edotaccent w -15 KPX edotaccent x -20 KPX edotaccent y -30 KPX edotaccent yacute -30 KPX edotaccent ydieresis -30 KPX egrave comma -10 KPX egrave g -40 KPX egrave gbreve -40 KPX egrave gcommaaccent -40 KPX egrave period -15 KPX egrave v -15 KPX egrave w -15 KPX egrave x -20 KPX egrave y -30 KPX egrave yacute -30 KPX egrave ydieresis -30 KPX emacron comma -10 KPX emacron g -40 KPX emacron gbreve -40 KPX emacron gcommaaccent -40 KPX emacron period -15 KPX emacron v -15 KPX emacron w -15 KPX emacron x -20 KPX emacron y -30 KPX emacron yacute -30 KPX emacron ydieresis -30 KPX eogonek comma -10 KPX eogonek g -40 KPX eogonek gbreve -40 KPX eogonek gcommaaccent -40 KPX eogonek period -15 KPX eogonek v -15 KPX eogonek w -15 KPX eogonek x -20 KPX eogonek y -30 KPX eogonek yacute -30 KPX eogonek ydieresis -30 KPX f comma -10 KPX f dotlessi -60 KPX f f -18 KPX f i -20 KPX f iogonek -20 KPX f period -15 KPX f quoteright 92 KPX g comma -10 KPX g e -10 KPX g eacute -10 KPX g ecaron -10 KPX g ecircumflex -10 KPX g edieresis -10 KPX g edotaccent -10 KPX g egrave -10 KPX g emacron -10 KPX g eogonek -10 KPX g g -10 KPX g gbreve -10 KPX g gcommaaccent -10 KPX g period -15 KPX gbreve comma -10 KPX gbreve e -10 KPX gbreve eacute -10 KPX gbreve ecaron -10 KPX gbreve ecircumflex -10 KPX gbreve edieresis -10 KPX gbreve edotaccent -10 KPX gbreve egrave -10 KPX gbreve emacron -10 KPX gbreve eogonek -10 KPX gbreve g -10 KPX gbreve gbreve -10 KPX gbreve gcommaaccent -10 KPX gbreve period -15 KPX gcommaaccent comma -10 KPX gcommaaccent e -10 KPX gcommaaccent eacute -10 KPX gcommaaccent ecaron -10 KPX gcommaaccent ecircumflex -10 KPX gcommaaccent edieresis -10 KPX gcommaaccent edotaccent -10 KPX gcommaaccent egrave -10 KPX gcommaaccent emacron -10 KPX gcommaaccent eogonek -10 KPX gcommaaccent g -10 KPX gcommaaccent gbreve -10 KPX gcommaaccent gcommaaccent -10 KPX gcommaaccent period -15 KPX k e -10 KPX k eacute -10 KPX k ecaron -10 KPX k ecircumflex -10 KPX k edieresis -10 KPX k edotaccent -10 KPX k egrave -10 KPX k emacron -10 KPX k eogonek -10 KPX k o -10 KPX k oacute -10 KPX k ocircumflex -10 KPX k odieresis -10 KPX k ograve -10 KPX k ohungarumlaut -10 KPX k omacron -10 KPX k oslash -10 KPX k otilde -10 KPX k y -10 KPX k yacute -10 KPX k ydieresis -10 KPX kcommaaccent e -10 KPX kcommaaccent eacute -10 KPX kcommaaccent ecaron -10 KPX kcommaaccent ecircumflex -10 KPX kcommaaccent edieresis -10 KPX kcommaaccent edotaccent -10 KPX kcommaaccent egrave -10 KPX kcommaaccent emacron -10 KPX kcommaaccent eogonek -10 KPX kcommaaccent o -10 KPX kcommaaccent oacute -10 KPX kcommaaccent ocircumflex -10 KPX kcommaaccent odieresis -10 KPX kcommaaccent ograve -10 KPX kcommaaccent ohungarumlaut -10 KPX kcommaaccent omacron -10 KPX kcommaaccent oslash -10 KPX kcommaaccent otilde -10 KPX kcommaaccent y -10 KPX kcommaaccent yacute -10 KPX kcommaaccent ydieresis -10 KPX n v -40 KPX nacute v -40 KPX ncaron v -40 KPX ncommaaccent v -40 KPX ntilde v -40 KPX o g -10 KPX o gbreve -10 KPX o gcommaaccent -10 KPX o v -10 KPX oacute g -10 KPX oacute gbreve -10 KPX oacute gcommaaccent -10 KPX oacute v -10 KPX ocircumflex g -10 KPX ocircumflex gbreve -10 KPX ocircumflex gcommaaccent -10 KPX ocircumflex v -10 KPX odieresis g -10 KPX odieresis gbreve -10 KPX odieresis gcommaaccent -10 KPX odieresis v -10 KPX ograve g -10 KPX ograve gbreve -10 KPX ograve gcommaaccent -10 KPX ograve v -10 KPX ohungarumlaut g -10 KPX ohungarumlaut gbreve -10 KPX ohungarumlaut gcommaaccent -10 KPX ohungarumlaut v -10 KPX omacron g -10 KPX omacron gbreve -10 KPX omacron gcommaaccent -10 KPX omacron v -10 KPX oslash g -10 KPX oslash gbreve -10 KPX oslash gcommaaccent -10 KPX oslash v -10 KPX otilde g -10 KPX otilde gbreve -10 KPX otilde gcommaaccent -10 KPX otilde v -10 KPX period quotedblright -140 KPX period quoteright -140 KPX quoteleft quoteleft -111 KPX quoteright d -25 KPX quoteright dcroat -25 KPX quoteright quoteright -111 KPX quoteright r -25 KPX quoteright racute -25 KPX quoteright rcaron -25 KPX quoteright rcommaaccent -25 KPX quoteright s -40 KPX quoteright sacute -40 KPX quoteright scaron -40 KPX quoteright scedilla -40 KPX quoteright scommaaccent -40 KPX quoteright space -111 KPX quoteright t -30 KPX quoteright tcommaaccent -30 KPX quoteright v -10 KPX r a -15 KPX r aacute -15 KPX r abreve -15 KPX r acircumflex -15 KPX r adieresis -15 KPX r agrave -15 KPX r amacron -15 KPX r aogonek -15 KPX r aring -15 KPX r atilde -15 KPX r c -37 KPX r cacute -37 KPX r ccaron -37 KPX r ccedilla -37 KPX r comma -111 KPX r d -37 KPX r dcroat -37 KPX r e -37 KPX r eacute -37 KPX r ecaron -37 KPX r ecircumflex -37 KPX r edieresis -37 KPX r edotaccent -37 KPX r egrave -37 KPX r emacron -37 KPX r eogonek -37 KPX r g -37 KPX r gbreve -37 KPX r gcommaaccent -37 KPX r hyphen -20 KPX r o -45 KPX r oacute -45 KPX r ocircumflex -45 KPX r odieresis -45 KPX r ograve -45 KPX r ohungarumlaut -45 KPX r omacron -45 KPX r oslash -45 KPX r otilde -45 KPX r period -111 KPX r q -37 KPX r s -10 KPX r sacute -10 KPX r scaron -10 KPX r scedilla -10 KPX r scommaaccent -10 KPX racute a -15 KPX racute aacute -15 KPX racute abreve -15 KPX racute acircumflex -15 KPX racute adieresis -15 KPX racute agrave -15 KPX racute amacron -15 KPX racute aogonek -15 KPX racute aring -15 KPX racute atilde -15 KPX racute c -37 KPX racute cacute -37 KPX racute ccaron -37 KPX racute ccedilla -37 KPX racute comma -111 KPX racute d -37 KPX racute dcroat -37 KPX racute e -37 KPX racute eacute -37 KPX racute ecaron -37 KPX racute ecircumflex -37 KPX racute edieresis -37 KPX racute edotaccent -37 KPX racute egrave -37 KPX racute emacron -37 KPX racute eogonek -37 KPX racute g -37 KPX racute gbreve -37 KPX racute gcommaaccent -37 KPX racute hyphen -20 KPX racute o -45 KPX racute oacute -45 KPX racute ocircumflex -45 KPX racute odieresis -45 KPX racute ograve -45 KPX racute ohungarumlaut -45 KPX racute omacron -45 KPX racute oslash -45 KPX racute otilde -45 KPX racute period -111 KPX racute q -37 KPX racute s -10 KPX racute sacute -10 KPX racute scaron -10 KPX racute scedilla -10 KPX racute scommaaccent -10 KPX rcaron a -15 KPX rcaron aacute -15 KPX rcaron abreve -15 KPX rcaron acircumflex -15 KPX rcaron adieresis -15 KPX rcaron agrave -15 KPX rcaron amacron -15 KPX rcaron aogonek -15 KPX rcaron aring -15 KPX rcaron atilde -15 KPX rcaron c -37 KPX rcaron cacute -37 KPX rcaron ccaron -37 KPX rcaron ccedilla -37 KPX rcaron comma -111 KPX rcaron d -37 KPX rcaron dcroat -37 KPX rcaron e -37 KPX rcaron eacute -37 KPX rcaron ecaron -37 KPX rcaron ecircumflex -37 KPX rcaron edieresis -37 KPX rcaron edotaccent -37 KPX rcaron egrave -37 KPX rcaron emacron -37 KPX rcaron eogonek -37 KPX rcaron g -37 KPX rcaron gbreve -37 KPX rcaron gcommaaccent -37 KPX rcaron hyphen -20 KPX rcaron o -45 KPX rcaron oacute -45 KPX rcaron ocircumflex -45 KPX rcaron odieresis -45 KPX rcaron ograve -45 KPX rcaron ohungarumlaut -45 KPX rcaron omacron -45 KPX rcaron oslash -45 KPX rcaron otilde -45 KPX rcaron period -111 KPX rcaron q -37 KPX rcaron s -10 KPX rcaron sacute -10 KPX rcaron scaron -10 KPX rcaron scedilla -10 KPX rcaron scommaaccent -10 KPX rcommaaccent a -15 KPX rcommaaccent aacute -15 KPX rcommaaccent abreve -15 KPX rcommaaccent acircumflex -15 KPX rcommaaccent adieresis -15 KPX rcommaaccent agrave -15 KPX rcommaaccent amacron -15 KPX rcommaaccent aogonek -15 KPX rcommaaccent aring -15 KPX rcommaaccent atilde -15 KPX rcommaaccent c -37 KPX rcommaaccent cacute -37 KPX rcommaaccent ccaron -37 KPX rcommaaccent ccedilla -37 KPX rcommaaccent comma -111 KPX rcommaaccent d -37 KPX rcommaaccent dcroat -37 KPX rcommaaccent e -37 KPX rcommaaccent eacute -37 KPX rcommaaccent ecaron -37 KPX rcommaaccent ecircumflex -37 KPX rcommaaccent edieresis -37 KPX rcommaaccent edotaccent -37 KPX rcommaaccent egrave -37 KPX rcommaaccent emacron -37 KPX rcommaaccent eogonek -37 KPX rcommaaccent g -37 KPX rcommaaccent gbreve -37 KPX rcommaaccent gcommaaccent -37 KPX rcommaaccent hyphen -20 KPX rcommaaccent o -45 KPX rcommaaccent oacute -45 KPX rcommaaccent ocircumflex -45 KPX rcommaaccent odieresis -45 KPX rcommaaccent ograve -45 KPX rcommaaccent ohungarumlaut -45 KPX rcommaaccent omacron -45 KPX rcommaaccent oslash -45 KPX rcommaaccent otilde -45 KPX rcommaaccent period -111 KPX rcommaaccent q -37 KPX rcommaaccent s -10 KPX rcommaaccent sacute -10 KPX rcommaaccent scaron -10 KPX rcommaaccent scedilla -10 KPX rcommaaccent scommaaccent -10 KPX space A -18 KPX space Aacute -18 KPX space Abreve -18 KPX space Acircumflex -18 KPX space Adieresis -18 KPX space Agrave -18 KPX space Amacron -18 KPX space Aogonek -18 KPX space Aring -18 KPX space Atilde -18 KPX space T -18 KPX space Tcaron -18 KPX space Tcommaaccent -18 KPX space V -35 KPX space W -40 KPX space Y -75 KPX space Yacute -75 KPX space Ydieresis -75 KPX v comma -74 KPX v period -74 KPX w comma -74 KPX w period -74 KPX y comma -55 KPX y period -55 KPX yacute comma -55 KPX yacute period -55 KPX ydieresis comma -55 KPX ydieresis period -55 EndKernPairs EndKernData EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/Times-Roman.afm000066400000000000000000001726371320103431700273760ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 12:49:17 1997 Comment UniqueID 43068 Comment VMusage 43909 54934 FontName Times-Roman FullName Times Roman FamilyName Times Weight Roman ItalicAngle 0 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -168 -218 1000 898 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme AdobeStandardEncoding CapHeight 662 XHeight 450 Ascender 683 Descender -217 StdHW 28 StdVW 84 StartCharMetrics 315 C 32 ; WX 250 ; N space ; B 0 0 0 0 ; C 33 ; WX 333 ; N exclam ; B 130 -9 238 676 ; C 34 ; WX 408 ; N quotedbl ; B 77 431 331 676 ; C 35 ; WX 500 ; N numbersign ; B 5 0 496 662 ; C 36 ; WX 500 ; N dollar ; B 44 -87 457 727 ; C 37 ; WX 833 ; N percent ; B 61 -13 772 676 ; C 38 ; WX 778 ; N ampersand ; B 42 -13 750 676 ; C 39 ; WX 333 ; N quoteright ; B 79 433 218 676 ; C 40 ; WX 333 ; N parenleft ; B 48 -177 304 676 ; C 41 ; WX 333 ; N parenright ; B 29 -177 285 676 ; C 42 ; WX 500 ; N asterisk ; B 69 265 432 676 ; C 43 ; WX 564 ; N plus ; B 30 0 534 506 ; C 44 ; WX 250 ; N comma ; B 56 -141 195 102 ; C 45 ; WX 333 ; N hyphen ; B 39 194 285 257 ; C 46 ; WX 250 ; N period ; B 70 -11 181 100 ; C 47 ; WX 278 ; N slash ; B -9 -14 287 676 ; C 48 ; WX 500 ; N zero ; B 24 -14 476 676 ; C 49 ; WX 500 ; N one ; B 111 0 394 676 ; C 50 ; WX 500 ; N two ; B 30 0 475 676 ; C 51 ; WX 500 ; N three ; B 43 -14 431 676 ; C 52 ; WX 500 ; N four ; B 12 0 472 676 ; C 53 ; WX 500 ; N five ; B 32 -14 438 688 ; C 54 ; WX 500 ; N six ; B 34 -14 468 684 ; C 55 ; WX 500 ; N seven ; B 20 -8 449 662 ; C 56 ; WX 500 ; N eight ; B 56 -14 445 676 ; C 57 ; WX 500 ; N nine ; B 30 -22 459 676 ; C 58 ; WX 278 ; N colon ; B 81 -11 192 459 ; C 59 ; WX 278 ; N semicolon ; B 80 -141 219 459 ; C 60 ; WX 564 ; N less ; B 28 -8 536 514 ; C 61 ; WX 564 ; N equal ; B 30 120 534 386 ; C 62 ; WX 564 ; N greater ; B 28 -8 536 514 ; C 63 ; WX 444 ; N question ; B 68 -8 414 676 ; C 64 ; WX 921 ; N at ; B 116 -14 809 676 ; C 65 ; WX 722 ; N A ; B 15 0 706 674 ; C 66 ; WX 667 ; N B ; B 17 0 593 662 ; C 67 ; WX 667 ; N C ; B 28 -14 633 676 ; C 68 ; WX 722 ; N D ; B 16 0 685 662 ; C 69 ; WX 611 ; N E ; B 12 0 597 662 ; C 70 ; WX 556 ; N F ; B 12 0 546 662 ; C 71 ; WX 722 ; N G ; B 32 -14 709 676 ; C 72 ; WX 722 ; N H ; B 19 0 702 662 ; C 73 ; WX 333 ; N I ; B 18 0 315 662 ; C 74 ; WX 389 ; N J ; B 10 -14 370 662 ; C 75 ; WX 722 ; N K ; B 34 0 723 662 ; C 76 ; WX 611 ; N L ; B 12 0 598 662 ; C 77 ; WX 889 ; N M ; B 12 0 863 662 ; C 78 ; WX 722 ; N N ; B 12 -11 707 662 ; C 79 ; WX 722 ; N O ; B 34 -14 688 676 ; C 80 ; WX 556 ; N P ; B 16 0 542 662 ; C 81 ; WX 722 ; N Q ; B 34 -178 701 676 ; C 82 ; WX 667 ; N R ; B 17 0 659 662 ; C 83 ; WX 556 ; N S ; B 42 -14 491 676 ; C 84 ; WX 611 ; N T ; B 17 0 593 662 ; C 85 ; WX 722 ; N U ; B 14 -14 705 662 ; C 86 ; WX 722 ; N V ; B 16 -11 697 662 ; C 87 ; WX 944 ; N W ; B 5 -11 932 662 ; C 88 ; WX 722 ; N X ; B 10 0 704 662 ; C 89 ; WX 722 ; N Y ; B 22 0 703 662 ; C 90 ; WX 611 ; N Z ; B 9 0 597 662 ; C 91 ; WX 333 ; N bracketleft ; B 88 -156 299 662 ; C 92 ; WX 278 ; N backslash ; B -9 -14 287 676 ; C 93 ; WX 333 ; N bracketright ; B 34 -156 245 662 ; C 94 ; WX 469 ; N asciicircum ; B 24 297 446 662 ; C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; C 96 ; WX 333 ; N quoteleft ; B 115 433 254 676 ; C 97 ; WX 444 ; N a ; B 37 -10 442 460 ; C 98 ; WX 500 ; N b ; B 3 -10 468 683 ; C 99 ; WX 444 ; N c ; B 25 -10 412 460 ; C 100 ; WX 500 ; N d ; B 27 -10 491 683 ; C 101 ; WX 444 ; N e ; B 25 -10 424 460 ; C 102 ; WX 333 ; N f ; B 20 0 383 683 ; L i fi ; L l fl ; C 103 ; WX 500 ; N g ; B 28 -218 470 460 ; C 104 ; WX 500 ; N h ; B 9 0 487 683 ; C 105 ; WX 278 ; N i ; B 16 0 253 683 ; C 106 ; WX 278 ; N j ; B -70 -218 194 683 ; C 107 ; WX 500 ; N k ; B 7 0 505 683 ; C 108 ; WX 278 ; N l ; B 19 0 257 683 ; C 109 ; WX 778 ; N m ; B 16 0 775 460 ; C 110 ; WX 500 ; N n ; B 16 0 485 460 ; C 111 ; WX 500 ; N o ; B 29 -10 470 460 ; C 112 ; WX 500 ; N p ; B 5 -217 470 460 ; C 113 ; WX 500 ; N q ; B 24 -217 488 460 ; C 114 ; WX 333 ; N r ; B 5 0 335 460 ; C 115 ; WX 389 ; N s ; B 51 -10 348 460 ; C 116 ; WX 278 ; N t ; B 13 -10 279 579 ; C 117 ; WX 500 ; N u ; B 9 -10 479 450 ; C 118 ; WX 500 ; N v ; B 19 -14 477 450 ; C 119 ; WX 722 ; N w ; B 21 -14 694 450 ; C 120 ; WX 500 ; N x ; B 17 0 479 450 ; C 121 ; WX 500 ; N y ; B 14 -218 475 450 ; C 122 ; WX 444 ; N z ; B 27 0 418 450 ; C 123 ; WX 480 ; N braceleft ; B 100 -181 350 680 ; C 124 ; WX 200 ; N bar ; B 67 -218 133 782 ; C 125 ; WX 480 ; N braceright ; B 130 -181 380 680 ; C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ; C 161 ; WX 333 ; N exclamdown ; B 97 -218 205 467 ; C 162 ; WX 500 ; N cent ; B 53 -138 448 579 ; C 163 ; WX 500 ; N sterling ; B 12 -8 490 676 ; C 164 ; WX 167 ; N fraction ; B -168 -14 331 676 ; C 165 ; WX 500 ; N yen ; B -53 0 512 662 ; C 166 ; WX 500 ; N florin ; B 7 -189 490 676 ; C 167 ; WX 500 ; N section ; B 70 -148 426 676 ; C 168 ; WX 500 ; N currency ; B -22 58 522 602 ; C 169 ; WX 180 ; N quotesingle ; B 48 431 133 676 ; C 170 ; WX 444 ; N quotedblleft ; B 43 433 414 676 ; C 171 ; WX 500 ; N guillemotleft ; B 42 33 456 416 ; C 172 ; WX 333 ; N guilsinglleft ; B 63 33 285 416 ; C 173 ; WX 333 ; N guilsinglright ; B 48 33 270 416 ; C 174 ; WX 556 ; N fi ; B 31 0 521 683 ; C 175 ; WX 556 ; N fl ; B 32 0 521 683 ; C 177 ; WX 500 ; N endash ; B 0 201 500 250 ; C 178 ; WX 500 ; N dagger ; B 59 -149 442 676 ; C 179 ; WX 500 ; N daggerdbl ; B 58 -153 442 676 ; C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ; C 182 ; WX 453 ; N paragraph ; B -22 -154 450 662 ; C 183 ; WX 350 ; N bullet ; B 40 196 310 466 ; C 184 ; WX 333 ; N quotesinglbase ; B 79 -141 218 102 ; C 185 ; WX 444 ; N quotedblbase ; B 45 -141 416 102 ; C 186 ; WX 444 ; N quotedblright ; B 30 433 401 676 ; C 187 ; WX 500 ; N guillemotright ; B 44 33 458 416 ; C 188 ; WX 1000 ; N ellipsis ; B 111 -11 888 100 ; C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 706 ; C 191 ; WX 444 ; N questiondown ; B 30 -218 376 466 ; C 193 ; WX 333 ; N grave ; B 19 507 242 678 ; C 194 ; WX 333 ; N acute ; B 93 507 317 678 ; C 195 ; WX 333 ; N circumflex ; B 11 507 322 674 ; C 196 ; WX 333 ; N tilde ; B 1 532 331 638 ; C 197 ; WX 333 ; N macron ; B 11 547 322 601 ; C 198 ; WX 333 ; N breve ; B 26 507 307 664 ; C 199 ; WX 333 ; N dotaccent ; B 118 581 216 681 ; C 200 ; WX 333 ; N dieresis ; B 18 581 315 681 ; C 202 ; WX 333 ; N ring ; B 67 512 266 711 ; C 203 ; WX 333 ; N cedilla ; B 52 -215 261 0 ; C 205 ; WX 333 ; N hungarumlaut ; B -3 507 377 678 ; C 206 ; WX 333 ; N ogonek ; B 62 -165 243 0 ; C 207 ; WX 333 ; N caron ; B 11 507 322 674 ; C 208 ; WX 1000 ; N emdash ; B 0 201 1000 250 ; C 225 ; WX 889 ; N AE ; B 0 0 863 662 ; C 227 ; WX 276 ; N ordfeminine ; B 4 394 270 676 ; C 232 ; WX 611 ; N Lslash ; B 12 0 598 662 ; C 233 ; WX 722 ; N Oslash ; B 34 -80 688 734 ; C 234 ; WX 889 ; N OE ; B 30 -6 885 668 ; C 235 ; WX 310 ; N ordmasculine ; B 6 394 304 676 ; C 241 ; WX 667 ; N ae ; B 38 -10 632 460 ; C 245 ; WX 278 ; N dotlessi ; B 16 0 253 460 ; C 248 ; WX 278 ; N lslash ; B 19 0 259 683 ; C 249 ; WX 500 ; N oslash ; B 29 -112 470 551 ; C 250 ; WX 722 ; N oe ; B 30 -10 690 460 ; C 251 ; WX 500 ; N germandbls ; B 12 -9 468 683 ; C -1 ; WX 333 ; N Idieresis ; B 18 0 315 835 ; C -1 ; WX 444 ; N eacute ; B 25 -10 424 678 ; C -1 ; WX 444 ; N abreve ; B 37 -10 442 664 ; C -1 ; WX 500 ; N uhungarumlaut ; B 9 -10 501 678 ; C -1 ; WX 444 ; N ecaron ; B 25 -10 424 674 ; C -1 ; WX 722 ; N Ydieresis ; B 22 0 703 835 ; C -1 ; WX 564 ; N divide ; B 30 -10 534 516 ; C -1 ; WX 722 ; N Yacute ; B 22 0 703 890 ; C -1 ; WX 722 ; N Acircumflex ; B 15 0 706 886 ; C -1 ; WX 444 ; N aacute ; B 37 -10 442 678 ; C -1 ; WX 722 ; N Ucircumflex ; B 14 -14 705 886 ; C -1 ; WX 500 ; N yacute ; B 14 -218 475 678 ; C -1 ; WX 389 ; N scommaaccent ; B 51 -218 348 460 ; C -1 ; WX 444 ; N ecircumflex ; B 25 -10 424 674 ; C -1 ; WX 722 ; N Uring ; B 14 -14 705 898 ; C -1 ; WX 722 ; N Udieresis ; B 14 -14 705 835 ; C -1 ; WX 444 ; N aogonek ; B 37 -165 469 460 ; C -1 ; WX 722 ; N Uacute ; B 14 -14 705 890 ; C -1 ; WX 500 ; N uogonek ; B 9 -155 487 450 ; C -1 ; WX 611 ; N Edieresis ; B 12 0 597 835 ; C -1 ; WX 722 ; N Dcroat ; B 16 0 685 662 ; C -1 ; WX 250 ; N commaaccent ; B 59 -218 184 -50 ; C -1 ; WX 760 ; N copyright ; B 38 -14 722 676 ; C -1 ; WX 611 ; N Emacron ; B 12 0 597 813 ; C -1 ; WX 444 ; N ccaron ; B 25 -10 412 674 ; C -1 ; WX 444 ; N aring ; B 37 -10 442 711 ; C -1 ; WX 722 ; N Ncommaaccent ; B 12 -198 707 662 ; C -1 ; WX 278 ; N lacute ; B 19 0 290 890 ; C -1 ; WX 444 ; N agrave ; B 37 -10 442 678 ; C -1 ; WX 611 ; N Tcommaaccent ; B 17 -218 593 662 ; C -1 ; WX 667 ; N Cacute ; B 28 -14 633 890 ; C -1 ; WX 444 ; N atilde ; B 37 -10 442 638 ; C -1 ; WX 611 ; N Edotaccent ; B 12 0 597 835 ; C -1 ; WX 389 ; N scaron ; B 39 -10 350 674 ; C -1 ; WX 389 ; N scedilla ; B 51 -215 348 460 ; C -1 ; WX 278 ; N iacute ; B 16 0 290 678 ; C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ; C -1 ; WX 667 ; N Rcaron ; B 17 0 659 886 ; C -1 ; WX 722 ; N Gcommaaccent ; B 32 -218 709 676 ; C -1 ; WX 500 ; N ucircumflex ; B 9 -10 479 674 ; C -1 ; WX 444 ; N acircumflex ; B 37 -10 442 674 ; C -1 ; WX 722 ; N Amacron ; B 15 0 706 813 ; C -1 ; WX 333 ; N rcaron ; B 5 0 335 674 ; C -1 ; WX 444 ; N ccedilla ; B 25 -215 412 460 ; C -1 ; WX 611 ; N Zdotaccent ; B 9 0 597 835 ; C -1 ; WX 556 ; N Thorn ; B 16 0 542 662 ; C -1 ; WX 722 ; N Omacron ; B 34 -14 688 813 ; C -1 ; WX 667 ; N Racute ; B 17 0 659 890 ; C -1 ; WX 556 ; N Sacute ; B 42 -14 491 890 ; C -1 ; WX 588 ; N dcaron ; B 27 -10 589 695 ; C -1 ; WX 722 ; N Umacron ; B 14 -14 705 813 ; C -1 ; WX 500 ; N uring ; B 9 -10 479 711 ; C -1 ; WX 300 ; N threesuperior ; B 15 262 291 676 ; C -1 ; WX 722 ; N Ograve ; B 34 -14 688 890 ; C -1 ; WX 722 ; N Agrave ; B 15 0 706 890 ; C -1 ; WX 722 ; N Abreve ; B 15 0 706 876 ; C -1 ; WX 564 ; N multiply ; B 38 8 527 497 ; C -1 ; WX 500 ; N uacute ; B 9 -10 479 678 ; C -1 ; WX 611 ; N Tcaron ; B 17 0 593 886 ; C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ; C -1 ; WX 500 ; N ydieresis ; B 14 -218 475 623 ; C -1 ; WX 722 ; N Nacute ; B 12 -11 707 890 ; C -1 ; WX 278 ; N icircumflex ; B -16 0 295 674 ; C -1 ; WX 611 ; N Ecircumflex ; B 12 0 597 886 ; C -1 ; WX 444 ; N adieresis ; B 37 -10 442 623 ; C -1 ; WX 444 ; N edieresis ; B 25 -10 424 623 ; C -1 ; WX 444 ; N cacute ; B 25 -10 413 678 ; C -1 ; WX 500 ; N nacute ; B 16 0 485 678 ; C -1 ; WX 500 ; N umacron ; B 9 -10 479 601 ; C -1 ; WX 722 ; N Ncaron ; B 12 -11 707 886 ; C -1 ; WX 333 ; N Iacute ; B 18 0 317 890 ; C -1 ; WX 564 ; N plusminus ; B 30 0 534 506 ; C -1 ; WX 200 ; N brokenbar ; B 67 -143 133 707 ; C -1 ; WX 760 ; N registered ; B 38 -14 722 676 ; C -1 ; WX 722 ; N Gbreve ; B 32 -14 709 876 ; C -1 ; WX 333 ; N Idotaccent ; B 18 0 315 835 ; C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ; C -1 ; WX 611 ; N Egrave ; B 12 0 597 890 ; C -1 ; WX 333 ; N racute ; B 5 0 335 678 ; C -1 ; WX 500 ; N omacron ; B 29 -10 470 601 ; C -1 ; WX 611 ; N Zacute ; B 9 0 597 890 ; C -1 ; WX 611 ; N Zcaron ; B 9 0 597 886 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 523 666 ; C -1 ; WX 722 ; N Eth ; B 16 0 685 662 ; C -1 ; WX 667 ; N Ccedilla ; B 28 -215 633 676 ; C -1 ; WX 278 ; N lcommaaccent ; B 19 -218 257 683 ; C -1 ; WX 326 ; N tcaron ; B 13 -10 318 722 ; C -1 ; WX 444 ; N eogonek ; B 25 -165 424 460 ; C -1 ; WX 722 ; N Uogonek ; B 14 -165 705 662 ; C -1 ; WX 722 ; N Aacute ; B 15 0 706 890 ; C -1 ; WX 722 ; N Adieresis ; B 15 0 706 835 ; C -1 ; WX 444 ; N egrave ; B 25 -10 424 678 ; C -1 ; WX 444 ; N zacute ; B 27 0 418 678 ; C -1 ; WX 278 ; N iogonek ; B 16 -165 265 683 ; C -1 ; WX 722 ; N Oacute ; B 34 -14 688 890 ; C -1 ; WX 500 ; N oacute ; B 29 -10 470 678 ; C -1 ; WX 444 ; N amacron ; B 37 -10 442 601 ; C -1 ; WX 389 ; N sacute ; B 51 -10 348 678 ; C -1 ; WX 278 ; N idieresis ; B -9 0 288 623 ; C -1 ; WX 722 ; N Ocircumflex ; B 34 -14 688 886 ; C -1 ; WX 722 ; N Ugrave ; B 14 -14 705 890 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C -1 ; WX 500 ; N thorn ; B 5 -217 470 683 ; C -1 ; WX 300 ; N twosuperior ; B 1 270 296 676 ; C -1 ; WX 722 ; N Odieresis ; B 34 -14 688 835 ; C -1 ; WX 500 ; N mu ; B 36 -218 512 450 ; C -1 ; WX 278 ; N igrave ; B -8 0 253 678 ; C -1 ; WX 500 ; N ohungarumlaut ; B 29 -10 491 678 ; C -1 ; WX 611 ; N Eogonek ; B 12 -165 597 662 ; C -1 ; WX 500 ; N dcroat ; B 27 -10 500 683 ; C -1 ; WX 750 ; N threequarters ; B 15 -14 718 676 ; C -1 ; WX 556 ; N Scedilla ; B 42 -215 491 676 ; C -1 ; WX 344 ; N lcaron ; B 19 0 347 695 ; C -1 ; WX 722 ; N Kcommaaccent ; B 34 -198 723 662 ; C -1 ; WX 611 ; N Lacute ; B 12 0 598 890 ; C -1 ; WX 980 ; N trademark ; B 30 256 957 662 ; C -1 ; WX 444 ; N edotaccent ; B 25 -10 424 623 ; C -1 ; WX 333 ; N Igrave ; B 18 0 315 890 ; C -1 ; WX 333 ; N Imacron ; B 11 0 322 813 ; C -1 ; WX 611 ; N Lcaron ; B 12 0 598 676 ; C -1 ; WX 750 ; N onehalf ; B 31 -14 746 676 ; C -1 ; WX 549 ; N lessequal ; B 26 0 523 666 ; C -1 ; WX 500 ; N ocircumflex ; B 29 -10 470 674 ; C -1 ; WX 500 ; N ntilde ; B 16 0 485 638 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 14 -14 705 890 ; C -1 ; WX 611 ; N Eacute ; B 12 0 597 890 ; C -1 ; WX 444 ; N emacron ; B 25 -10 424 601 ; C -1 ; WX 500 ; N gbreve ; B 28 -218 470 664 ; C -1 ; WX 750 ; N onequarter ; B 37 -14 718 676 ; C -1 ; WX 556 ; N Scaron ; B 42 -14 491 886 ; C -1 ; WX 556 ; N Scommaaccent ; B 42 -218 491 676 ; C -1 ; WX 722 ; N Ohungarumlaut ; B 34 -14 688 890 ; C -1 ; WX 400 ; N degree ; B 57 390 343 676 ; C -1 ; WX 500 ; N ograve ; B 29 -10 470 678 ; C -1 ; WX 667 ; N Ccaron ; B 28 -14 633 886 ; C -1 ; WX 500 ; N ugrave ; B 9 -10 479 678 ; C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ; C -1 ; WX 722 ; N Dcaron ; B 16 0 685 886 ; C -1 ; WX 333 ; N rcommaaccent ; B 5 -218 335 460 ; C -1 ; WX 722 ; N Ntilde ; B 12 -11 707 850 ; C -1 ; WX 500 ; N otilde ; B 29 -10 470 638 ; C -1 ; WX 667 ; N Rcommaaccent ; B 17 -198 659 662 ; C -1 ; WX 611 ; N Lcommaaccent ; B 12 -218 598 662 ; C -1 ; WX 722 ; N Atilde ; B 15 0 706 850 ; C -1 ; WX 722 ; N Aogonek ; B 15 -165 738 674 ; C -1 ; WX 722 ; N Aring ; B 15 0 706 898 ; C -1 ; WX 722 ; N Otilde ; B 34 -14 688 850 ; C -1 ; WX 444 ; N zdotaccent ; B 27 0 418 623 ; C -1 ; WX 611 ; N Ecaron ; B 12 0 597 886 ; C -1 ; WX 333 ; N Iogonek ; B 18 -165 315 662 ; C -1 ; WX 500 ; N kcommaaccent ; B 7 -218 505 683 ; C -1 ; WX 564 ; N minus ; B 30 220 534 286 ; C -1 ; WX 333 ; N Icircumflex ; B 11 0 322 886 ; C -1 ; WX 500 ; N ncaron ; B 16 0 485 674 ; C -1 ; WX 278 ; N tcommaaccent ; B 13 -218 279 579 ; C -1 ; WX 564 ; N logicalnot ; B 30 108 534 386 ; C -1 ; WX 500 ; N odieresis ; B 29 -10 470 623 ; C -1 ; WX 500 ; N udieresis ; B 9 -10 479 623 ; C -1 ; WX 549 ; N notequal ; B 12 -31 537 547 ; C -1 ; WX 500 ; N gcommaaccent ; B 28 -218 470 749 ; C -1 ; WX 500 ; N eth ; B 29 -10 471 686 ; C -1 ; WX 444 ; N zcaron ; B 27 0 418 674 ; C -1 ; WX 500 ; N ncommaaccent ; B 16 -218 485 460 ; C -1 ; WX 300 ; N onesuperior ; B 57 270 248 676 ; C -1 ; WX 278 ; N imacron ; B 6 0 271 601 ; C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2073 KPX A C -40 KPX A Cacute -40 KPX A Ccaron -40 KPX A Ccedilla -40 KPX A G -40 KPX A Gbreve -40 KPX A Gcommaaccent -40 KPX A O -55 KPX A Oacute -55 KPX A Ocircumflex -55 KPX A Odieresis -55 KPX A Ograve -55 KPX A Ohungarumlaut -55 KPX A Omacron -55 KPX A Oslash -55 KPX A Otilde -55 KPX A Q -55 KPX A T -111 KPX A Tcaron -111 KPX A Tcommaaccent -111 KPX A U -55 KPX A Uacute -55 KPX A Ucircumflex -55 KPX A Udieresis -55 KPX A Ugrave -55 KPX A Uhungarumlaut -55 KPX A Umacron -55 KPX A Uogonek -55 KPX A Uring -55 KPX A V -135 KPX A W -90 KPX A Y -105 KPX A Yacute -105 KPX A Ydieresis -105 KPX A quoteright -111 KPX A v -74 KPX A w -92 KPX A y -92 KPX A yacute -92 KPX A ydieresis -92 KPX Aacute C -40 KPX Aacute Cacute -40 KPX Aacute Ccaron -40 KPX Aacute Ccedilla -40 KPX Aacute G -40 KPX Aacute Gbreve -40 KPX Aacute Gcommaaccent -40 KPX Aacute O -55 KPX Aacute Oacute -55 KPX Aacute Ocircumflex -55 KPX Aacute Odieresis -55 KPX Aacute Ograve -55 KPX Aacute Ohungarumlaut -55 KPX Aacute Omacron -55 KPX Aacute Oslash -55 KPX Aacute Otilde -55 KPX Aacute Q -55 KPX Aacute T -111 KPX Aacute Tcaron -111 KPX Aacute Tcommaaccent -111 KPX Aacute U -55 KPX Aacute Uacute -55 KPX Aacute Ucircumflex -55 KPX Aacute Udieresis -55 KPX Aacute Ugrave -55 KPX Aacute Uhungarumlaut -55 KPX Aacute Umacron -55 KPX Aacute Uogonek -55 KPX Aacute Uring -55 KPX Aacute V -135 KPX Aacute W -90 KPX Aacute Y -105 KPX Aacute Yacute -105 KPX Aacute Ydieresis -105 KPX Aacute quoteright -111 KPX Aacute v -74 KPX Aacute w -92 KPX Aacute y -92 KPX Aacute yacute -92 KPX Aacute ydieresis -92 KPX Abreve C -40 KPX Abreve Cacute -40 KPX Abreve Ccaron -40 KPX Abreve Ccedilla -40 KPX Abreve G -40 KPX Abreve Gbreve -40 KPX Abreve Gcommaaccent -40 KPX Abreve O -55 KPX Abreve Oacute -55 KPX Abreve Ocircumflex -55 KPX Abreve Odieresis -55 KPX Abreve Ograve -55 KPX Abreve Ohungarumlaut -55 KPX Abreve Omacron -55 KPX Abreve Oslash -55 KPX Abreve Otilde -55 KPX Abreve Q -55 KPX Abreve T -111 KPX Abreve Tcaron -111 KPX Abreve Tcommaaccent -111 KPX Abreve U -55 KPX Abreve Uacute -55 KPX Abreve Ucircumflex -55 KPX Abreve Udieresis -55 KPX Abreve Ugrave -55 KPX Abreve Uhungarumlaut -55 KPX Abreve Umacron -55 KPX Abreve Uogonek -55 KPX Abreve Uring -55 KPX Abreve V -135 KPX Abreve W -90 KPX Abreve Y -105 KPX Abreve Yacute -105 KPX Abreve Ydieresis -105 KPX Abreve quoteright -111 KPX Abreve v -74 KPX Abreve w -92 KPX Abreve y -92 KPX Abreve yacute -92 KPX Abreve ydieresis -92 KPX Acircumflex C -40 KPX Acircumflex Cacute -40 KPX Acircumflex Ccaron -40 KPX Acircumflex Ccedilla -40 KPX Acircumflex G -40 KPX Acircumflex Gbreve -40 KPX Acircumflex Gcommaaccent -40 KPX Acircumflex O -55 KPX Acircumflex Oacute -55 KPX Acircumflex Ocircumflex -55 KPX Acircumflex Odieresis -55 KPX Acircumflex Ograve -55 KPX Acircumflex Ohungarumlaut -55 KPX Acircumflex Omacron -55 KPX Acircumflex Oslash -55 KPX Acircumflex Otilde -55 KPX Acircumflex Q -55 KPX Acircumflex T -111 KPX Acircumflex Tcaron -111 KPX Acircumflex Tcommaaccent -111 KPX Acircumflex U -55 KPX Acircumflex Uacute -55 KPX Acircumflex Ucircumflex -55 KPX Acircumflex Udieresis -55 KPX Acircumflex Ugrave -55 KPX Acircumflex Uhungarumlaut -55 KPX Acircumflex Umacron -55 KPX Acircumflex Uogonek -55 KPX Acircumflex Uring -55 KPX Acircumflex V -135 KPX Acircumflex W -90 KPX Acircumflex Y -105 KPX Acircumflex Yacute -105 KPX Acircumflex Ydieresis -105 KPX Acircumflex quoteright -111 KPX Acircumflex v -74 KPX Acircumflex w -92 KPX Acircumflex y -92 KPX Acircumflex yacute -92 KPX Acircumflex ydieresis -92 KPX Adieresis C -40 KPX Adieresis Cacute -40 KPX Adieresis Ccaron -40 KPX Adieresis Ccedilla -40 KPX Adieresis G -40 KPX Adieresis Gbreve -40 KPX Adieresis Gcommaaccent -40 KPX Adieresis O -55 KPX Adieresis Oacute -55 KPX Adieresis Ocircumflex -55 KPX Adieresis Odieresis -55 KPX Adieresis Ograve -55 KPX Adieresis Ohungarumlaut -55 KPX Adieresis Omacron -55 KPX Adieresis Oslash -55 KPX Adieresis Otilde -55 KPX Adieresis Q -55 KPX Adieresis T -111 KPX Adieresis Tcaron -111 KPX Adieresis Tcommaaccent -111 KPX Adieresis U -55 KPX Adieresis Uacute -55 KPX Adieresis Ucircumflex -55 KPX Adieresis Udieresis -55 KPX Adieresis Ugrave -55 KPX Adieresis Uhungarumlaut -55 KPX Adieresis Umacron -55 KPX Adieresis Uogonek -55 KPX Adieresis Uring -55 KPX Adieresis V -135 KPX Adieresis W -90 KPX Adieresis Y -105 KPX Adieresis Yacute -105 KPX Adieresis Ydieresis -105 KPX Adieresis quoteright -111 KPX Adieresis v -74 KPX Adieresis w -92 KPX Adieresis y -92 KPX Adieresis yacute -92 KPX Adieresis ydieresis -92 KPX Agrave C -40 KPX Agrave Cacute -40 KPX Agrave Ccaron -40 KPX Agrave Ccedilla -40 KPX Agrave G -40 KPX Agrave Gbreve -40 KPX Agrave Gcommaaccent -40 KPX Agrave O -55 KPX Agrave Oacute -55 KPX Agrave Ocircumflex -55 KPX Agrave Odieresis -55 KPX Agrave Ograve -55 KPX Agrave Ohungarumlaut -55 KPX Agrave Omacron -55 KPX Agrave Oslash -55 KPX Agrave Otilde -55 KPX Agrave Q -55 KPX Agrave T -111 KPX Agrave Tcaron -111 KPX Agrave Tcommaaccent -111 KPX Agrave U -55 KPX Agrave Uacute -55 KPX Agrave Ucircumflex -55 KPX Agrave Udieresis -55 KPX Agrave Ugrave -55 KPX Agrave Uhungarumlaut -55 KPX Agrave Umacron -55 KPX Agrave Uogonek -55 KPX Agrave Uring -55 KPX Agrave V -135 KPX Agrave W -90 KPX Agrave Y -105 KPX Agrave Yacute -105 KPX Agrave Ydieresis -105 KPX Agrave quoteright -111 KPX Agrave v -74 KPX Agrave w -92 KPX Agrave y -92 KPX Agrave yacute -92 KPX Agrave ydieresis -92 KPX Amacron C -40 KPX Amacron Cacute -40 KPX Amacron Ccaron -40 KPX Amacron Ccedilla -40 KPX Amacron G -40 KPX Amacron Gbreve -40 KPX Amacron Gcommaaccent -40 KPX Amacron O -55 KPX Amacron Oacute -55 KPX Amacron Ocircumflex -55 KPX Amacron Odieresis -55 KPX Amacron Ograve -55 KPX Amacron Ohungarumlaut -55 KPX Amacron Omacron -55 KPX Amacron Oslash -55 KPX Amacron Otilde -55 KPX Amacron Q -55 KPX Amacron T -111 KPX Amacron Tcaron -111 KPX Amacron Tcommaaccent -111 KPX Amacron U -55 KPX Amacron Uacute -55 KPX Amacron Ucircumflex -55 KPX Amacron Udieresis -55 KPX Amacron Ugrave -55 KPX Amacron Uhungarumlaut -55 KPX Amacron Umacron -55 KPX Amacron Uogonek -55 KPX Amacron Uring -55 KPX Amacron V -135 KPX Amacron W -90 KPX Amacron Y -105 KPX Amacron Yacute -105 KPX Amacron Ydieresis -105 KPX Amacron quoteright -111 KPX Amacron v -74 KPX Amacron w -92 KPX Amacron y -92 KPX Amacron yacute -92 KPX Amacron ydieresis -92 KPX Aogonek C -40 KPX Aogonek Cacute -40 KPX Aogonek Ccaron -40 KPX Aogonek Ccedilla -40 KPX Aogonek G -40 KPX Aogonek Gbreve -40 KPX Aogonek Gcommaaccent -40 KPX Aogonek O -55 KPX Aogonek Oacute -55 KPX Aogonek Ocircumflex -55 KPX Aogonek Odieresis -55 KPX Aogonek Ograve -55 KPX Aogonek Ohungarumlaut -55 KPX Aogonek Omacron -55 KPX Aogonek Oslash -55 KPX Aogonek Otilde -55 KPX Aogonek Q -55 KPX Aogonek T -111 KPX Aogonek Tcaron -111 KPX Aogonek Tcommaaccent -111 KPX Aogonek U -55 KPX Aogonek Uacute -55 KPX Aogonek Ucircumflex -55 KPX Aogonek Udieresis -55 KPX Aogonek Ugrave -55 KPX Aogonek Uhungarumlaut -55 KPX Aogonek Umacron -55 KPX Aogonek Uogonek -55 KPX Aogonek Uring -55 KPX Aogonek V -135 KPX Aogonek W -90 KPX Aogonek Y -105 KPX Aogonek Yacute -105 KPX Aogonek Ydieresis -105 KPX Aogonek quoteright -111 KPX Aogonek v -74 KPX Aogonek w -52 KPX Aogonek y -52 KPX Aogonek yacute -52 KPX Aogonek ydieresis -52 KPX Aring C -40 KPX Aring Cacute -40 KPX Aring Ccaron -40 KPX Aring Ccedilla -40 KPX Aring G -40 KPX Aring Gbreve -40 KPX Aring Gcommaaccent -40 KPX Aring O -55 KPX Aring Oacute -55 KPX Aring Ocircumflex -55 KPX Aring Odieresis -55 KPX Aring Ograve -55 KPX Aring Ohungarumlaut -55 KPX Aring Omacron -55 KPX Aring Oslash -55 KPX Aring Otilde -55 KPX Aring Q -55 KPX Aring T -111 KPX Aring Tcaron -111 KPX Aring Tcommaaccent -111 KPX Aring U -55 KPX Aring Uacute -55 KPX Aring Ucircumflex -55 KPX Aring Udieresis -55 KPX Aring Ugrave -55 KPX Aring Uhungarumlaut -55 KPX Aring Umacron -55 KPX Aring Uogonek -55 KPX Aring Uring -55 KPX Aring V -135 KPX Aring W -90 KPX Aring Y -105 KPX Aring Yacute -105 KPX Aring Ydieresis -105 KPX Aring quoteright -111 KPX Aring v -74 KPX Aring w -92 KPX Aring y -92 KPX Aring yacute -92 KPX Aring ydieresis -92 KPX Atilde C -40 KPX Atilde Cacute -40 KPX Atilde Ccaron -40 KPX Atilde Ccedilla -40 KPX Atilde G -40 KPX Atilde Gbreve -40 KPX Atilde Gcommaaccent -40 KPX Atilde O -55 KPX Atilde Oacute -55 KPX Atilde Ocircumflex -55 KPX Atilde Odieresis -55 KPX Atilde Ograve -55 KPX Atilde Ohungarumlaut -55 KPX Atilde Omacron -55 KPX Atilde Oslash -55 KPX Atilde Otilde -55 KPX Atilde Q -55 KPX Atilde T -111 KPX Atilde Tcaron -111 KPX Atilde Tcommaaccent -111 KPX Atilde U -55 KPX Atilde Uacute -55 KPX Atilde Ucircumflex -55 KPX Atilde Udieresis -55 KPX Atilde Ugrave -55 KPX Atilde Uhungarumlaut -55 KPX Atilde Umacron -55 KPX Atilde Uogonek -55 KPX Atilde Uring -55 KPX Atilde V -135 KPX Atilde W -90 KPX Atilde Y -105 KPX Atilde Yacute -105 KPX Atilde Ydieresis -105 KPX Atilde quoteright -111 KPX Atilde v -74 KPX Atilde w -92 KPX Atilde y -92 KPX Atilde yacute -92 KPX Atilde ydieresis -92 KPX B A -35 KPX B Aacute -35 KPX B Abreve -35 KPX B Acircumflex -35 KPX B Adieresis -35 KPX B Agrave -35 KPX B Amacron -35 KPX B Aogonek -35 KPX B Aring -35 KPX B Atilde -35 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX D A -40 KPX D Aacute -40 KPX D Abreve -40 KPX D Acircumflex -40 KPX D Adieresis -40 KPX D Agrave -40 KPX D Amacron -40 KPX D Aogonek -40 KPX D Aring -40 KPX D Atilde -40 KPX D V -40 KPX D W -30 KPX D Y -55 KPX D Yacute -55 KPX D Ydieresis -55 KPX Dcaron A -40 KPX Dcaron Aacute -40 KPX Dcaron Abreve -40 KPX Dcaron Acircumflex -40 KPX Dcaron Adieresis -40 KPX Dcaron Agrave -40 KPX Dcaron Amacron -40 KPX Dcaron Aogonek -40 KPX Dcaron Aring -40 KPX Dcaron Atilde -40 KPX Dcaron V -40 KPX Dcaron W -30 KPX Dcaron Y -55 KPX Dcaron Yacute -55 KPX Dcaron Ydieresis -55 KPX Dcroat A -40 KPX Dcroat Aacute -40 KPX Dcroat Abreve -40 KPX Dcroat Acircumflex -40 KPX Dcroat Adieresis -40 KPX Dcroat Agrave -40 KPX Dcroat Amacron -40 KPX Dcroat Aogonek -40 KPX Dcroat Aring -40 KPX Dcroat Atilde -40 KPX Dcroat V -40 KPX Dcroat W -30 KPX Dcroat Y -55 KPX Dcroat Yacute -55 KPX Dcroat Ydieresis -55 KPX F A -74 KPX F Aacute -74 KPX F Abreve -74 KPX F Acircumflex -74 KPX F Adieresis -74 KPX F Agrave -74 KPX F Amacron -74 KPX F Aogonek -74 KPX F Aring -74 KPX F Atilde -74 KPX F a -15 KPX F aacute -15 KPX F abreve -15 KPX F acircumflex -15 KPX F adieresis -15 KPX F agrave -15 KPX F amacron -15 KPX F aogonek -15 KPX F aring -15 KPX F atilde -15 KPX F comma -80 KPX F o -15 KPX F oacute -15 KPX F ocircumflex -15 KPX F odieresis -15 KPX F ograve -15 KPX F ohungarumlaut -15 KPX F omacron -15 KPX F oslash -15 KPX F otilde -15 KPX F period -80 KPX J A -60 KPX J Aacute -60 KPX J Abreve -60 KPX J Acircumflex -60 KPX J Adieresis -60 KPX J Agrave -60 KPX J Amacron -60 KPX J Aogonek -60 KPX J Aring -60 KPX J Atilde -60 KPX K O -30 KPX K Oacute -30 KPX K Ocircumflex -30 KPX K Odieresis -30 KPX K Ograve -30 KPX K Ohungarumlaut -30 KPX K Omacron -30 KPX K Oslash -30 KPX K Otilde -30 KPX K e -25 KPX K eacute -25 KPX K ecaron -25 KPX K ecircumflex -25 KPX K edieresis -25 KPX K edotaccent -25 KPX K egrave -25 KPX K emacron -25 KPX K eogonek -25 KPX K o -35 KPX K oacute -35 KPX K ocircumflex -35 KPX K odieresis -35 KPX K ograve -35 KPX K ohungarumlaut -35 KPX K omacron -35 KPX K oslash -35 KPX K otilde -35 KPX K u -15 KPX K uacute -15 KPX K ucircumflex -15 KPX K udieresis -15 KPX K ugrave -15 KPX K uhungarumlaut -15 KPX K umacron -15 KPX K uogonek -15 KPX K uring -15 KPX K y -25 KPX K yacute -25 KPX K ydieresis -25 KPX Kcommaaccent O -30 KPX Kcommaaccent Oacute -30 KPX Kcommaaccent Ocircumflex -30 KPX Kcommaaccent Odieresis -30 KPX Kcommaaccent Ograve -30 KPX Kcommaaccent Ohungarumlaut -30 KPX Kcommaaccent Omacron -30 KPX Kcommaaccent Oslash -30 KPX Kcommaaccent Otilde -30 KPX Kcommaaccent e -25 KPX Kcommaaccent eacute -25 KPX Kcommaaccent ecaron -25 KPX Kcommaaccent ecircumflex -25 KPX Kcommaaccent edieresis -25 KPX Kcommaaccent edotaccent -25 KPX Kcommaaccent egrave -25 KPX Kcommaaccent emacron -25 KPX Kcommaaccent eogonek -25 KPX Kcommaaccent o -35 KPX Kcommaaccent oacute -35 KPX Kcommaaccent ocircumflex -35 KPX Kcommaaccent odieresis -35 KPX Kcommaaccent ograve -35 KPX Kcommaaccent ohungarumlaut -35 KPX Kcommaaccent omacron -35 KPX Kcommaaccent oslash -35 KPX Kcommaaccent otilde -35 KPX Kcommaaccent u -15 KPX Kcommaaccent uacute -15 KPX Kcommaaccent ucircumflex -15 KPX Kcommaaccent udieresis -15 KPX Kcommaaccent ugrave -15 KPX Kcommaaccent uhungarumlaut -15 KPX Kcommaaccent umacron -15 KPX Kcommaaccent uogonek -15 KPX Kcommaaccent uring -15 KPX Kcommaaccent y -25 KPX Kcommaaccent yacute -25 KPX Kcommaaccent ydieresis -25 KPX L T -92 KPX L Tcaron -92 KPX L Tcommaaccent -92 KPX L V -100 KPX L W -74 KPX L Y -100 KPX L Yacute -100 KPX L Ydieresis -100 KPX L quoteright -92 KPX L y -55 KPX L yacute -55 KPX L ydieresis -55 KPX Lacute T -92 KPX Lacute Tcaron -92 KPX Lacute Tcommaaccent -92 KPX Lacute V -100 KPX Lacute W -74 KPX Lacute Y -100 KPX Lacute Yacute -100 KPX Lacute Ydieresis -100 KPX Lacute quoteright -92 KPX Lacute y -55 KPX Lacute yacute -55 KPX Lacute ydieresis -55 KPX Lcaron quoteright -92 KPX Lcaron y -55 KPX Lcaron yacute -55 KPX Lcaron ydieresis -55 KPX Lcommaaccent T -92 KPX Lcommaaccent Tcaron -92 KPX Lcommaaccent Tcommaaccent -92 KPX Lcommaaccent V -100 KPX Lcommaaccent W -74 KPX Lcommaaccent Y -100 KPX Lcommaaccent Yacute -100 KPX Lcommaaccent Ydieresis -100 KPX Lcommaaccent quoteright -92 KPX Lcommaaccent y -55 KPX Lcommaaccent yacute -55 KPX Lcommaaccent ydieresis -55 KPX Lslash T -92 KPX Lslash Tcaron -92 KPX Lslash Tcommaaccent -92 KPX Lslash V -100 KPX Lslash W -74 KPX Lslash Y -100 KPX Lslash Yacute -100 KPX Lslash Ydieresis -100 KPX Lslash quoteright -92 KPX Lslash y -55 KPX Lslash yacute -55 KPX Lslash ydieresis -55 KPX N A -35 KPX N Aacute -35 KPX N Abreve -35 KPX N Acircumflex -35 KPX N Adieresis -35 KPX N Agrave -35 KPX N Amacron -35 KPX N Aogonek -35 KPX N Aring -35 KPX N Atilde -35 KPX Nacute A -35 KPX Nacute Aacute -35 KPX Nacute Abreve -35 KPX Nacute Acircumflex -35 KPX Nacute Adieresis -35 KPX Nacute Agrave -35 KPX Nacute Amacron -35 KPX Nacute Aogonek -35 KPX Nacute Aring -35 KPX Nacute Atilde -35 KPX Ncaron A -35 KPX Ncaron Aacute -35 KPX Ncaron Abreve -35 KPX Ncaron Acircumflex -35 KPX Ncaron Adieresis -35 KPX Ncaron Agrave -35 KPX Ncaron Amacron -35 KPX Ncaron Aogonek -35 KPX Ncaron Aring -35 KPX Ncaron Atilde -35 KPX Ncommaaccent A -35 KPX Ncommaaccent Aacute -35 KPX Ncommaaccent Abreve -35 KPX Ncommaaccent Acircumflex -35 KPX Ncommaaccent Adieresis -35 KPX Ncommaaccent Agrave -35 KPX Ncommaaccent Amacron -35 KPX Ncommaaccent Aogonek -35 KPX Ncommaaccent Aring -35 KPX Ncommaaccent Atilde -35 KPX Ntilde A -35 KPX Ntilde Aacute -35 KPX Ntilde Abreve -35 KPX Ntilde Acircumflex -35 KPX Ntilde Adieresis -35 KPX Ntilde Agrave -35 KPX Ntilde Amacron -35 KPX Ntilde Aogonek -35 KPX Ntilde Aring -35 KPX Ntilde Atilde -35 KPX O A -35 KPX O Aacute -35 KPX O Abreve -35 KPX O Acircumflex -35 KPX O Adieresis -35 KPX O Agrave -35 KPX O Amacron -35 KPX O Aogonek -35 KPX O Aring -35 KPX O Atilde -35 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -35 KPX O X -40 KPX O Y -50 KPX O Yacute -50 KPX O Ydieresis -50 KPX Oacute A -35 KPX Oacute Aacute -35 KPX Oacute Abreve -35 KPX Oacute Acircumflex -35 KPX Oacute Adieresis -35 KPX Oacute Agrave -35 KPX Oacute Amacron -35 KPX Oacute Aogonek -35 KPX Oacute Aring -35 KPX Oacute Atilde -35 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -35 KPX Oacute X -40 KPX Oacute Y -50 KPX Oacute Yacute -50 KPX Oacute Ydieresis -50 KPX Ocircumflex A -35 KPX Ocircumflex Aacute -35 KPX Ocircumflex Abreve -35 KPX Ocircumflex Acircumflex -35 KPX Ocircumflex Adieresis -35 KPX Ocircumflex Agrave -35 KPX Ocircumflex Amacron -35 KPX Ocircumflex Aogonek -35 KPX Ocircumflex Aring -35 KPX Ocircumflex Atilde -35 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -35 KPX Ocircumflex X -40 KPX Ocircumflex Y -50 KPX Ocircumflex Yacute -50 KPX Ocircumflex Ydieresis -50 KPX Odieresis A -35 KPX Odieresis Aacute -35 KPX Odieresis Abreve -35 KPX Odieresis Acircumflex -35 KPX Odieresis Adieresis -35 KPX Odieresis Agrave -35 KPX Odieresis Amacron -35 KPX Odieresis Aogonek -35 KPX Odieresis Aring -35 KPX Odieresis Atilde -35 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -35 KPX Odieresis X -40 KPX Odieresis Y -50 KPX Odieresis Yacute -50 KPX Odieresis Ydieresis -50 KPX Ograve A -35 KPX Ograve Aacute -35 KPX Ograve Abreve -35 KPX Ograve Acircumflex -35 KPX Ograve Adieresis -35 KPX Ograve Agrave -35 KPX Ograve Amacron -35 KPX Ograve Aogonek -35 KPX Ograve Aring -35 KPX Ograve Atilde -35 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -35 KPX Ograve X -40 KPX Ograve Y -50 KPX Ograve Yacute -50 KPX Ograve Ydieresis -50 KPX Ohungarumlaut A -35 KPX Ohungarumlaut Aacute -35 KPX Ohungarumlaut Abreve -35 KPX Ohungarumlaut Acircumflex -35 KPX Ohungarumlaut Adieresis -35 KPX Ohungarumlaut Agrave -35 KPX Ohungarumlaut Amacron -35 KPX Ohungarumlaut Aogonek -35 KPX Ohungarumlaut Aring -35 KPX Ohungarumlaut Atilde -35 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -35 KPX Ohungarumlaut X -40 KPX Ohungarumlaut Y -50 KPX Ohungarumlaut Yacute -50 KPX Ohungarumlaut Ydieresis -50 KPX Omacron A -35 KPX Omacron Aacute -35 KPX Omacron Abreve -35 KPX Omacron Acircumflex -35 KPX Omacron Adieresis -35 KPX Omacron Agrave -35 KPX Omacron Amacron -35 KPX Omacron Aogonek -35 KPX Omacron Aring -35 KPX Omacron Atilde -35 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -35 KPX Omacron X -40 KPX Omacron Y -50 KPX Omacron Yacute -50 KPX Omacron Ydieresis -50 KPX Oslash A -35 KPX Oslash Aacute -35 KPX Oslash Abreve -35 KPX Oslash Acircumflex -35 KPX Oslash Adieresis -35 KPX Oslash Agrave -35 KPX Oslash Amacron -35 KPX Oslash Aogonek -35 KPX Oslash Aring -35 KPX Oslash Atilde -35 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -35 KPX Oslash X -40 KPX Oslash Y -50 KPX Oslash Yacute -50 KPX Oslash Ydieresis -50 KPX Otilde A -35 KPX Otilde Aacute -35 KPX Otilde Abreve -35 KPX Otilde Acircumflex -35 KPX Otilde Adieresis -35 KPX Otilde Agrave -35 KPX Otilde Amacron -35 KPX Otilde Aogonek -35 KPX Otilde Aring -35 KPX Otilde Atilde -35 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -35 KPX Otilde X -40 KPX Otilde Y -50 KPX Otilde Yacute -50 KPX Otilde Ydieresis -50 KPX P A -92 KPX P Aacute -92 KPX P Abreve -92 KPX P Acircumflex -92 KPX P Adieresis -92 KPX P Agrave -92 KPX P Amacron -92 KPX P Aogonek -92 KPX P Aring -92 KPX P Atilde -92 KPX P a -15 KPX P aacute -15 KPX P abreve -15 KPX P acircumflex -15 KPX P adieresis -15 KPX P agrave -15 KPX P amacron -15 KPX P aogonek -15 KPX P aring -15 KPX P atilde -15 KPX P comma -111 KPX P period -111 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX R O -40 KPX R Oacute -40 KPX R Ocircumflex -40 KPX R Odieresis -40 KPX R Ograve -40 KPX R Ohungarumlaut -40 KPX R Omacron -40 KPX R Oslash -40 KPX R Otilde -40 KPX R T -60 KPX R Tcaron -60 KPX R Tcommaaccent -60 KPX R U -40 KPX R Uacute -40 KPX R Ucircumflex -40 KPX R Udieresis -40 KPX R Ugrave -40 KPX R Uhungarumlaut -40 KPX R Umacron -40 KPX R Uogonek -40 KPX R Uring -40 KPX R V -80 KPX R W -55 KPX R Y -65 KPX R Yacute -65 KPX R Ydieresis -65 KPX Racute O -40 KPX Racute Oacute -40 KPX Racute Ocircumflex -40 KPX Racute Odieresis -40 KPX Racute Ograve -40 KPX Racute Ohungarumlaut -40 KPX Racute Omacron -40 KPX Racute Oslash -40 KPX Racute Otilde -40 KPX Racute T -60 KPX Racute Tcaron -60 KPX Racute Tcommaaccent -60 KPX Racute U -40 KPX Racute Uacute -40 KPX Racute Ucircumflex -40 KPX Racute Udieresis -40 KPX Racute Ugrave -40 KPX Racute Uhungarumlaut -40 KPX Racute Umacron -40 KPX Racute Uogonek -40 KPX Racute Uring -40 KPX Racute V -80 KPX Racute W -55 KPX Racute Y -65 KPX Racute Yacute -65 KPX Racute Ydieresis -65 KPX Rcaron O -40 KPX Rcaron Oacute -40 KPX Rcaron Ocircumflex -40 KPX Rcaron Odieresis -40 KPX Rcaron Ograve -40 KPX Rcaron Ohungarumlaut -40 KPX Rcaron Omacron -40 KPX Rcaron Oslash -40 KPX Rcaron Otilde -40 KPX Rcaron T -60 KPX Rcaron Tcaron -60 KPX Rcaron Tcommaaccent -60 KPX Rcaron U -40 KPX Rcaron Uacute -40 KPX Rcaron Ucircumflex -40 KPX Rcaron Udieresis -40 KPX Rcaron Ugrave -40 KPX Rcaron Uhungarumlaut -40 KPX Rcaron Umacron -40 KPX Rcaron Uogonek -40 KPX Rcaron Uring -40 KPX Rcaron V -80 KPX Rcaron W -55 KPX Rcaron Y -65 KPX Rcaron Yacute -65 KPX Rcaron Ydieresis -65 KPX Rcommaaccent O -40 KPX Rcommaaccent Oacute -40 KPX Rcommaaccent Ocircumflex -40 KPX Rcommaaccent Odieresis -40 KPX Rcommaaccent Ograve -40 KPX Rcommaaccent Ohungarumlaut -40 KPX Rcommaaccent Omacron -40 KPX Rcommaaccent Oslash -40 KPX Rcommaaccent Otilde -40 KPX Rcommaaccent T -60 KPX Rcommaaccent Tcaron -60 KPX Rcommaaccent Tcommaaccent -60 KPX Rcommaaccent U -40 KPX Rcommaaccent Uacute -40 KPX Rcommaaccent Ucircumflex -40 KPX Rcommaaccent Udieresis -40 KPX Rcommaaccent Ugrave -40 KPX Rcommaaccent Uhungarumlaut -40 KPX Rcommaaccent Umacron -40 KPX Rcommaaccent Uogonek -40 KPX Rcommaaccent Uring -40 KPX Rcommaaccent V -80 KPX Rcommaaccent W -55 KPX Rcommaaccent Y -65 KPX Rcommaaccent Yacute -65 KPX Rcommaaccent Ydieresis -65 KPX T A -93 KPX T Aacute -93 KPX T Abreve -93 KPX T Acircumflex -93 KPX T Adieresis -93 KPX T Agrave -93 KPX T Amacron -93 KPX T Aogonek -93 KPX T Aring -93 KPX T Atilde -93 KPX T O -18 KPX T Oacute -18 KPX T Ocircumflex -18 KPX T Odieresis -18 KPX T Ograve -18 KPX T Ohungarumlaut -18 KPX T Omacron -18 KPX T Oslash -18 KPX T Otilde -18 KPX T a -80 KPX T aacute -80 KPX T abreve -80 KPX T acircumflex -80 KPX T adieresis -40 KPX T agrave -40 KPX T amacron -40 KPX T aogonek -80 KPX T aring -80 KPX T atilde -40 KPX T colon -50 KPX T comma -74 KPX T e -70 KPX T eacute -70 KPX T ecaron -70 KPX T ecircumflex -70 KPX T edieresis -30 KPX T edotaccent -70 KPX T egrave -70 KPX T emacron -30 KPX T eogonek -70 KPX T hyphen -92 KPX T i -35 KPX T iacute -35 KPX T iogonek -35 KPX T o -80 KPX T oacute -80 KPX T ocircumflex -80 KPX T odieresis -80 KPX T ograve -80 KPX T ohungarumlaut -80 KPX T omacron -80 KPX T oslash -80 KPX T otilde -80 KPX T period -74 KPX T r -35 KPX T racute -35 KPX T rcaron -35 KPX T rcommaaccent -35 KPX T semicolon -55 KPX T u -45 KPX T uacute -45 KPX T ucircumflex -45 KPX T udieresis -45 KPX T ugrave -45 KPX T uhungarumlaut -45 KPX T umacron -45 KPX T uogonek -45 KPX T uring -45 KPX T w -80 KPX T y -80 KPX T yacute -80 KPX T ydieresis -80 KPX Tcaron A -93 KPX Tcaron Aacute -93 KPX Tcaron Abreve -93 KPX Tcaron Acircumflex -93 KPX Tcaron Adieresis -93 KPX Tcaron Agrave -93 KPX Tcaron Amacron -93 KPX Tcaron Aogonek -93 KPX Tcaron Aring -93 KPX Tcaron Atilde -93 KPX Tcaron O -18 KPX Tcaron Oacute -18 KPX Tcaron Ocircumflex -18 KPX Tcaron Odieresis -18 KPX Tcaron Ograve -18 KPX Tcaron Ohungarumlaut -18 KPX Tcaron Omacron -18 KPX Tcaron Oslash -18 KPX Tcaron Otilde -18 KPX Tcaron a -80 KPX Tcaron aacute -80 KPX Tcaron abreve -80 KPX Tcaron acircumflex -80 KPX Tcaron adieresis -40 KPX Tcaron agrave -40 KPX Tcaron amacron -40 KPX Tcaron aogonek -80 KPX Tcaron aring -80 KPX Tcaron atilde -40 KPX Tcaron colon -50 KPX Tcaron comma -74 KPX Tcaron e -70 KPX Tcaron eacute -70 KPX Tcaron ecaron -70 KPX Tcaron ecircumflex -30 KPX Tcaron edieresis -30 KPX Tcaron edotaccent -70 KPX Tcaron egrave -70 KPX Tcaron emacron -30 KPX Tcaron eogonek -70 KPX Tcaron hyphen -92 KPX Tcaron i -35 KPX Tcaron iacute -35 KPX Tcaron iogonek -35 KPX Tcaron o -80 KPX Tcaron oacute -80 KPX Tcaron ocircumflex -80 KPX Tcaron odieresis -80 KPX Tcaron ograve -80 KPX Tcaron ohungarumlaut -80 KPX Tcaron omacron -80 KPX Tcaron oslash -80 KPX Tcaron otilde -80 KPX Tcaron period -74 KPX Tcaron r -35 KPX Tcaron racute -35 KPX Tcaron rcaron -35 KPX Tcaron rcommaaccent -35 KPX Tcaron semicolon -55 KPX Tcaron u -45 KPX Tcaron uacute -45 KPX Tcaron ucircumflex -45 KPX Tcaron udieresis -45 KPX Tcaron ugrave -45 KPX Tcaron uhungarumlaut -45 KPX Tcaron umacron -45 KPX Tcaron uogonek -45 KPX Tcaron uring -45 KPX Tcaron w -80 KPX Tcaron y -80 KPX Tcaron yacute -80 KPX Tcaron ydieresis -80 KPX Tcommaaccent A -93 KPX Tcommaaccent Aacute -93 KPX Tcommaaccent Abreve -93 KPX Tcommaaccent Acircumflex -93 KPX Tcommaaccent Adieresis -93 KPX Tcommaaccent Agrave -93 KPX Tcommaaccent Amacron -93 KPX Tcommaaccent Aogonek -93 KPX Tcommaaccent Aring -93 KPX Tcommaaccent Atilde -93 KPX Tcommaaccent O -18 KPX Tcommaaccent Oacute -18 KPX Tcommaaccent Ocircumflex -18 KPX Tcommaaccent Odieresis -18 KPX Tcommaaccent Ograve -18 KPX Tcommaaccent Ohungarumlaut -18 KPX Tcommaaccent Omacron -18 KPX Tcommaaccent Oslash -18 KPX Tcommaaccent Otilde -18 KPX Tcommaaccent a -80 KPX Tcommaaccent aacute -80 KPX Tcommaaccent abreve -80 KPX Tcommaaccent acircumflex -80 KPX Tcommaaccent adieresis -40 KPX Tcommaaccent agrave -40 KPX Tcommaaccent amacron -40 KPX Tcommaaccent aogonek -80 KPX Tcommaaccent aring -80 KPX Tcommaaccent atilde -40 KPX Tcommaaccent colon -50 KPX Tcommaaccent comma -74 KPX Tcommaaccent e -70 KPX Tcommaaccent eacute -70 KPX Tcommaaccent ecaron -70 KPX Tcommaaccent ecircumflex -30 KPX Tcommaaccent edieresis -30 KPX Tcommaaccent edotaccent -70 KPX Tcommaaccent egrave -30 KPX Tcommaaccent emacron -70 KPX Tcommaaccent eogonek -70 KPX Tcommaaccent hyphen -92 KPX Tcommaaccent i -35 KPX Tcommaaccent iacute -35 KPX Tcommaaccent iogonek -35 KPX Tcommaaccent o -80 KPX Tcommaaccent oacute -80 KPX Tcommaaccent ocircumflex -80 KPX Tcommaaccent odieresis -80 KPX Tcommaaccent ograve -80 KPX Tcommaaccent ohungarumlaut -80 KPX Tcommaaccent omacron -80 KPX Tcommaaccent oslash -80 KPX Tcommaaccent otilde -80 KPX Tcommaaccent period -74 KPX Tcommaaccent r -35 KPX Tcommaaccent racute -35 KPX Tcommaaccent rcaron -35 KPX Tcommaaccent rcommaaccent -35 KPX Tcommaaccent semicolon -55 KPX Tcommaaccent u -45 KPX Tcommaaccent uacute -45 KPX Tcommaaccent ucircumflex -45 KPX Tcommaaccent udieresis -45 KPX Tcommaaccent ugrave -45 KPX Tcommaaccent uhungarumlaut -45 KPX Tcommaaccent umacron -45 KPX Tcommaaccent uogonek -45 KPX Tcommaaccent uring -45 KPX Tcommaaccent w -80 KPX Tcommaaccent y -80 KPX Tcommaaccent yacute -80 KPX Tcommaaccent ydieresis -80 KPX U A -40 KPX U Aacute -40 KPX U Abreve -40 KPX U Acircumflex -40 KPX U Adieresis -40 KPX U Agrave -40 KPX U Amacron -40 KPX U Aogonek -40 KPX U Aring -40 KPX U Atilde -40 KPX Uacute A -40 KPX Uacute Aacute -40 KPX Uacute Abreve -40 KPX Uacute Acircumflex -40 KPX Uacute Adieresis -40 KPX Uacute Agrave -40 KPX Uacute Amacron -40 KPX Uacute Aogonek -40 KPX Uacute Aring -40 KPX Uacute Atilde -40 KPX Ucircumflex A -40 KPX Ucircumflex Aacute -40 KPX Ucircumflex Abreve -40 KPX Ucircumflex Acircumflex -40 KPX Ucircumflex Adieresis -40 KPX Ucircumflex Agrave -40 KPX Ucircumflex Amacron -40 KPX Ucircumflex Aogonek -40 KPX Ucircumflex Aring -40 KPX Ucircumflex Atilde -40 KPX Udieresis A -40 KPX Udieresis Aacute -40 KPX Udieresis Abreve -40 KPX Udieresis Acircumflex -40 KPX Udieresis Adieresis -40 KPX Udieresis Agrave -40 KPX Udieresis Amacron -40 KPX Udieresis Aogonek -40 KPX Udieresis Aring -40 KPX Udieresis Atilde -40 KPX Ugrave A -40 KPX Ugrave Aacute -40 KPX Ugrave Abreve -40 KPX Ugrave Acircumflex -40 KPX Ugrave Adieresis -40 KPX Ugrave Agrave -40 KPX Ugrave Amacron -40 KPX Ugrave Aogonek -40 KPX Ugrave Aring -40 KPX Ugrave Atilde -40 KPX Uhungarumlaut A -40 KPX Uhungarumlaut Aacute -40 KPX Uhungarumlaut Abreve -40 KPX Uhungarumlaut Acircumflex -40 KPX Uhungarumlaut Adieresis -40 KPX Uhungarumlaut Agrave -40 KPX Uhungarumlaut Amacron -40 KPX Uhungarumlaut Aogonek -40 KPX Uhungarumlaut Aring -40 KPX Uhungarumlaut Atilde -40 KPX Umacron A -40 KPX Umacron Aacute -40 KPX Umacron Abreve -40 KPX Umacron Acircumflex -40 KPX Umacron Adieresis -40 KPX Umacron Agrave -40 KPX Umacron Amacron -40 KPX Umacron Aogonek -40 KPX Umacron Aring -40 KPX Umacron Atilde -40 KPX Uogonek A -40 KPX Uogonek Aacute -40 KPX Uogonek Abreve -40 KPX Uogonek Acircumflex -40 KPX Uogonek Adieresis -40 KPX Uogonek Agrave -40 KPX Uogonek Amacron -40 KPX Uogonek Aogonek -40 KPX Uogonek Aring -40 KPX Uogonek Atilde -40 KPX Uring A -40 KPX Uring Aacute -40 KPX Uring Abreve -40 KPX Uring Acircumflex -40 KPX Uring Adieresis -40 KPX Uring Agrave -40 KPX Uring Amacron -40 KPX Uring Aogonek -40 KPX Uring Aring -40 KPX Uring Atilde -40 KPX V A -135 KPX V Aacute -135 KPX V Abreve -135 KPX V Acircumflex -135 KPX V Adieresis -135 KPX V Agrave -135 KPX V Amacron -135 KPX V Aogonek -135 KPX V Aring -135 KPX V Atilde -135 KPX V G -15 KPX V Gbreve -15 KPX V Gcommaaccent -15 KPX V O -40 KPX V Oacute -40 KPX V Ocircumflex -40 KPX V Odieresis -40 KPX V Ograve -40 KPX V Ohungarumlaut -40 KPX V Omacron -40 KPX V Oslash -40 KPX V Otilde -40 KPX V a -111 KPX V aacute -111 KPX V abreve -111 KPX V acircumflex -71 KPX V adieresis -71 KPX V agrave -71 KPX V amacron -71 KPX V aogonek -111 KPX V aring -111 KPX V atilde -71 KPX V colon -74 KPX V comma -129 KPX V e -111 KPX V eacute -111 KPX V ecaron -71 KPX V ecircumflex -71 KPX V edieresis -71 KPX V edotaccent -111 KPX V egrave -71 KPX V emacron -71 KPX V eogonek -111 KPX V hyphen -100 KPX V i -60 KPX V iacute -60 KPX V icircumflex -20 KPX V idieresis -20 KPX V igrave -20 KPX V imacron -20 KPX V iogonek -60 KPX V o -129 KPX V oacute -129 KPX V ocircumflex -129 KPX V odieresis -89 KPX V ograve -89 KPX V ohungarumlaut -129 KPX V omacron -89 KPX V oslash -129 KPX V otilde -89 KPX V period -129 KPX V semicolon -74 KPX V u -75 KPX V uacute -75 KPX V ucircumflex -75 KPX V udieresis -75 KPX V ugrave -75 KPX V uhungarumlaut -75 KPX V umacron -75 KPX V uogonek -75 KPX V uring -75 KPX W A -120 KPX W Aacute -120 KPX W Abreve -120 KPX W Acircumflex -120 KPX W Adieresis -120 KPX W Agrave -120 KPX W Amacron -120 KPX W Aogonek -120 KPX W Aring -120 KPX W Atilde -120 KPX W O -10 KPX W Oacute -10 KPX W Ocircumflex -10 KPX W Odieresis -10 KPX W Ograve -10 KPX W Ohungarumlaut -10 KPX W Omacron -10 KPX W Oslash -10 KPX W Otilde -10 KPX W a -80 KPX W aacute -80 KPX W abreve -80 KPX W acircumflex -80 KPX W adieresis -80 KPX W agrave -80 KPX W amacron -80 KPX W aogonek -80 KPX W aring -80 KPX W atilde -80 KPX W colon -37 KPX W comma -92 KPX W e -80 KPX W eacute -80 KPX W ecaron -80 KPX W ecircumflex -80 KPX W edieresis -40 KPX W edotaccent -80 KPX W egrave -40 KPX W emacron -40 KPX W eogonek -80 KPX W hyphen -65 KPX W i -40 KPX W iacute -40 KPX W iogonek -40 KPX W o -80 KPX W oacute -80 KPX W ocircumflex -80 KPX W odieresis -80 KPX W ograve -80 KPX W ohungarumlaut -80 KPX W omacron -80 KPX W oslash -80 KPX W otilde -80 KPX W period -92 KPX W semicolon -37 KPX W u -50 KPX W uacute -50 KPX W ucircumflex -50 KPX W udieresis -50 KPX W ugrave -50 KPX W uhungarumlaut -50 KPX W umacron -50 KPX W uogonek -50 KPX W uring -50 KPX W y -73 KPX W yacute -73 KPX W ydieresis -73 KPX Y A -120 KPX Y Aacute -120 KPX Y Abreve -120 KPX Y Acircumflex -120 KPX Y Adieresis -120 KPX Y Agrave -120 KPX Y Amacron -120 KPX Y Aogonek -120 KPX Y Aring -120 KPX Y Atilde -120 KPX Y O -30 KPX Y Oacute -30 KPX Y Ocircumflex -30 KPX Y Odieresis -30 KPX Y Ograve -30 KPX Y Ohungarumlaut -30 KPX Y Omacron -30 KPX Y Oslash -30 KPX Y Otilde -30 KPX Y a -100 KPX Y aacute -100 KPX Y abreve -100 KPX Y acircumflex -100 KPX Y adieresis -60 KPX Y agrave -60 KPX Y amacron -60 KPX Y aogonek -100 KPX Y aring -100 KPX Y atilde -60 KPX Y colon -92 KPX Y comma -129 KPX Y e -100 KPX Y eacute -100 KPX Y ecaron -100 KPX Y ecircumflex -100 KPX Y edieresis -60 KPX Y edotaccent -100 KPX Y egrave -60 KPX Y emacron -60 KPX Y eogonek -100 KPX Y hyphen -111 KPX Y i -55 KPX Y iacute -55 KPX Y iogonek -55 KPX Y o -110 KPX Y oacute -110 KPX Y ocircumflex -110 KPX Y odieresis -70 KPX Y ograve -70 KPX Y ohungarumlaut -110 KPX Y omacron -70 KPX Y oslash -110 KPX Y otilde -70 KPX Y period -129 KPX Y semicolon -92 KPX Y u -111 KPX Y uacute -111 KPX Y ucircumflex -111 KPX Y udieresis -71 KPX Y ugrave -71 KPX Y uhungarumlaut -111 KPX Y umacron -71 KPX Y uogonek -111 KPX Y uring -111 KPX Yacute A -120 KPX Yacute Aacute -120 KPX Yacute Abreve -120 KPX Yacute Acircumflex -120 KPX Yacute Adieresis -120 KPX Yacute Agrave -120 KPX Yacute Amacron -120 KPX Yacute Aogonek -120 KPX Yacute Aring -120 KPX Yacute Atilde -120 KPX Yacute O -30 KPX Yacute Oacute -30 KPX Yacute Ocircumflex -30 KPX Yacute Odieresis -30 KPX Yacute Ograve -30 KPX Yacute Ohungarumlaut -30 KPX Yacute Omacron -30 KPX Yacute Oslash -30 KPX Yacute Otilde -30 KPX Yacute a -100 KPX Yacute aacute -100 KPX Yacute abreve -100 KPX Yacute acircumflex -100 KPX Yacute adieresis -60 KPX Yacute agrave -60 KPX Yacute amacron -60 KPX Yacute aogonek -100 KPX Yacute aring -100 KPX Yacute atilde -60 KPX Yacute colon -92 KPX Yacute comma -129 KPX Yacute e -100 KPX Yacute eacute -100 KPX Yacute ecaron -100 KPX Yacute ecircumflex -100 KPX Yacute edieresis -60 KPX Yacute edotaccent -100 KPX Yacute egrave -60 KPX Yacute emacron -60 KPX Yacute eogonek -100 KPX Yacute hyphen -111 KPX Yacute i -55 KPX Yacute iacute -55 KPX Yacute iogonek -55 KPX Yacute o -110 KPX Yacute oacute -110 KPX Yacute ocircumflex -110 KPX Yacute odieresis -70 KPX Yacute ograve -70 KPX Yacute ohungarumlaut -110 KPX Yacute omacron -70 KPX Yacute oslash -110 KPX Yacute otilde -70 KPX Yacute period -129 KPX Yacute semicolon -92 KPX Yacute u -111 KPX Yacute uacute -111 KPX Yacute ucircumflex -111 KPX Yacute udieresis -71 KPX Yacute ugrave -71 KPX Yacute uhungarumlaut -111 KPX Yacute umacron -71 KPX Yacute uogonek -111 KPX Yacute uring -111 KPX Ydieresis A -120 KPX Ydieresis Aacute -120 KPX Ydieresis Abreve -120 KPX Ydieresis Acircumflex -120 KPX Ydieresis Adieresis -120 KPX Ydieresis Agrave -120 KPX Ydieresis Amacron -120 KPX Ydieresis Aogonek -120 KPX Ydieresis Aring -120 KPX Ydieresis Atilde -120 KPX Ydieresis O -30 KPX Ydieresis Oacute -30 KPX Ydieresis Ocircumflex -30 KPX Ydieresis Odieresis -30 KPX Ydieresis Ograve -30 KPX Ydieresis Ohungarumlaut -30 KPX Ydieresis Omacron -30 KPX Ydieresis Oslash -30 KPX Ydieresis Otilde -30 KPX Ydieresis a -100 KPX Ydieresis aacute -100 KPX Ydieresis abreve -100 KPX Ydieresis acircumflex -100 KPX Ydieresis adieresis -60 KPX Ydieresis agrave -60 KPX Ydieresis amacron -60 KPX Ydieresis aogonek -100 KPX Ydieresis aring -100 KPX Ydieresis atilde -100 KPX Ydieresis colon -92 KPX Ydieresis comma -129 KPX Ydieresis e -100 KPX Ydieresis eacute -100 KPX Ydieresis ecaron -100 KPX Ydieresis ecircumflex -100 KPX Ydieresis edieresis -60 KPX Ydieresis edotaccent -100 KPX Ydieresis egrave -60 KPX Ydieresis emacron -60 KPX Ydieresis eogonek -100 KPX Ydieresis hyphen -111 KPX Ydieresis i -55 KPX Ydieresis iacute -55 KPX Ydieresis iogonek -55 KPX Ydieresis o -110 KPX Ydieresis oacute -110 KPX Ydieresis ocircumflex -110 KPX Ydieresis odieresis -70 KPX Ydieresis ograve -70 KPX Ydieresis ohungarumlaut -110 KPX Ydieresis omacron -70 KPX Ydieresis oslash -110 KPX Ydieresis otilde -70 KPX Ydieresis period -129 KPX Ydieresis semicolon -92 KPX Ydieresis u -111 KPX Ydieresis uacute -111 KPX Ydieresis ucircumflex -111 KPX Ydieresis udieresis -71 KPX Ydieresis ugrave -71 KPX Ydieresis uhungarumlaut -111 KPX Ydieresis umacron -71 KPX Ydieresis uogonek -111 KPX Ydieresis uring -111 KPX a v -20 KPX a w -15 KPX aacute v -20 KPX aacute w -15 KPX abreve v -20 KPX abreve w -15 KPX acircumflex v -20 KPX acircumflex w -15 KPX adieresis v -20 KPX adieresis w -15 KPX agrave v -20 KPX agrave w -15 KPX amacron v -20 KPX amacron w -15 KPX aogonek v -20 KPX aogonek w -15 KPX aring v -20 KPX aring w -15 KPX atilde v -20 KPX atilde w -15 KPX b period -40 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX b v -15 KPX c y -15 KPX c yacute -15 KPX c ydieresis -15 KPX cacute y -15 KPX cacute yacute -15 KPX cacute ydieresis -15 KPX ccaron y -15 KPX ccaron yacute -15 KPX ccaron ydieresis -15 KPX ccedilla y -15 KPX ccedilla yacute -15 KPX ccedilla ydieresis -15 KPX comma quotedblright -70 KPX comma quoteright -70 KPX e g -15 KPX e gbreve -15 KPX e gcommaaccent -15 KPX e v -25 KPX e w -25 KPX e x -15 KPX e y -15 KPX e yacute -15 KPX e ydieresis -15 KPX eacute g -15 KPX eacute gbreve -15 KPX eacute gcommaaccent -15 KPX eacute v -25 KPX eacute w -25 KPX eacute x -15 KPX eacute y -15 KPX eacute yacute -15 KPX eacute ydieresis -15 KPX ecaron g -15 KPX ecaron gbreve -15 KPX ecaron gcommaaccent -15 KPX ecaron v -25 KPX ecaron w -25 KPX ecaron x -15 KPX ecaron y -15 KPX ecaron yacute -15 KPX ecaron ydieresis -15 KPX ecircumflex g -15 KPX ecircumflex gbreve -15 KPX ecircumflex gcommaaccent -15 KPX ecircumflex v -25 KPX ecircumflex w -25 KPX ecircumflex x -15 KPX ecircumflex y -15 KPX ecircumflex yacute -15 KPX ecircumflex ydieresis -15 KPX edieresis g -15 KPX edieresis gbreve -15 KPX edieresis gcommaaccent -15 KPX edieresis v -25 KPX edieresis w -25 KPX edieresis x -15 KPX edieresis y -15 KPX edieresis yacute -15 KPX edieresis ydieresis -15 KPX edotaccent g -15 KPX edotaccent gbreve -15 KPX edotaccent gcommaaccent -15 KPX edotaccent v -25 KPX edotaccent w -25 KPX edotaccent x -15 KPX edotaccent y -15 KPX edotaccent yacute -15 KPX edotaccent ydieresis -15 KPX egrave g -15 KPX egrave gbreve -15 KPX egrave gcommaaccent -15 KPX egrave v -25 KPX egrave w -25 KPX egrave x -15 KPX egrave y -15 KPX egrave yacute -15 KPX egrave ydieresis -15 KPX emacron g -15 KPX emacron gbreve -15 KPX emacron gcommaaccent -15 KPX emacron v -25 KPX emacron w -25 KPX emacron x -15 KPX emacron y -15 KPX emacron yacute -15 KPX emacron ydieresis -15 KPX eogonek g -15 KPX eogonek gbreve -15 KPX eogonek gcommaaccent -15 KPX eogonek v -25 KPX eogonek w -25 KPX eogonek x -15 KPX eogonek y -15 KPX eogonek yacute -15 KPX eogonek ydieresis -15 KPX f a -10 KPX f aacute -10 KPX f abreve -10 KPX f acircumflex -10 KPX f adieresis -10 KPX f agrave -10 KPX f amacron -10 KPX f aogonek -10 KPX f aring -10 KPX f atilde -10 KPX f dotlessi -50 KPX f f -25 KPX f i -20 KPX f iacute -20 KPX f quoteright 55 KPX g a -5 KPX g aacute -5 KPX g abreve -5 KPX g acircumflex -5 KPX g adieresis -5 KPX g agrave -5 KPX g amacron -5 KPX g aogonek -5 KPX g aring -5 KPX g atilde -5 KPX gbreve a -5 KPX gbreve aacute -5 KPX gbreve abreve -5 KPX gbreve acircumflex -5 KPX gbreve adieresis -5 KPX gbreve agrave -5 KPX gbreve amacron -5 KPX gbreve aogonek -5 KPX gbreve aring -5 KPX gbreve atilde -5 KPX gcommaaccent a -5 KPX gcommaaccent aacute -5 KPX gcommaaccent abreve -5 KPX gcommaaccent acircumflex -5 KPX gcommaaccent adieresis -5 KPX gcommaaccent agrave -5 KPX gcommaaccent amacron -5 KPX gcommaaccent aogonek -5 KPX gcommaaccent aring -5 KPX gcommaaccent atilde -5 KPX h y -5 KPX h yacute -5 KPX h ydieresis -5 KPX i v -25 KPX iacute v -25 KPX icircumflex v -25 KPX idieresis v -25 KPX igrave v -25 KPX imacron v -25 KPX iogonek v -25 KPX k e -10 KPX k eacute -10 KPX k ecaron -10 KPX k ecircumflex -10 KPX k edieresis -10 KPX k edotaccent -10 KPX k egrave -10 KPX k emacron -10 KPX k eogonek -10 KPX k o -10 KPX k oacute -10 KPX k ocircumflex -10 KPX k odieresis -10 KPX k ograve -10 KPX k ohungarumlaut -10 KPX k omacron -10 KPX k oslash -10 KPX k otilde -10 KPX k y -15 KPX k yacute -15 KPX k ydieresis -15 KPX kcommaaccent e -10 KPX kcommaaccent eacute -10 KPX kcommaaccent ecaron -10 KPX kcommaaccent ecircumflex -10 KPX kcommaaccent edieresis -10 KPX kcommaaccent edotaccent -10 KPX kcommaaccent egrave -10 KPX kcommaaccent emacron -10 KPX kcommaaccent eogonek -10 KPX kcommaaccent o -10 KPX kcommaaccent oacute -10 KPX kcommaaccent ocircumflex -10 KPX kcommaaccent odieresis -10 KPX kcommaaccent ograve -10 KPX kcommaaccent ohungarumlaut -10 KPX kcommaaccent omacron -10 KPX kcommaaccent oslash -10 KPX kcommaaccent otilde -10 KPX kcommaaccent y -15 KPX kcommaaccent yacute -15 KPX kcommaaccent ydieresis -15 KPX l w -10 KPX lacute w -10 KPX lcommaaccent w -10 KPX lslash w -10 KPX n v -40 KPX n y -15 KPX n yacute -15 KPX n ydieresis -15 KPX nacute v -40 KPX nacute y -15 KPX nacute yacute -15 KPX nacute ydieresis -15 KPX ncaron v -40 KPX ncaron y -15 KPX ncaron yacute -15 KPX ncaron ydieresis -15 KPX ncommaaccent v -40 KPX ncommaaccent y -15 KPX ncommaaccent yacute -15 KPX ncommaaccent ydieresis -15 KPX ntilde v -40 KPX ntilde y -15 KPX ntilde yacute -15 KPX ntilde ydieresis -15 KPX o v -15 KPX o w -25 KPX o y -10 KPX o yacute -10 KPX o ydieresis -10 KPX oacute v -15 KPX oacute w -25 KPX oacute y -10 KPX oacute yacute -10 KPX oacute ydieresis -10 KPX ocircumflex v -15 KPX ocircumflex w -25 KPX ocircumflex y -10 KPX ocircumflex yacute -10 KPX ocircumflex ydieresis -10 KPX odieresis v -15 KPX odieresis w -25 KPX odieresis y -10 KPX odieresis yacute -10 KPX odieresis ydieresis -10 KPX ograve v -15 KPX ograve w -25 KPX ograve y -10 KPX ograve yacute -10 KPX ograve ydieresis -10 KPX ohungarumlaut v -15 KPX ohungarumlaut w -25 KPX ohungarumlaut y -10 KPX ohungarumlaut yacute -10 KPX ohungarumlaut ydieresis -10 KPX omacron v -15 KPX omacron w -25 KPX omacron y -10 KPX omacron yacute -10 KPX omacron ydieresis -10 KPX oslash v -15 KPX oslash w -25 KPX oslash y -10 KPX oslash yacute -10 KPX oslash ydieresis -10 KPX otilde v -15 KPX otilde w -25 KPX otilde y -10 KPX otilde yacute -10 KPX otilde ydieresis -10 KPX p y -10 KPX p yacute -10 KPX p ydieresis -10 KPX period quotedblright -70 KPX period quoteright -70 KPX quotedblleft A -80 KPX quotedblleft Aacute -80 KPX quotedblleft Abreve -80 KPX quotedblleft Acircumflex -80 KPX quotedblleft Adieresis -80 KPX quotedblleft Agrave -80 KPX quotedblleft Amacron -80 KPX quotedblleft Aogonek -80 KPX quotedblleft Aring -80 KPX quotedblleft Atilde -80 KPX quoteleft A -80 KPX quoteleft Aacute -80 KPX quoteleft Abreve -80 KPX quoteleft Acircumflex -80 KPX quoteleft Adieresis -80 KPX quoteleft Agrave -80 KPX quoteleft Amacron -80 KPX quoteleft Aogonek -80 KPX quoteleft Aring -80 KPX quoteleft Atilde -80 KPX quoteleft quoteleft -74 KPX quoteright d -50 KPX quoteright dcroat -50 KPX quoteright l -10 KPX quoteright lacute -10 KPX quoteright lcommaaccent -10 KPX quoteright lslash -10 KPX quoteright quoteright -74 KPX quoteright r -50 KPX quoteright racute -50 KPX quoteright rcaron -50 KPX quoteright rcommaaccent -50 KPX quoteright s -55 KPX quoteright sacute -55 KPX quoteright scaron -55 KPX quoteright scedilla -55 KPX quoteright scommaaccent -55 KPX quoteright space -74 KPX quoteright t -18 KPX quoteright tcommaaccent -18 KPX quoteright v -50 KPX r comma -40 KPX r g -18 KPX r gbreve -18 KPX r gcommaaccent -18 KPX r hyphen -20 KPX r period -55 KPX racute comma -40 KPX racute g -18 KPX racute gbreve -18 KPX racute gcommaaccent -18 KPX racute hyphen -20 KPX racute period -55 KPX rcaron comma -40 KPX rcaron g -18 KPX rcaron gbreve -18 KPX rcaron gcommaaccent -18 KPX rcaron hyphen -20 KPX rcaron period -55 KPX rcommaaccent comma -40 KPX rcommaaccent g -18 KPX rcommaaccent gbreve -18 KPX rcommaaccent gcommaaccent -18 KPX rcommaaccent hyphen -20 KPX rcommaaccent period -55 KPX space A -55 KPX space Aacute -55 KPX space Abreve -55 KPX space Acircumflex -55 KPX space Adieresis -55 KPX space Agrave -55 KPX space Amacron -55 KPX space Aogonek -55 KPX space Aring -55 KPX space Atilde -55 KPX space T -18 KPX space Tcaron -18 KPX space Tcommaaccent -18 KPX space V -50 KPX space W -30 KPX space Y -90 KPX space Yacute -90 KPX space Ydieresis -90 KPX v a -25 KPX v aacute -25 KPX v abreve -25 KPX v acircumflex -25 KPX v adieresis -25 KPX v agrave -25 KPX v amacron -25 KPX v aogonek -25 KPX v aring -25 KPX v atilde -25 KPX v comma -65 KPX v e -15 KPX v eacute -15 KPX v ecaron -15 KPX v ecircumflex -15 KPX v edieresis -15 KPX v edotaccent -15 KPX v egrave -15 KPX v emacron -15 KPX v eogonek -15 KPX v o -20 KPX v oacute -20 KPX v ocircumflex -20 KPX v odieresis -20 KPX v ograve -20 KPX v ohungarumlaut -20 KPX v omacron -20 KPX v oslash -20 KPX v otilde -20 KPX v period -65 KPX w a -10 KPX w aacute -10 KPX w abreve -10 KPX w acircumflex -10 KPX w adieresis -10 KPX w agrave -10 KPX w amacron -10 KPX w aogonek -10 KPX w aring -10 KPX w atilde -10 KPX w comma -65 KPX w o -10 KPX w oacute -10 KPX w ocircumflex -10 KPX w odieresis -10 KPX w ograve -10 KPX w ohungarumlaut -10 KPX w omacron -10 KPX w oslash -10 KPX w otilde -10 KPX w period -65 KPX x e -15 KPX x eacute -15 KPX x ecaron -15 KPX x ecircumflex -15 KPX x edieresis -15 KPX x edotaccent -15 KPX x egrave -15 KPX x emacron -15 KPX x eogonek -15 KPX y comma -65 KPX y period -65 KPX yacute comma -65 KPX yacute period -65 KPX ydieresis comma -65 KPX ydieresis period -65 EndKernPairs EndKernData EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/afm/ZapfDingbats.afm000066400000000000000000000230301320103431700275750ustar00rootroot00000000000000StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 15:14:13 1997 Comment UniqueID 43082 Comment VMusage 45775 55535 FontName ZapfDingbats FullName ITC Zapf Dingbats FamilyName ZapfDingbats Weight Medium ItalicAngle 0 IsFixedPitch false CharacterSet Special FontBBox -1 -143 981 820 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved.ITC Zapf Dingbats is a registered trademark of International Typeface Corporation. EncodingScheme FontSpecific StdHW 28 StdVW 90 StartCharMetrics 202 C 32 ; WX 278 ; N space ; B 0 0 0 0 ; C 33 ; WX 974 ; N a1 ; B 35 72 939 621 ; C 34 ; WX 961 ; N a2 ; B 35 81 927 611 ; C 35 ; WX 974 ; N a202 ; B 35 72 939 621 ; C 36 ; WX 980 ; N a3 ; B 35 0 945 692 ; C 37 ; WX 719 ; N a4 ; B 34 139 685 566 ; C 38 ; WX 789 ; N a5 ; B 35 -14 755 705 ; C 39 ; WX 790 ; N a119 ; B 35 -14 755 705 ; C 40 ; WX 791 ; N a118 ; B 35 -13 761 705 ; C 41 ; WX 690 ; N a117 ; B 34 138 655 553 ; C 42 ; WX 960 ; N a11 ; B 35 123 925 568 ; C 43 ; WX 939 ; N a12 ; B 35 134 904 559 ; C 44 ; WX 549 ; N a13 ; B 29 -11 516 705 ; C 45 ; WX 855 ; N a14 ; B 34 59 820 632 ; C 46 ; WX 911 ; N a15 ; B 35 50 876 642 ; C 47 ; WX 933 ; N a16 ; B 35 139 899 550 ; C 48 ; WX 911 ; N a105 ; B 35 50 876 642 ; C 49 ; WX 945 ; N a17 ; B 35 139 909 553 ; C 50 ; WX 974 ; N a18 ; B 35 104 938 587 ; C 51 ; WX 755 ; N a19 ; B 34 -13 721 705 ; C 52 ; WX 846 ; N a20 ; B 36 -14 811 705 ; C 53 ; WX 762 ; N a21 ; B 35 0 727 692 ; C 54 ; WX 761 ; N a22 ; B 35 0 727 692 ; C 55 ; WX 571 ; N a23 ; B -1 -68 571 661 ; C 56 ; WX 677 ; N a24 ; B 36 -13 642 705 ; C 57 ; WX 763 ; N a25 ; B 35 0 728 692 ; C 58 ; WX 760 ; N a26 ; B 35 0 726 692 ; C 59 ; WX 759 ; N a27 ; B 35 0 725 692 ; C 60 ; WX 754 ; N a28 ; B 35 0 720 692 ; C 61 ; WX 494 ; N a6 ; B 35 0 460 692 ; C 62 ; WX 552 ; N a7 ; B 35 0 517 692 ; C 63 ; WX 537 ; N a8 ; B 35 0 503 692 ; C 64 ; WX 577 ; N a9 ; B 35 96 542 596 ; C 65 ; WX 692 ; N a10 ; B 35 -14 657 705 ; C 66 ; WX 786 ; N a29 ; B 35 -14 751 705 ; C 67 ; WX 788 ; N a30 ; B 35 -14 752 705 ; C 68 ; WX 788 ; N a31 ; B 35 -14 753 705 ; C 69 ; WX 790 ; N a32 ; B 35 -14 756 705 ; C 70 ; WX 793 ; N a33 ; B 35 -13 759 705 ; C 71 ; WX 794 ; N a34 ; B 35 -13 759 705 ; C 72 ; WX 816 ; N a35 ; B 35 -14 782 705 ; C 73 ; WX 823 ; N a36 ; B 35 -14 787 705 ; C 74 ; WX 789 ; N a37 ; B 35 -14 754 705 ; C 75 ; WX 841 ; N a38 ; B 35 -14 807 705 ; C 76 ; WX 823 ; N a39 ; B 35 -14 789 705 ; C 77 ; WX 833 ; N a40 ; B 35 -14 798 705 ; C 78 ; WX 816 ; N a41 ; B 35 -13 782 705 ; C 79 ; WX 831 ; N a42 ; B 35 -14 796 705 ; C 80 ; WX 923 ; N a43 ; B 35 -14 888 705 ; C 81 ; WX 744 ; N a44 ; B 35 0 710 692 ; C 82 ; WX 723 ; N a45 ; B 35 0 688 692 ; C 83 ; WX 749 ; N a46 ; B 35 0 714 692 ; C 84 ; WX 790 ; N a47 ; B 34 -14 756 705 ; C 85 ; WX 792 ; N a48 ; B 35 -14 758 705 ; C 86 ; WX 695 ; N a49 ; B 35 -14 661 706 ; C 87 ; WX 776 ; N a50 ; B 35 -6 741 699 ; C 88 ; WX 768 ; N a51 ; B 35 -7 734 699 ; C 89 ; WX 792 ; N a52 ; B 35 -14 757 705 ; C 90 ; WX 759 ; N a53 ; B 35 0 725 692 ; C 91 ; WX 707 ; N a54 ; B 35 -13 672 704 ; C 92 ; WX 708 ; N a55 ; B 35 -14 672 705 ; C 93 ; WX 682 ; N a56 ; B 35 -14 647 705 ; C 94 ; WX 701 ; N a57 ; B 35 -14 666 705 ; C 95 ; WX 826 ; N a58 ; B 35 -14 791 705 ; C 96 ; WX 815 ; N a59 ; B 35 -14 780 705 ; C 97 ; WX 789 ; N a60 ; B 35 -14 754 705 ; C 98 ; WX 789 ; N a61 ; B 35 -14 754 705 ; C 99 ; WX 707 ; N a62 ; B 34 -14 673 705 ; C 100 ; WX 687 ; N a63 ; B 36 0 651 692 ; C 101 ; WX 696 ; N a64 ; B 35 0 661 691 ; C 102 ; WX 689 ; N a65 ; B 35 0 655 692 ; C 103 ; WX 786 ; N a66 ; B 34 -14 751 705 ; C 104 ; WX 787 ; N a67 ; B 35 -14 752 705 ; C 105 ; WX 713 ; N a68 ; B 35 -14 678 705 ; C 106 ; WX 791 ; N a69 ; B 35 -14 756 705 ; C 107 ; WX 785 ; N a70 ; B 36 -14 751 705 ; C 108 ; WX 791 ; N a71 ; B 35 -14 757 705 ; C 109 ; WX 873 ; N a72 ; B 35 -14 838 705 ; C 110 ; WX 761 ; N a73 ; B 35 0 726 692 ; C 111 ; WX 762 ; N a74 ; B 35 0 727 692 ; C 112 ; WX 762 ; N a203 ; B 35 0 727 692 ; C 113 ; WX 759 ; N a75 ; B 35 0 725 692 ; C 114 ; WX 759 ; N a204 ; B 35 0 725 692 ; C 115 ; WX 892 ; N a76 ; B 35 0 858 705 ; C 116 ; WX 892 ; N a77 ; B 35 -14 858 692 ; C 117 ; WX 788 ; N a78 ; B 35 -14 754 705 ; C 118 ; WX 784 ; N a79 ; B 35 -14 749 705 ; C 119 ; WX 438 ; N a81 ; B 35 -14 403 705 ; C 120 ; WX 138 ; N a82 ; B 35 0 104 692 ; C 121 ; WX 277 ; N a83 ; B 35 0 242 692 ; C 122 ; WX 415 ; N a84 ; B 35 0 380 692 ; C 123 ; WX 392 ; N a97 ; B 35 263 357 705 ; C 124 ; WX 392 ; N a98 ; B 34 263 357 705 ; C 125 ; WX 668 ; N a99 ; B 35 263 633 705 ; C 126 ; WX 668 ; N a100 ; B 36 263 634 705 ; C 128 ; WX 390 ; N a89 ; B 35 -14 356 705 ; C 129 ; WX 390 ; N a90 ; B 35 -14 355 705 ; C 130 ; WX 317 ; N a93 ; B 35 0 283 692 ; C 131 ; WX 317 ; N a94 ; B 35 0 283 692 ; C 132 ; WX 276 ; N a91 ; B 35 0 242 692 ; C 133 ; WX 276 ; N a92 ; B 35 0 242 692 ; C 134 ; WX 509 ; N a205 ; B 35 0 475 692 ; C 135 ; WX 509 ; N a85 ; B 35 0 475 692 ; C 136 ; WX 410 ; N a206 ; B 35 0 375 692 ; C 137 ; WX 410 ; N a86 ; B 35 0 375 692 ; C 138 ; WX 234 ; N a87 ; B 35 -14 199 705 ; C 139 ; WX 234 ; N a88 ; B 35 -14 199 705 ; C 140 ; WX 334 ; N a95 ; B 35 0 299 692 ; C 141 ; WX 334 ; N a96 ; B 35 0 299 692 ; C 161 ; WX 732 ; N a101 ; B 35 -143 697 806 ; C 162 ; WX 544 ; N a102 ; B 56 -14 488 706 ; C 163 ; WX 544 ; N a103 ; B 34 -14 508 705 ; C 164 ; WX 910 ; N a104 ; B 35 40 875 651 ; C 165 ; WX 667 ; N a106 ; B 35 -14 633 705 ; C 166 ; WX 760 ; N a107 ; B 35 -14 726 705 ; C 167 ; WX 760 ; N a108 ; B 0 121 758 569 ; C 168 ; WX 776 ; N a112 ; B 35 0 741 705 ; C 169 ; WX 595 ; N a111 ; B 34 -14 560 705 ; C 170 ; WX 694 ; N a110 ; B 35 -14 659 705 ; C 171 ; WX 626 ; N a109 ; B 34 0 591 705 ; C 172 ; WX 788 ; N a120 ; B 35 -14 754 705 ; C 173 ; WX 788 ; N a121 ; B 35 -14 754 705 ; C 174 ; WX 788 ; N a122 ; B 35 -14 754 705 ; C 175 ; WX 788 ; N a123 ; B 35 -14 754 705 ; C 176 ; WX 788 ; N a124 ; B 35 -14 754 705 ; C 177 ; WX 788 ; N a125 ; B 35 -14 754 705 ; C 178 ; WX 788 ; N a126 ; B 35 -14 754 705 ; C 179 ; WX 788 ; N a127 ; B 35 -14 754 705 ; C 180 ; WX 788 ; N a128 ; B 35 -14 754 705 ; C 181 ; WX 788 ; N a129 ; B 35 -14 754 705 ; C 182 ; WX 788 ; N a130 ; B 35 -14 754 705 ; C 183 ; WX 788 ; N a131 ; B 35 -14 754 705 ; C 184 ; WX 788 ; N a132 ; B 35 -14 754 705 ; C 185 ; WX 788 ; N a133 ; B 35 -14 754 705 ; C 186 ; WX 788 ; N a134 ; B 35 -14 754 705 ; C 187 ; WX 788 ; N a135 ; B 35 -14 754 705 ; C 188 ; WX 788 ; N a136 ; B 35 -14 754 705 ; C 189 ; WX 788 ; N a137 ; B 35 -14 754 705 ; C 190 ; WX 788 ; N a138 ; B 35 -14 754 705 ; C 191 ; WX 788 ; N a139 ; B 35 -14 754 705 ; C 192 ; WX 788 ; N a140 ; B 35 -14 754 705 ; C 193 ; WX 788 ; N a141 ; B 35 -14 754 705 ; C 194 ; WX 788 ; N a142 ; B 35 -14 754 705 ; C 195 ; WX 788 ; N a143 ; B 35 -14 754 705 ; C 196 ; WX 788 ; N a144 ; B 35 -14 754 705 ; C 197 ; WX 788 ; N a145 ; B 35 -14 754 705 ; C 198 ; WX 788 ; N a146 ; B 35 -14 754 705 ; C 199 ; WX 788 ; N a147 ; B 35 -14 754 705 ; C 200 ; WX 788 ; N a148 ; B 35 -14 754 705 ; C 201 ; WX 788 ; N a149 ; B 35 -14 754 705 ; C 202 ; WX 788 ; N a150 ; B 35 -14 754 705 ; C 203 ; WX 788 ; N a151 ; B 35 -14 754 705 ; C 204 ; WX 788 ; N a152 ; B 35 -14 754 705 ; C 205 ; WX 788 ; N a153 ; B 35 -14 754 705 ; C 206 ; WX 788 ; N a154 ; B 35 -14 754 705 ; C 207 ; WX 788 ; N a155 ; B 35 -14 754 705 ; C 208 ; WX 788 ; N a156 ; B 35 -14 754 705 ; C 209 ; WX 788 ; N a157 ; B 35 -14 754 705 ; C 210 ; WX 788 ; N a158 ; B 35 -14 754 705 ; C 211 ; WX 788 ; N a159 ; B 35 -14 754 705 ; C 212 ; WX 894 ; N a160 ; B 35 58 860 634 ; C 213 ; WX 838 ; N a161 ; B 35 152 803 540 ; C 214 ; WX 1016 ; N a163 ; B 34 152 981 540 ; C 215 ; WX 458 ; N a164 ; B 35 -127 422 820 ; C 216 ; WX 748 ; N a196 ; B 35 94 698 597 ; C 217 ; WX 924 ; N a165 ; B 35 140 890 552 ; C 218 ; WX 748 ; N a192 ; B 35 94 698 597 ; C 219 ; WX 918 ; N a166 ; B 35 166 884 526 ; C 220 ; WX 927 ; N a167 ; B 35 32 892 660 ; C 221 ; WX 928 ; N a168 ; B 35 129 891 562 ; C 222 ; WX 928 ; N a169 ; B 35 128 893 563 ; C 223 ; WX 834 ; N a170 ; B 35 155 799 537 ; C 224 ; WX 873 ; N a171 ; B 35 93 838 599 ; C 225 ; WX 828 ; N a172 ; B 35 104 791 588 ; C 226 ; WX 924 ; N a173 ; B 35 98 889 594 ; C 227 ; WX 924 ; N a162 ; B 35 98 889 594 ; C 228 ; WX 917 ; N a174 ; B 35 0 882 692 ; C 229 ; WX 930 ; N a175 ; B 35 84 896 608 ; C 230 ; WX 931 ; N a176 ; B 35 84 896 608 ; C 231 ; WX 463 ; N a177 ; B 35 -99 429 791 ; C 232 ; WX 883 ; N a178 ; B 35 71 848 623 ; C 233 ; WX 836 ; N a179 ; B 35 44 802 648 ; C 234 ; WX 836 ; N a193 ; B 35 44 802 648 ; C 235 ; WX 867 ; N a180 ; B 35 101 832 591 ; C 236 ; WX 867 ; N a199 ; B 35 101 832 591 ; C 237 ; WX 696 ; N a181 ; B 35 44 661 648 ; C 238 ; WX 696 ; N a200 ; B 35 44 661 648 ; C 239 ; WX 874 ; N a182 ; B 35 77 840 619 ; C 241 ; WX 874 ; N a201 ; B 35 73 840 615 ; C 242 ; WX 760 ; N a183 ; B 35 0 725 692 ; C 243 ; WX 946 ; N a184 ; B 35 160 911 533 ; C 244 ; WX 771 ; N a197 ; B 34 37 736 655 ; C 245 ; WX 865 ; N a185 ; B 35 207 830 481 ; C 246 ; WX 771 ; N a194 ; B 34 37 736 655 ; C 247 ; WX 888 ; N a198 ; B 34 -19 853 712 ; C 248 ; WX 967 ; N a186 ; B 35 124 932 568 ; C 249 ; WX 888 ; N a195 ; B 34 -19 853 712 ; C 250 ; WX 831 ; N a187 ; B 35 113 796 579 ; C 251 ; WX 873 ; N a188 ; B 36 118 838 578 ; C 252 ; WX 927 ; N a189 ; B 35 150 891 542 ; C 253 ; WX 970 ; N a190 ; B 35 76 931 616 ; C 254 ; WX 918 ; N a191 ; B 34 99 884 593 ; EndCharMetrics EndFontMetrics sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/glyphlist/000077500000000000000000000000001320103431700260125ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/glyphlist/additional.txt000066400000000000000000000064231320103431700306700ustar00rootroot00000000000000# # 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. # # Format: Semicolon-delimited fields: # (1) glyph name # (2) Unicode scalar value # # These mappings are missing in glyphlist.txt # angbracketleft;3008 angbracketright;3009 circlecopyrt;00A9 controlNULL;0000 # # TeX-related mappings using named values # angbracketleftbig;2329 angbracketleftBig;2329 angbracketleftbigg;2329 angbracketleftBigg;2329 angbracketrightBig;232A angbracketrightbig;232A angbracketrightBigg;232A angbracketrightbigg;232A arrowhookleft;21AA arrowhookright;21A9 arrowlefttophalf;21BC arrowleftbothalf;21BD arrownortheast;2197 arrownorthwest;2196 arrowrighttophalf;21C0 arrowrightbothalf;21C1 arrowsoutheast;2198 arrowsouthwest;2199 backslashbig;2216 backslashBig;2216 backslashBigg;2216 backslashbigg;2216 bardbl;2016 bracehtipdownleft;FE37 bracehtipdownright;FE37 bracehtipupleft;FE38 bracehtipupright;FE38 braceleftBig;007B braceleftbig;007B braceleftbigg;007B braceleftBigg;007B bracerightBig;007D bracerightbig;007D bracerightbigg;007D bracerightBigg;007D bracketleftbig;005B bracketleftBig;005B bracketleftbigg;005B bracketleftBigg;005B bracketrightBig;005D bracketrightbig;005D bracketrightbigg;005D bracketrightBigg;005D ceilingleftbig;2308 ceilingleftBig;2308 ceilingleftBigg;2308 ceilingleftbigg;2308 ceilingrightbig;2309 ceilingrightBig;2309 ceilingrightbigg;2309 ceilingrightBigg;2309 circledotdisplay;2299 circledottext;2299 circlemultiplydisplay;2297 circlemultiplytext;2297 circleplusdisplay;2295 circleplustext;2295 contintegraldisplay;222E contintegraltext;222E coproductdisplay;2210 coproducttext;2210 floorleftBig;230A floorleftbig;230A floorleftbigg;230A floorleftBigg;230A floorrightbig;230B floorrightBig;230B floorrightBigg;230B floorrightbigg;230B hatwide;0302 hatwider;0302 hatwidest;0302 intercal;1D40 integraldisplay;222B integraltext;222B intersectiondisplay;22C2 intersectiontext;22C2 logicalanddisplay;2227 logicalandtext;2227 logicalordisplay;2228 logicalortext;2228 parenleftBig;0028 parenleftbig;0028 parenleftBigg;0028 parenleftbigg;0028 parenrightBig;0029 parenrightbig;0029 parenrightBigg;0029 parenrightbigg;0029 prime;2032 productdisplay;220F producttext;220F radicalbig;221A radicalBig;221A radicalBigg;221A radicalbigg;221A radicalbt;221A radicaltp;221A radicalvertex;221A slashbig;002F slashBig;002F slashBigg;002F slashbigg;002F summationdisplay;2211 summationtext;2211 tildewide;02DC tildewider;02DC tildewidest;02DC uniondisplay;22C3 unionmultidisplay;228E unionmultitext;228E unionsqdisplay;2294 unionsqtext;2294 uniontext;22C3 vextenddouble;2225 vextendsingle;2223 #ENDsambox-1.1.19/src/main/resources/org/sejda/sambox/resources/glyphlist/glyphlist.txt000066400000000000000000002304371320103431700306030ustar00rootroot00000000000000# ----------------------------------------------------------- # Copyright 1997, 1998, 2002, 2007, 2010 Adobe Systems Incorporated. # All rights reserved. # # Redistribution and use in source and binary forms, with or # without modification, are permitted provided that the # following conditions are met: # # Redistributions of source code must retain the above # copyright notice, this list of conditions and the following # disclaimer. # # Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials # provided with the distribution. # # Neither the name of Adobe Systems Incorporated nor the names # of its contributors may be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------- # Name: Adobe Glyph List # Table version: 2.0 # Date: September 20, 2002 # URL: http://sourceforge.net/adobe/aglfn/ # # Format: two semicolon-delimited fields: # (1) glyph name--upper/lowercase letters and digits # (2) Unicode scalar value--four uppercase hexadecimal digits # A;0041 AE;00C6 AEacute;01FC AEmacron;01E2 AEsmall;F7E6 Aacute;00C1 Aacutesmall;F7E1 Abreve;0102 Abreveacute;1EAE Abrevecyrillic;04D0 Abrevedotbelow;1EB6 Abrevegrave;1EB0 Abrevehookabove;1EB2 Abrevetilde;1EB4 Acaron;01CD Acircle;24B6 Acircumflex;00C2 Acircumflexacute;1EA4 Acircumflexdotbelow;1EAC Acircumflexgrave;1EA6 Acircumflexhookabove;1EA8 Acircumflexsmall;F7E2 Acircumflextilde;1EAA Acute;F6C9 Acutesmall;F7B4 Acyrillic;0410 Adblgrave;0200 Adieresis;00C4 Adieresiscyrillic;04D2 Adieresismacron;01DE Adieresissmall;F7E4 Adotbelow;1EA0 Adotmacron;01E0 Agrave;00C0 Agravesmall;F7E0 Ahookabove;1EA2 Aiecyrillic;04D4 Ainvertedbreve;0202 Alpha;0391 Alphatonos;0386 Amacron;0100 Amonospace;FF21 Aogonek;0104 Aring;00C5 Aringacute;01FA Aringbelow;1E00 Aringsmall;F7E5 Asmall;F761 Atilde;00C3 Atildesmall;F7E3 Aybarmenian;0531 B;0042 Bcircle;24B7 Bdotaccent;1E02 Bdotbelow;1E04 Becyrillic;0411 Benarmenian;0532 Beta;0392 Bhook;0181 Blinebelow;1E06 Bmonospace;FF22 Brevesmall;F6F4 Bsmall;F762 Btopbar;0182 C;0043 Caarmenian;053E Cacute;0106 Caron;F6CA Caronsmall;F6F5 Ccaron;010C Ccedilla;00C7 Ccedillaacute;1E08 Ccedillasmall;F7E7 Ccircle;24B8 Ccircumflex;0108 Cdot;010A Cdotaccent;010A Cedillasmall;F7B8 Chaarmenian;0549 Cheabkhasiancyrillic;04BC Checyrillic;0427 Chedescenderabkhasiancyrillic;04BE Chedescendercyrillic;04B6 Chedieresiscyrillic;04F4 Cheharmenian;0543 Chekhakassiancyrillic;04CB Cheverticalstrokecyrillic;04B8 Chi;03A7 Chook;0187 Circumflexsmall;F6F6 Cmonospace;FF23 Coarmenian;0551 Csmall;F763 D;0044 DZ;01F1 DZcaron;01C4 Daarmenian;0534 Dafrican;0189 Dcaron;010E Dcedilla;1E10 Dcircle;24B9 Dcircumflexbelow;1E12 Dcroat;0110 Ddotaccent;1E0A Ddotbelow;1E0C Decyrillic;0414 Deicoptic;03EE Delta;2206 Deltagreek;0394 Dhook;018A Dieresis;F6CB DieresisAcute;F6CC DieresisGrave;F6CD Dieresissmall;F7A8 Digammagreek;03DC Djecyrillic;0402 Dlinebelow;1E0E Dmonospace;FF24 Dotaccentsmall;F6F7 Dslash;0110 Dsmall;F764 Dtopbar;018B Dz;01F2 Dzcaron;01C5 Dzeabkhasiancyrillic;04E0 Dzecyrillic;0405 Dzhecyrillic;040F E;0045 Eacute;00C9 Eacutesmall;F7E9 Ebreve;0114 Ecaron;011A Ecedillabreve;1E1C Echarmenian;0535 Ecircle;24BA Ecircumflex;00CA Ecircumflexacute;1EBE Ecircumflexbelow;1E18 Ecircumflexdotbelow;1EC6 Ecircumflexgrave;1EC0 Ecircumflexhookabove;1EC2 Ecircumflexsmall;F7EA Ecircumflextilde;1EC4 Ecyrillic;0404 Edblgrave;0204 Edieresis;00CB Edieresissmall;F7EB Edot;0116 Edotaccent;0116 Edotbelow;1EB8 Efcyrillic;0424 Egrave;00C8 Egravesmall;F7E8 Eharmenian;0537 Ehookabove;1EBA Eightroman;2167 Einvertedbreve;0206 Eiotifiedcyrillic;0464 Elcyrillic;041B Elevenroman;216A Emacron;0112 Emacronacute;1E16 Emacrongrave;1E14 Emcyrillic;041C Emonospace;FF25 Encyrillic;041D Endescendercyrillic;04A2 Eng;014A Enghecyrillic;04A4 Enhookcyrillic;04C7 Eogonek;0118 Eopen;0190 Epsilon;0395 Epsilontonos;0388 Ercyrillic;0420 Ereversed;018E Ereversedcyrillic;042D Escyrillic;0421 Esdescendercyrillic;04AA Esh;01A9 Esmall;F765 Eta;0397 Etarmenian;0538 Etatonos;0389 Eth;00D0 Ethsmall;F7F0 Etilde;1EBC Etildebelow;1E1A Euro;20AC Ezh;01B7 Ezhcaron;01EE Ezhreversed;01B8 F;0046 Fcircle;24BB Fdotaccent;1E1E Feharmenian;0556 Feicoptic;03E4 Fhook;0191 Fitacyrillic;0472 Fiveroman;2164 Fmonospace;FF26 Fourroman;2163 Fsmall;F766 G;0047 GBsquare;3387 Gacute;01F4 Gamma;0393 Gammaafrican;0194 Gangiacoptic;03EA Gbreve;011E Gcaron;01E6 Gcedilla;0122 Gcircle;24BC Gcircumflex;011C Gcommaaccent;0122 Gdot;0120 Gdotaccent;0120 Gecyrillic;0413 Ghadarmenian;0542 Ghemiddlehookcyrillic;0494 Ghestrokecyrillic;0492 Gheupturncyrillic;0490 Ghook;0193 Gimarmenian;0533 Gjecyrillic;0403 Gmacron;1E20 Gmonospace;FF27 Grave;F6CE Gravesmall;F760 Gsmall;F767 Gsmallhook;029B Gstroke;01E4 H;0048 H18533;25CF H18543;25AA H18551;25AB H22073;25A1 HPsquare;33CB Haabkhasiancyrillic;04A8 Hadescendercyrillic;04B2 Hardsigncyrillic;042A Hbar;0126 Hbrevebelow;1E2A Hcedilla;1E28 Hcircle;24BD Hcircumflex;0124 Hdieresis;1E26 Hdotaccent;1E22 Hdotbelow;1E24 Hmonospace;FF28 Hoarmenian;0540 Horicoptic;03E8 Hsmall;F768 Hungarumlaut;F6CF Hungarumlautsmall;F6F8 Hzsquare;3390 I;0049 IAcyrillic;042F IJ;0132 IUcyrillic;042E Iacute;00CD Iacutesmall;F7ED Ibreve;012C Icaron;01CF Icircle;24BE Icircumflex;00CE Icircumflexsmall;F7EE Icyrillic;0406 Idblgrave;0208 Idieresis;00CF Idieresisacute;1E2E Idieresiscyrillic;04E4 Idieresissmall;F7EF Idot;0130 Idotaccent;0130 Idotbelow;1ECA Iebrevecyrillic;04D6 Iecyrillic;0415 Ifraktur;2111 Igrave;00CC Igravesmall;F7EC Ihookabove;1EC8 Iicyrillic;0418 Iinvertedbreve;020A Iishortcyrillic;0419 Imacron;012A Imacroncyrillic;04E2 Imonospace;FF29 Iniarmenian;053B Iocyrillic;0401 Iogonek;012E Iota;0399 Iotaafrican;0196 Iotadieresis;03AA Iotatonos;038A Ismall;F769 Istroke;0197 Itilde;0128 Itildebelow;1E2C Izhitsacyrillic;0474 Izhitsadblgravecyrillic;0476 J;004A Jaarmenian;0541 Jcircle;24BF Jcircumflex;0134 Jecyrillic;0408 Jheharmenian;054B Jmonospace;FF2A Jsmall;F76A K;004B KBsquare;3385 KKsquare;33CD Kabashkircyrillic;04A0 Kacute;1E30 Kacyrillic;041A Kadescendercyrillic;049A Kahookcyrillic;04C3 Kappa;039A Kastrokecyrillic;049E Kaverticalstrokecyrillic;049C Kcaron;01E8 Kcedilla;0136 Kcircle;24C0 Kcommaaccent;0136 Kdotbelow;1E32 Keharmenian;0554 Kenarmenian;053F Khacyrillic;0425 Kheicoptic;03E6 Khook;0198 Kjecyrillic;040C Klinebelow;1E34 Kmonospace;FF2B Koppacyrillic;0480 Koppagreek;03DE Ksicyrillic;046E Ksmall;F76B L;004C LJ;01C7 LL;F6BF Lacute;0139 Lambda;039B Lcaron;013D Lcedilla;013B Lcircle;24C1 Lcircumflexbelow;1E3C Lcommaaccent;013B Ldot;013F Ldotaccent;013F Ldotbelow;1E36 Ldotbelowmacron;1E38 Liwnarmenian;053C Lj;01C8 Ljecyrillic;0409 Llinebelow;1E3A Lmonospace;FF2C Lslash;0141 Lslashsmall;F6F9 Lsmall;F76C M;004D MBsquare;3386 Macron;F6D0 Macronsmall;F7AF Macute;1E3E Mcircle;24C2 Mdotaccent;1E40 Mdotbelow;1E42 Menarmenian;0544 Mmonospace;FF2D Msmall;F76D Mturned;019C Mu;039C N;004E NJ;01CA Nacute;0143 Ncaron;0147 Ncedilla;0145 Ncircle;24C3 Ncircumflexbelow;1E4A Ncommaaccent;0145 Ndotaccent;1E44 Ndotbelow;1E46 Nhookleft;019D Nineroman;2168 Nj;01CB Njecyrillic;040A Nlinebelow;1E48 Nmonospace;FF2E Nowarmenian;0546 Nsmall;F76E Ntilde;00D1 Ntildesmall;F7F1 Nu;039D O;004F OE;0152 OEsmall;F6FA Oacute;00D3 Oacutesmall;F7F3 Obarredcyrillic;04E8 Obarreddieresiscyrillic;04EA Obreve;014E Ocaron;01D1 Ocenteredtilde;019F Ocircle;24C4 Ocircumflex;00D4 Ocircumflexacute;1ED0 Ocircumflexdotbelow;1ED8 Ocircumflexgrave;1ED2 Ocircumflexhookabove;1ED4 Ocircumflexsmall;F7F4 Ocircumflextilde;1ED6 Ocyrillic;041E Odblacute;0150 Odblgrave;020C Odieresis;00D6 Odieresiscyrillic;04E6 Odieresissmall;F7F6 Odotbelow;1ECC Ogoneksmall;F6FB Ograve;00D2 Ogravesmall;F7F2 Oharmenian;0555 Ohm;2126 Ohookabove;1ECE Ohorn;01A0 Ohornacute;1EDA Ohorndotbelow;1EE2 Ohorngrave;1EDC Ohornhookabove;1EDE Ohorntilde;1EE0 Ohungarumlaut;0150 Oi;01A2 Oinvertedbreve;020E Omacron;014C Omacronacute;1E52 Omacrongrave;1E50 Omega;2126 Omegacyrillic;0460 Omegagreek;03A9 Omegaroundcyrillic;047A Omegatitlocyrillic;047C Omegatonos;038F Omicron;039F Omicrontonos;038C Omonospace;FF2F Oneroman;2160 Oogonek;01EA Oogonekmacron;01EC Oopen;0186 Oslash;00D8 Oslashacute;01FE Oslashsmall;F7F8 Osmall;F76F Ostrokeacute;01FE Otcyrillic;047E Otilde;00D5 Otildeacute;1E4C Otildedieresis;1E4E Otildesmall;F7F5 P;0050 Pacute;1E54 Pcircle;24C5 Pdotaccent;1E56 Pecyrillic;041F Peharmenian;054A Pemiddlehookcyrillic;04A6 Phi;03A6 Phook;01A4 Pi;03A0 Piwrarmenian;0553 Pmonospace;FF30 Psi;03A8 Psicyrillic;0470 Psmall;F770 Q;0051 Qcircle;24C6 Qmonospace;FF31 Qsmall;F771 R;0052 Raarmenian;054C Racute;0154 Rcaron;0158 Rcedilla;0156 Rcircle;24C7 Rcommaaccent;0156 Rdblgrave;0210 Rdotaccent;1E58 Rdotbelow;1E5A Rdotbelowmacron;1E5C Reharmenian;0550 Rfraktur;211C Rho;03A1 Ringsmall;F6FC Rinvertedbreve;0212 Rlinebelow;1E5E Rmonospace;FF32 Rsmall;F772 Rsmallinverted;0281 Rsmallinvertedsuperior;02B6 S;0053 SF010000;250C SF020000;2514 SF030000;2510 SF040000;2518 SF050000;253C SF060000;252C SF070000;2534 SF080000;251C SF090000;2524 SF100000;2500 SF110000;2502 SF190000;2561 SF200000;2562 SF210000;2556 SF220000;2555 SF230000;2563 SF240000;2551 SF250000;2557 SF260000;255D SF270000;255C SF280000;255B SF360000;255E SF370000;255F SF380000;255A SF390000;2554 SF400000;2569 SF410000;2566 SF420000;2560 SF430000;2550 SF440000;256C SF450000;2567 SF460000;2568 SF470000;2564 SF480000;2565 SF490000;2559 SF500000;2558 SF510000;2552 SF520000;2553 SF530000;256B SF540000;256A Sacute;015A Sacutedotaccent;1E64 Sampigreek;03E0 Scaron;0160 Scarondotaccent;1E66 Scaronsmall;F6FD Scedilla;015E Schwa;018F Schwacyrillic;04D8 Schwadieresiscyrillic;04DA Scircle;24C8 Scircumflex;015C Scommaaccent;0218 Sdotaccent;1E60 Sdotbelow;1E62 Sdotbelowdotaccent;1E68 Seharmenian;054D Sevenroman;2166 Shaarmenian;0547 Shacyrillic;0428 Shchacyrillic;0429 Sheicoptic;03E2 Shhacyrillic;04BA Shimacoptic;03EC Sigma;03A3 Sixroman;2165 Smonospace;FF33 Softsigncyrillic;042C Ssmall;F773 Stigmagreek;03DA T;0054 Tau;03A4 Tbar;0166 Tcaron;0164 Tcedilla;0162 Tcircle;24C9 Tcircumflexbelow;1E70 Tcommaaccent;0162 Tdotaccent;1E6A Tdotbelow;1E6C Tecyrillic;0422 Tedescendercyrillic;04AC Tenroman;2169 Tetsecyrillic;04B4 Theta;0398 Thook;01AC Thorn;00DE Thornsmall;F7FE Threeroman;2162 Tildesmall;F6FE Tiwnarmenian;054F Tlinebelow;1E6E Tmonospace;FF34 Toarmenian;0539 Tonefive;01BC Tonesix;0184 Tonetwo;01A7 Tretroflexhook;01AE Tsecyrillic;0426 Tshecyrillic;040B Tsmall;F774 Twelveroman;216B Tworoman;2161 U;0055 Uacute;00DA Uacutesmall;F7FA Ubreve;016C Ucaron;01D3 Ucircle;24CA Ucircumflex;00DB Ucircumflexbelow;1E76 Ucircumflexsmall;F7FB Ucyrillic;0423 Udblacute;0170 Udblgrave;0214 Udieresis;00DC Udieresisacute;01D7 Udieresisbelow;1E72 Udieresiscaron;01D9 Udieresiscyrillic;04F0 Udieresisgrave;01DB Udieresismacron;01D5 Udieresissmall;F7FC Udotbelow;1EE4 Ugrave;00D9 Ugravesmall;F7F9 Uhookabove;1EE6 Uhorn;01AF Uhornacute;1EE8 Uhorndotbelow;1EF0 Uhorngrave;1EEA Uhornhookabove;1EEC Uhorntilde;1EEE Uhungarumlaut;0170 Uhungarumlautcyrillic;04F2 Uinvertedbreve;0216 Ukcyrillic;0478 Umacron;016A Umacroncyrillic;04EE Umacrondieresis;1E7A Umonospace;FF35 Uogonek;0172 Upsilon;03A5 Upsilon1;03D2 Upsilonacutehooksymbolgreek;03D3 Upsilonafrican;01B1 Upsilondieresis;03AB Upsilondieresishooksymbolgreek;03D4 Upsilonhooksymbol;03D2 Upsilontonos;038E Uring;016E Ushortcyrillic;040E Usmall;F775 Ustraightcyrillic;04AE Ustraightstrokecyrillic;04B0 Utilde;0168 Utildeacute;1E78 Utildebelow;1E74 V;0056 Vcircle;24CB Vdotbelow;1E7E Vecyrillic;0412 Vewarmenian;054E Vhook;01B2 Vmonospace;FF36 Voarmenian;0548 Vsmall;F776 Vtilde;1E7C W;0057 Wacute;1E82 Wcircle;24CC Wcircumflex;0174 Wdieresis;1E84 Wdotaccent;1E86 Wdotbelow;1E88 Wgrave;1E80 Wmonospace;FF37 Wsmall;F777 X;0058 Xcircle;24CD Xdieresis;1E8C Xdotaccent;1E8A Xeharmenian;053D Xi;039E Xmonospace;FF38 Xsmall;F778 Y;0059 Yacute;00DD Yacutesmall;F7FD Yatcyrillic;0462 Ycircle;24CE Ycircumflex;0176 Ydieresis;0178 Ydieresissmall;F7FF Ydotaccent;1E8E Ydotbelow;1EF4 Yericyrillic;042B Yerudieresiscyrillic;04F8 Ygrave;1EF2 Yhook;01B3 Yhookabove;1EF6 Yiarmenian;0545 Yicyrillic;0407 Yiwnarmenian;0552 Ymonospace;FF39 Ysmall;F779 Ytilde;1EF8 Yusbigcyrillic;046A Yusbigiotifiedcyrillic;046C Yuslittlecyrillic;0466 Yuslittleiotifiedcyrillic;0468 Z;005A Zaarmenian;0536 Zacute;0179 Zcaron;017D Zcaronsmall;F6FF Zcircle;24CF Zcircumflex;1E90 Zdot;017B Zdotaccent;017B Zdotbelow;1E92 Zecyrillic;0417 Zedescendercyrillic;0498 Zedieresiscyrillic;04DE Zeta;0396 Zhearmenian;053A Zhebrevecyrillic;04C1 Zhecyrillic;0416 Zhedescendercyrillic;0496 Zhedieresiscyrillic;04DC Zlinebelow;1E94 Zmonospace;FF3A Zsmall;F77A Zstroke;01B5 a;0061 aabengali;0986 aacute;00E1 aadeva;0906 aagujarati;0A86 aagurmukhi;0A06 aamatragurmukhi;0A3E aarusquare;3303 aavowelsignbengali;09BE aavowelsigndeva;093E aavowelsigngujarati;0ABE abbreviationmarkarmenian;055F abbreviationsigndeva;0970 abengali;0985 abopomofo;311A abreve;0103 abreveacute;1EAF abrevecyrillic;04D1 abrevedotbelow;1EB7 abrevegrave;1EB1 abrevehookabove;1EB3 abrevetilde;1EB5 acaron;01CE acircle;24D0 acircumflex;00E2 acircumflexacute;1EA5 acircumflexdotbelow;1EAD acircumflexgrave;1EA7 acircumflexhookabove;1EA9 acircumflextilde;1EAB acute;00B4 acutebelowcmb;0317 acutecmb;0301 acutecomb;0301 acutedeva;0954 acutelowmod;02CF acutetonecmb;0341 acyrillic;0430 adblgrave;0201 addakgurmukhi;0A71 adeva;0905 adieresis;00E4 adieresiscyrillic;04D3 adieresismacron;01DF adotbelow;1EA1 adotmacron;01E1 ae;00E6 aeacute;01FD aekorean;3150 aemacron;01E3 afii00208;2015 afii08941;20A4 afii10017;0410 afii10018;0411 afii10019;0412 afii10020;0413 afii10021;0414 afii10022;0415 afii10023;0401 afii10024;0416 afii10025;0417 afii10026;0418 afii10027;0419 afii10028;041A afii10029;041B afii10030;041C afii10031;041D afii10032;041E afii10033;041F afii10034;0420 afii10035;0421 afii10036;0422 afii10037;0423 afii10038;0424 afii10039;0425 afii10040;0426 afii10041;0427 afii10042;0428 afii10043;0429 afii10044;042A afii10045;042B afii10046;042C afii10047;042D afii10048;042E afii10049;042F afii10050;0490 afii10051;0402 afii10052;0403 afii10053;0404 afii10054;0405 afii10055;0406 afii10056;0407 afii10057;0408 afii10058;0409 afii10059;040A afii10060;040B afii10061;040C afii10062;040E afii10063;F6C4 afii10064;F6C5 afii10065;0430 afii10066;0431 afii10067;0432 afii10068;0433 afii10069;0434 afii10070;0435 afii10071;0451 afii10072;0436 afii10073;0437 afii10074;0438 afii10075;0439 afii10076;043A afii10077;043B afii10078;043C afii10079;043D afii10080;043E afii10081;043F afii10082;0440 afii10083;0441 afii10084;0442 afii10085;0443 afii10086;0444 afii10087;0445 afii10088;0446 afii10089;0447 afii10090;0448 afii10091;0449 afii10092;044A afii10093;044B afii10094;044C afii10095;044D afii10096;044E afii10097;044F afii10098;0491 afii10099;0452 afii10100;0453 afii10101;0454 afii10102;0455 afii10103;0456 afii10104;0457 afii10105;0458 afii10106;0459 afii10107;045A afii10108;045B afii10109;045C afii10110;045E afii10145;040F afii10146;0462 afii10147;0472 afii10148;0474 afii10192;F6C6 afii10193;045F afii10194;0463 afii10195;0473 afii10196;0475 afii10831;F6C7 afii10832;F6C8 afii10846;04D9 afii299;200E afii300;200F afii301;200D afii57381;066A afii57388;060C afii57392;0660 afii57393;0661 afii57394;0662 afii57395;0663 afii57396;0664 afii57397;0665 afii57398;0666 afii57399;0667 afii57400;0668 afii57401;0669 afii57403;061B afii57407;061F afii57409;0621 afii57410;0622 afii57411;0623 afii57412;0624 afii57413;0625 afii57414;0626 afii57415;0627 afii57416;0628 afii57417;0629 afii57418;062A afii57419;062B afii57420;062C afii57421;062D afii57422;062E afii57423;062F afii57424;0630 afii57425;0631 afii57426;0632 afii57427;0633 afii57428;0634 afii57429;0635 afii57430;0636 afii57431;0637 afii57432;0638 afii57433;0639 afii57434;063A afii57440;0640 afii57441;0641 afii57442;0642 afii57443;0643 afii57444;0644 afii57445;0645 afii57446;0646 afii57448;0648 afii57449;0649 afii57450;064A afii57451;064B afii57452;064C afii57453;064D afii57454;064E afii57455;064F afii57456;0650 afii57457;0651 afii57458;0652 afii57470;0647 afii57505;06A4 afii57506;067E afii57507;0686 afii57508;0698 afii57509;06AF afii57511;0679 afii57512;0688 afii57513;0691 afii57514;06BA afii57519;06D2 afii57534;06D5 afii57636;20AA afii57645;05BE afii57658;05C3 afii57664;05D0 afii57665;05D1 afii57666;05D2 afii57667;05D3 afii57668;05D4 afii57669;05D5 afii57670;05D6 afii57671;05D7 afii57672;05D8 afii57673;05D9 afii57674;05DA afii57675;05DB afii57676;05DC afii57677;05DD afii57678;05DE afii57679;05DF afii57680;05E0 afii57681;05E1 afii57682;05E2 afii57683;05E3 afii57684;05E4 afii57685;05E5 afii57686;05E6 afii57687;05E7 afii57688;05E8 afii57689;05E9 afii57690;05EA afii57694;FB2A afii57695;FB2B afii57700;FB4B afii57705;FB1F afii57716;05F0 afii57717;05F1 afii57718;05F2 afii57723;FB35 afii57793;05B4 afii57794;05B5 afii57795;05B6 afii57796;05BB afii57797;05B8 afii57798;05B7 afii57799;05B0 afii57800;05B2 afii57801;05B1 afii57802;05B3 afii57803;05C2 afii57804;05C1 afii57806;05B9 afii57807;05BC afii57839;05BD afii57841;05BF afii57842;05C0 afii57929;02BC afii61248;2105 afii61289;2113 afii61352;2116 afii61573;202C afii61574;202D afii61575;202E afii61664;200C afii63167;066D afii64937;02BD agrave;00E0 agujarati;0A85 agurmukhi;0A05 ahiragana;3042 ahookabove;1EA3 aibengali;0990 aibopomofo;311E aideva;0910 aiecyrillic;04D5 aigujarati;0A90 aigurmukhi;0A10 aimatragurmukhi;0A48 ainarabic;0639 ainfinalarabic;FECA aininitialarabic;FECB ainmedialarabic;FECC ainvertedbreve;0203 aivowelsignbengali;09C8 aivowelsigndeva;0948 aivowelsigngujarati;0AC8 akatakana;30A2 akatakanahalfwidth;FF71 akorean;314F alef;05D0 alefarabic;0627 alefdageshhebrew;FB30 aleffinalarabic;FE8E alefhamzaabovearabic;0623 alefhamzaabovefinalarabic;FE84 alefhamzabelowarabic;0625 alefhamzabelowfinalarabic;FE88 alefhebrew;05D0 aleflamedhebrew;FB4F alefmaddaabovearabic;0622 alefmaddaabovefinalarabic;FE82 alefmaksuraarabic;0649 alefmaksurafinalarabic;FEF0 alefmaksurainitialarabic;FEF3 alefmaksuramedialarabic;FEF4 alefpatahhebrew;FB2E alefqamatshebrew;FB2F aleph;2135 allequal;224C alpha;03B1 alphatonos;03AC amacron;0101 amonospace;FF41 ampersand;0026 ampersandmonospace;FF06 ampersandsmall;F726 amsquare;33C2 anbopomofo;3122 angbopomofo;3124 angkhankhuthai;0E5A angle;2220 anglebracketleft;3008 anglebracketleftvertical;FE3F anglebracketright;3009 anglebracketrightvertical;FE40 angleleft;2329 angleright;232A angstrom;212B anoteleia;0387 anudattadeva;0952 anusvarabengali;0982 anusvaradeva;0902 anusvaragujarati;0A82 aogonek;0105 apaatosquare;3300 aparen;249C apostrophearmenian;055A apostrophemod;02BC apple;F8FF approaches;2250 approxequal;2248 approxequalorimage;2252 approximatelyequal;2245 araeaekorean;318E araeakorean;318D arc;2312 arighthalfring;1E9A aring;00E5 aringacute;01FB aringbelow;1E01 arrowboth;2194 arrowdashdown;21E3 arrowdashleft;21E0 arrowdashright;21E2 arrowdashup;21E1 arrowdblboth;21D4 arrowdbldown;21D3 arrowdblleft;21D0 arrowdblright;21D2 arrowdblup;21D1 arrowdown;2193 arrowdownleft;2199 arrowdownright;2198 arrowdownwhite;21E9 arrowheaddownmod;02C5 arrowheadleftmod;02C2 arrowheadrightmod;02C3 arrowheadupmod;02C4 arrowhorizex;F8E7 arrowleft;2190 arrowleftdbl;21D0 arrowleftdblstroke;21CD arrowleftoverright;21C6 arrowleftwhite;21E6 arrowright;2192 arrowrightdblstroke;21CF arrowrightheavy;279E arrowrightoverleft;21C4 arrowrightwhite;21E8 arrowtableft;21E4 arrowtabright;21E5 arrowup;2191 arrowupdn;2195 arrowupdnbse;21A8 arrowupdownbase;21A8 arrowupleft;2196 arrowupleftofdown;21C5 arrowupright;2197 arrowupwhite;21E7 arrowvertex;F8E6 asciicircum;005E asciicircummonospace;FF3E asciitilde;007E asciitildemonospace;FF5E ascript;0251 ascriptturned;0252 asmallhiragana;3041 asmallkatakana;30A1 asmallkatakanahalfwidth;FF67 asterisk;002A asteriskaltonearabic;066D asteriskarabic;066D asteriskmath;2217 asteriskmonospace;FF0A asterisksmall;FE61 asterism;2042 asuperior;F6E9 asymptoticallyequal;2243 at;0040 atilde;00E3 atmonospace;FF20 atsmall;FE6B aturned;0250 aubengali;0994 aubopomofo;3120 audeva;0914 augujarati;0A94 augurmukhi;0A14 aulengthmarkbengali;09D7 aumatragurmukhi;0A4C auvowelsignbengali;09CC auvowelsigndeva;094C auvowelsigngujarati;0ACC avagrahadeva;093D aybarmenian;0561 ayin;05E2 ayinaltonehebrew;FB20 ayinhebrew;05E2 b;0062 babengali;09AC backslash;005C backslashmonospace;FF3C badeva;092C bagujarati;0AAC bagurmukhi;0A2C bahiragana;3070 bahtthai;0E3F bakatakana;30D0 bar;007C barmonospace;FF5C bbopomofo;3105 bcircle;24D1 bdotaccent;1E03 bdotbelow;1E05 beamedsixteenthnotes;266C because;2235 becyrillic;0431 beharabic;0628 behfinalarabic;FE90 behinitialarabic;FE91 behiragana;3079 behmedialarabic;FE92 behmeeminitialarabic;FC9F behmeemisolatedarabic;FC08 behnoonfinalarabic;FC6D bekatakana;30D9 benarmenian;0562 bet;05D1 beta;03B2 betasymbolgreek;03D0 betdagesh;FB31 betdageshhebrew;FB31 bethebrew;05D1 betrafehebrew;FB4C bhabengali;09AD bhadeva;092D bhagujarati;0AAD bhagurmukhi;0A2D bhook;0253 bihiragana;3073 bikatakana;30D3 bilabialclick;0298 bindigurmukhi;0A02 birusquare;3331 blackcircle;25CF blackdiamond;25C6 blackdownpointingtriangle;25BC blackleftpointingpointer;25C4 blackleftpointingtriangle;25C0 blacklenticularbracketleft;3010 blacklenticularbracketleftvertical;FE3B blacklenticularbracketright;3011 blacklenticularbracketrightvertical;FE3C blacklowerlefttriangle;25E3 blacklowerrighttriangle;25E2 blackrectangle;25AC blackrightpointingpointer;25BA blackrightpointingtriangle;25B6 blacksmallsquare;25AA blacksmilingface;263B blacksquare;25A0 blackstar;2605 blackupperlefttriangle;25E4 blackupperrighttriangle;25E5 blackuppointingsmalltriangle;25B4 blackuppointingtriangle;25B2 blank;2423 blinebelow;1E07 block;2588 bmonospace;FF42 bobaimaithai;0E1A bohiragana;307C bokatakana;30DC bparen;249D bqsquare;33C3 braceex;F8F4 braceleft;007B braceleftbt;F8F3 braceleftmid;F8F2 braceleftmonospace;FF5B braceleftsmall;FE5B bracelefttp;F8F1 braceleftvertical;FE37 braceright;007D bracerightbt;F8FE bracerightmid;F8FD bracerightmonospace;FF5D bracerightsmall;FE5C bracerighttp;F8FC bracerightvertical;FE38 bracketleft;005B bracketleftbt;F8F0 bracketleftex;F8EF bracketleftmonospace;FF3B bracketlefttp;F8EE bracketright;005D bracketrightbt;F8FB bracketrightex;F8FA bracketrightmonospace;FF3D bracketrighttp;F8F9 breve;02D8 brevebelowcmb;032E brevecmb;0306 breveinvertedbelowcmb;032F breveinvertedcmb;0311 breveinverteddoublecmb;0361 bridgebelowcmb;032A bridgeinvertedbelowcmb;033A brokenbar;00A6 bstroke;0180 bsuperior;F6EA btopbar;0183 buhiragana;3076 bukatakana;30D6 bullet;2022 bulletinverse;25D8 bulletoperator;2219 bullseye;25CE c;0063 caarmenian;056E cabengali;099A cacute;0107 cadeva;091A cagujarati;0A9A cagurmukhi;0A1A calsquare;3388 candrabindubengali;0981 candrabinducmb;0310 candrabindudeva;0901 candrabindugujarati;0A81 capslock;21EA careof;2105 caron;02C7 caronbelowcmb;032C caroncmb;030C carriagereturn;21B5 cbopomofo;3118 ccaron;010D ccedilla;00E7 ccedillaacute;1E09 ccircle;24D2 ccircumflex;0109 ccurl;0255 cdot;010B cdotaccent;010B cdsquare;33C5 cedilla;00B8 cedillacmb;0327 cent;00A2 centigrade;2103 centinferior;F6DF centmonospace;FFE0 centoldstyle;F7A2 centsuperior;F6E0 chaarmenian;0579 chabengali;099B chadeva;091B chagujarati;0A9B chagurmukhi;0A1B chbopomofo;3114 cheabkhasiancyrillic;04BD checkmark;2713 checyrillic;0447 chedescenderabkhasiancyrillic;04BF chedescendercyrillic;04B7 chedieresiscyrillic;04F5 cheharmenian;0573 chekhakassiancyrillic;04CC cheverticalstrokecyrillic;04B9 chi;03C7 chieuchacirclekorean;3277 chieuchaparenkorean;3217 chieuchcirclekorean;3269 chieuchkorean;314A chieuchparenkorean;3209 chochangthai;0E0A chochanthai;0E08 chochingthai;0E09 chochoethai;0E0C chook;0188 cieucacirclekorean;3276 cieucaparenkorean;3216 cieuccirclekorean;3268 cieuckorean;3148 cieucparenkorean;3208 cieucuparenkorean;321C circle;25CB circlemultiply;2297 circleot;2299 circleplus;2295 circlepostalmark;3036 circlewithlefthalfblack;25D0 circlewithrighthalfblack;25D1 circumflex;02C6 circumflexbelowcmb;032D circumflexcmb;0302 clear;2327 clickalveolar;01C2 clickdental;01C0 clicklateral;01C1 clickretroflex;01C3 club;2663 clubsuitblack;2663 clubsuitwhite;2667 cmcubedsquare;33A4 cmonospace;FF43 cmsquaredsquare;33A0 coarmenian;0581 colon;003A colonmonetary;20A1 colonmonospace;FF1A colonsign;20A1 colonsmall;FE55 colontriangularhalfmod;02D1 colontriangularmod;02D0 comma;002C commaabovecmb;0313 commaaboverightcmb;0315 commaaccent;F6C3 commaarabic;060C commaarmenian;055D commainferior;F6E1 commamonospace;FF0C commareversedabovecmb;0314 commareversedmod;02BD commasmall;FE50 commasuperior;F6E2 commaturnedabovecmb;0312 commaturnedmod;02BB compass;263C congruent;2245 contourintegral;222E control;2303 controlACK;0006 controlBEL;0007 controlBS;0008 controlCAN;0018 controlCR;000D controlDC1;0011 controlDC2;0012 controlDC3;0013 controlDC4;0014 controlDEL;007F controlDLE;0010 controlEM;0019 controlENQ;0005 controlEOT;0004 controlESC;001B controlETB;0017 controlETX;0003 controlFF;000C controlFS;001C controlGS;001D controlHT;0009 controlLF;000A controlNAK;0015 controlRS;001E controlSI;000F controlSO;000E controlSOT;0002 controlSTX;0001 controlSUB;001A controlSYN;0016 controlUS;001F controlVT;000B copyright;00A9 copyrightsans;F8E9 copyrightserif;F6D9 cornerbracketleft;300C cornerbracketlefthalfwidth;FF62 cornerbracketleftvertical;FE41 cornerbracketright;300D cornerbracketrighthalfwidth;FF63 cornerbracketrightvertical;FE42 corporationsquare;337F cosquare;33C7 coverkgsquare;33C6 cparen;249E cruzeiro;20A2 cstretched;0297 curlyand;22CF curlyor;22CE currency;00A4 cyrBreve;F6D1 cyrFlex;F6D2 cyrbreve;F6D4 cyrflex;F6D5 d;0064 daarmenian;0564 dabengali;09A6 dadarabic;0636 dadeva;0926 dadfinalarabic;FEBE dadinitialarabic;FEBF dadmedialarabic;FEC0 dagesh;05BC dageshhebrew;05BC dagger;2020 daggerdbl;2021 dagujarati;0AA6 dagurmukhi;0A26 dahiragana;3060 dakatakana;30C0 dalarabic;062F dalet;05D3 daletdagesh;FB33 daletdageshhebrew;FB33 dalethatafpatah;05D3 05B2 dalethatafpatahhebrew;05D3 05B2 dalethatafsegol;05D3 05B1 dalethatafsegolhebrew;05D3 05B1 dalethebrew;05D3 dalethiriq;05D3 05B4 dalethiriqhebrew;05D3 05B4 daletholam;05D3 05B9 daletholamhebrew;05D3 05B9 daletpatah;05D3 05B7 daletpatahhebrew;05D3 05B7 daletqamats;05D3 05B8 daletqamatshebrew;05D3 05B8 daletqubuts;05D3 05BB daletqubutshebrew;05D3 05BB daletsegol;05D3 05B6 daletsegolhebrew;05D3 05B6 daletsheva;05D3 05B0 daletshevahebrew;05D3 05B0 dalettsere;05D3 05B5 dalettserehebrew;05D3 05B5 dalfinalarabic;FEAA dammaarabic;064F dammalowarabic;064F dammatanaltonearabic;064C dammatanarabic;064C danda;0964 dargahebrew;05A7 dargalefthebrew;05A7 dasiapneumatacyrilliccmb;0485 dblGrave;F6D3 dblanglebracketleft;300A dblanglebracketleftvertical;FE3D dblanglebracketright;300B dblanglebracketrightvertical;FE3E dblarchinvertedbelowcmb;032B dblarrowleft;21D4 dblarrowright;21D2 dbldanda;0965 dblgrave;F6D6 dblgravecmb;030F dblintegral;222C dbllowline;2017 dbllowlinecmb;0333 dbloverlinecmb;033F dblprimemod;02BA dblverticalbar;2016 dblverticallineabovecmb;030E dbopomofo;3109 dbsquare;33C8 dcaron;010F dcedilla;1E11 dcircle;24D3 dcircumflexbelow;1E13 dcroat;0111 ddabengali;09A1 ddadeva;0921 ddagujarati;0AA1 ddagurmukhi;0A21 ddalarabic;0688 ddalfinalarabic;FB89 dddhadeva;095C ddhabengali;09A2 ddhadeva;0922 ddhagujarati;0AA2 ddhagurmukhi;0A22 ddotaccent;1E0B ddotbelow;1E0D decimalseparatorarabic;066B decimalseparatorpersian;066B decyrillic;0434 degree;00B0 dehihebrew;05AD dehiragana;3067 deicoptic;03EF dekatakana;30C7 deleteleft;232B deleteright;2326 delta;03B4 deltaturned;018D denominatorminusonenumeratorbengali;09F8 dezh;02A4 dhabengali;09A7 dhadeva;0927 dhagujarati;0AA7 dhagurmukhi;0A27 dhook;0257 dialytikatonos;0385 dialytikatonoscmb;0344 diamond;2666 diamondsuitwhite;2662 dieresis;00A8 dieresisacute;F6D7 dieresisbelowcmb;0324 dieresiscmb;0308 dieresisgrave;F6D8 dieresistonos;0385 dihiragana;3062 dikatakana;30C2 dittomark;3003 divide;00F7 divides;2223 divisionslash;2215 djecyrillic;0452 dkshade;2593 dlinebelow;1E0F dlsquare;3397 dmacron;0111 dmonospace;FF44 dnblock;2584 dochadathai;0E0E dodekthai;0E14 dohiragana;3069 dokatakana;30C9 dollar;0024 dollarinferior;F6E3 dollarmonospace;FF04 dollaroldstyle;F724 dollarsmall;FE69 dollarsuperior;F6E4 dong;20AB dorusquare;3326 dotaccent;02D9 dotaccentcmb;0307 dotbelowcmb;0323 dotbelowcomb;0323 dotkatakana;30FB dotlessi;0131 dotlessj;F6BE dotlessjstrokehook;0284 dotmath;22C5 dottedcircle;25CC doubleyodpatah;FB1F doubleyodpatahhebrew;FB1F downtackbelowcmb;031E downtackmod;02D5 dparen;249F dsuperior;F6EB dtail;0256 dtopbar;018C duhiragana;3065 dukatakana;30C5 dz;01F3 dzaltone;02A3 dzcaron;01C6 dzcurl;02A5 dzeabkhasiancyrillic;04E1 dzecyrillic;0455 dzhecyrillic;045F e;0065 eacute;00E9 earth;2641 ebengali;098F ebopomofo;311C ebreve;0115 ecandradeva;090D ecandragujarati;0A8D ecandravowelsigndeva;0945 ecandravowelsigngujarati;0AC5 ecaron;011B ecedillabreve;1E1D echarmenian;0565 echyiwnarmenian;0587 ecircle;24D4 ecircumflex;00EA ecircumflexacute;1EBF ecircumflexbelow;1E19 ecircumflexdotbelow;1EC7 ecircumflexgrave;1EC1 ecircumflexhookabove;1EC3 ecircumflextilde;1EC5 ecyrillic;0454 edblgrave;0205 edeva;090F edieresis;00EB edot;0117 edotaccent;0117 edotbelow;1EB9 eegurmukhi;0A0F eematragurmukhi;0A47 efcyrillic;0444 egrave;00E8 egujarati;0A8F eharmenian;0567 ehbopomofo;311D ehiragana;3048 ehookabove;1EBB eibopomofo;311F eight;0038 eightarabic;0668 eightbengali;09EE eightcircle;2467 eightcircleinversesansserif;2791 eightdeva;096E eighteencircle;2471 eighteenparen;2485 eighteenperiod;2499 eightgujarati;0AEE eightgurmukhi;0A6E eighthackarabic;0668 eighthangzhou;3028 eighthnotebeamed;266B eightideographicparen;3227 eightinferior;2088 eightmonospace;FF18 eightoldstyle;F738 eightparen;247B eightperiod;248F eightpersian;06F8 eightroman;2177 eightsuperior;2078 eightthai;0E58 einvertedbreve;0207 eiotifiedcyrillic;0465 ekatakana;30A8 ekatakanahalfwidth;FF74 ekonkargurmukhi;0A74 ekorean;3154 elcyrillic;043B element;2208 elevencircle;246A elevenparen;247E elevenperiod;2492 elevenroman;217A ellipsis;2026 ellipsisvertical;22EE emacron;0113 emacronacute;1E17 emacrongrave;1E15 emcyrillic;043C emdash;2014 emdashvertical;FE31 emonospace;FF45 emphasismarkarmenian;055B emptyset;2205 enbopomofo;3123 encyrillic;043D endash;2013 endashvertical;FE32 endescendercyrillic;04A3 eng;014B engbopomofo;3125 enghecyrillic;04A5 enhookcyrillic;04C8 enspace;2002 eogonek;0119 eokorean;3153 eopen;025B eopenclosed;029A eopenreversed;025C eopenreversedclosed;025E eopenreversedhook;025D eparen;24A0 epsilon;03B5 epsilontonos;03AD equal;003D equalmonospace;FF1D equalsmall;FE66 equalsuperior;207C equivalence;2261 erbopomofo;3126 ercyrillic;0440 ereversed;0258 ereversedcyrillic;044D escyrillic;0441 esdescendercyrillic;04AB esh;0283 eshcurl;0286 eshortdeva;090E eshortvowelsigndeva;0946 eshreversedloop;01AA eshsquatreversed;0285 esmallhiragana;3047 esmallkatakana;30A7 esmallkatakanahalfwidth;FF6A estimated;212E esuperior;F6EC eta;03B7 etarmenian;0568 etatonos;03AE eth;00F0 etilde;1EBD etildebelow;1E1B etnahtafoukhhebrew;0591 etnahtafoukhlefthebrew;0591 etnahtahebrew;0591 etnahtalefthebrew;0591 eturned;01DD eukorean;3161 euro;20AC evowelsignbengali;09C7 evowelsigndeva;0947 evowelsigngujarati;0AC7 exclam;0021 exclamarmenian;055C exclamdbl;203C exclamdown;00A1 exclamdownsmall;F7A1 exclammonospace;FF01 exclamsmall;F721 existential;2203 ezh;0292 ezhcaron;01EF ezhcurl;0293 ezhreversed;01B9 ezhtail;01BA f;0066 fadeva;095E fagurmukhi;0A5E fahrenheit;2109 fathaarabic;064E fathalowarabic;064E fathatanarabic;064B fbopomofo;3108 fcircle;24D5 fdotaccent;1E1F feharabic;0641 feharmenian;0586 fehfinalarabic;FED2 fehinitialarabic;FED3 fehmedialarabic;FED4 feicoptic;03E5 female;2640 ff;FB00 ffi;FB03 ffl;FB04 fi;FB01 fifteencircle;246E fifteenparen;2482 fifteenperiod;2496 figuredash;2012 filledbox;25A0 filledrect;25AC finalkaf;05DA finalkafdagesh;FB3A finalkafdageshhebrew;FB3A finalkafhebrew;05DA finalkafqamats;05DA 05B8 finalkafqamatshebrew;05DA 05B8 finalkafsheva;05DA 05B0 finalkafshevahebrew;05DA 05B0 finalmem;05DD finalmemhebrew;05DD finalnun;05DF finalnunhebrew;05DF finalpe;05E3 finalpehebrew;05E3 finaltsadi;05E5 finaltsadihebrew;05E5 firsttonechinese;02C9 fisheye;25C9 fitacyrillic;0473 five;0035 fivearabic;0665 fivebengali;09EB fivecircle;2464 fivecircleinversesansserif;278E fivedeva;096B fiveeighths;215D fivegujarati;0AEB fivegurmukhi;0A6B fivehackarabic;0665 fivehangzhou;3025 fiveideographicparen;3224 fiveinferior;2085 fivemonospace;FF15 fiveoldstyle;F735 fiveparen;2478 fiveperiod;248C fivepersian;06F5 fiveroman;2174 fivesuperior;2075 fivethai;0E55 fl;FB02 florin;0192 fmonospace;FF46 fmsquare;3399 fofanthai;0E1F fofathai;0E1D fongmanthai;0E4F forall;2200 four;0034 fourarabic;0664 fourbengali;09EA fourcircle;2463 fourcircleinversesansserif;278D fourdeva;096A fourgujarati;0AEA fourgurmukhi;0A6A fourhackarabic;0664 fourhangzhou;3024 fourideographicparen;3223 fourinferior;2084 fourmonospace;FF14 fournumeratorbengali;09F7 fouroldstyle;F734 fourparen;2477 fourperiod;248B fourpersian;06F4 fourroman;2173 foursuperior;2074 fourteencircle;246D fourteenparen;2481 fourteenperiod;2495 fourthai;0E54 fourthtonechinese;02CB fparen;24A1 fraction;2044 franc;20A3 g;0067 gabengali;0997 gacute;01F5 gadeva;0917 gafarabic;06AF gaffinalarabic;FB93 gafinitialarabic;FB94 gafmedialarabic;FB95 gagujarati;0A97 gagurmukhi;0A17 gahiragana;304C gakatakana;30AC gamma;03B3 gammalatinsmall;0263 gammasuperior;02E0 gangiacoptic;03EB gbopomofo;310D gbreve;011F gcaron;01E7 gcedilla;0123 gcircle;24D6 gcircumflex;011D gcommaaccent;0123 gdot;0121 gdotaccent;0121 gecyrillic;0433 gehiragana;3052 gekatakana;30B2 geometricallyequal;2251 gereshaccenthebrew;059C gereshhebrew;05F3 gereshmuqdamhebrew;059D germandbls;00DF gershayimaccenthebrew;059E gershayimhebrew;05F4 getamark;3013 ghabengali;0998 ghadarmenian;0572 ghadeva;0918 ghagujarati;0A98 ghagurmukhi;0A18 ghainarabic;063A ghainfinalarabic;FECE ghaininitialarabic;FECF ghainmedialarabic;FED0 ghemiddlehookcyrillic;0495 ghestrokecyrillic;0493 gheupturncyrillic;0491 ghhadeva;095A ghhagurmukhi;0A5A ghook;0260 ghzsquare;3393 gihiragana;304E gikatakana;30AE gimarmenian;0563 gimel;05D2 gimeldagesh;FB32 gimeldageshhebrew;FB32 gimelhebrew;05D2 gjecyrillic;0453 glottalinvertedstroke;01BE glottalstop;0294 glottalstopinverted;0296 glottalstopmod;02C0 glottalstopreversed;0295 glottalstopreversedmod;02C1 glottalstopreversedsuperior;02E4 glottalstopstroke;02A1 glottalstopstrokereversed;02A2 gmacron;1E21 gmonospace;FF47 gohiragana;3054 gokatakana;30B4 gparen;24A2 gpasquare;33AC gradient;2207 grave;0060 gravebelowcmb;0316 gravecmb;0300 gravecomb;0300 gravedeva;0953 gravelowmod;02CE gravemonospace;FF40 gravetonecmb;0340 greater;003E greaterequal;2265 greaterequalorless;22DB greatermonospace;FF1E greaterorequivalent;2273 greaterorless;2277 greateroverequal;2267 greatersmall;FE65 gscript;0261 gstroke;01E5 guhiragana;3050 guillemotleft;00AB guillemotright;00BB guilsinglleft;2039 guilsinglright;203A gukatakana;30B0 guramusquare;3318 gysquare;33C9 h;0068 haabkhasiancyrillic;04A9 haaltonearabic;06C1 habengali;09B9 hadescendercyrillic;04B3 hadeva;0939 hagujarati;0AB9 hagurmukhi;0A39 haharabic;062D hahfinalarabic;FEA2 hahinitialarabic;FEA3 hahiragana;306F hahmedialarabic;FEA4 haitusquare;332A hakatakana;30CF hakatakanahalfwidth;FF8A halantgurmukhi;0A4D hamzaarabic;0621 hamzadammaarabic;0621 064F hamzadammatanarabic;0621 064C hamzafathaarabic;0621 064E hamzafathatanarabic;0621 064B hamzalowarabic;0621 hamzalowkasraarabic;0621 0650 hamzalowkasratanarabic;0621 064D hamzasukunarabic;0621 0652 hangulfiller;3164 hardsigncyrillic;044A harpoonleftbarbup;21BC harpoonrightbarbup;21C0 hasquare;33CA hatafpatah;05B2 hatafpatah16;05B2 hatafpatah23;05B2 hatafpatah2f;05B2 hatafpatahhebrew;05B2 hatafpatahnarrowhebrew;05B2 hatafpatahquarterhebrew;05B2 hatafpatahwidehebrew;05B2 hatafqamats;05B3 hatafqamats1b;05B3 hatafqamats28;05B3 hatafqamats34;05B3 hatafqamatshebrew;05B3 hatafqamatsnarrowhebrew;05B3 hatafqamatsquarterhebrew;05B3 hatafqamatswidehebrew;05B3 hatafsegol;05B1 hatafsegol17;05B1 hatafsegol24;05B1 hatafsegol30;05B1 hatafsegolhebrew;05B1 hatafsegolnarrowhebrew;05B1 hatafsegolquarterhebrew;05B1 hatafsegolwidehebrew;05B1 hbar;0127 hbopomofo;310F hbrevebelow;1E2B hcedilla;1E29 hcircle;24D7 hcircumflex;0125 hdieresis;1E27 hdotaccent;1E23 hdotbelow;1E25 he;05D4 heart;2665 heartsuitblack;2665 heartsuitwhite;2661 hedagesh;FB34 hedageshhebrew;FB34 hehaltonearabic;06C1 heharabic;0647 hehebrew;05D4 hehfinalaltonearabic;FBA7 hehfinalalttwoarabic;FEEA hehfinalarabic;FEEA hehhamzaabovefinalarabic;FBA5 hehhamzaaboveisolatedarabic;FBA4 hehinitialaltonearabic;FBA8 hehinitialarabic;FEEB hehiragana;3078 hehmedialaltonearabic;FBA9 hehmedialarabic;FEEC heiseierasquare;337B hekatakana;30D8 hekatakanahalfwidth;FF8D hekutaarusquare;3336 henghook;0267 herutusquare;3339 het;05D7 hethebrew;05D7 hhook;0266 hhooksuperior;02B1 hieuhacirclekorean;327B hieuhaparenkorean;321B hieuhcirclekorean;326D hieuhkorean;314E hieuhparenkorean;320D hihiragana;3072 hikatakana;30D2 hikatakanahalfwidth;FF8B hiriq;05B4 hiriq14;05B4 hiriq21;05B4 hiriq2d;05B4 hiriqhebrew;05B4 hiriqnarrowhebrew;05B4 hiriqquarterhebrew;05B4 hiriqwidehebrew;05B4 hlinebelow;1E96 hmonospace;FF48 hoarmenian;0570 hohipthai;0E2B hohiragana;307B hokatakana;30DB hokatakanahalfwidth;FF8E holam;05B9 holam19;05B9 holam26;05B9 holam32;05B9 holamhebrew;05B9 holamnarrowhebrew;05B9 holamquarterhebrew;05B9 holamwidehebrew;05B9 honokhukthai;0E2E hookabovecomb;0309 hookcmb;0309 hookpalatalizedbelowcmb;0321 hookretroflexbelowcmb;0322 hoonsquare;3342 horicoptic;03E9 horizontalbar;2015 horncmb;031B hotsprings;2668 house;2302 hparen;24A3 hsuperior;02B0 hturned;0265 huhiragana;3075 huiitosquare;3333 hukatakana;30D5 hukatakanahalfwidth;FF8C hungarumlaut;02DD hungarumlautcmb;030B hv;0195 hyphen;002D hypheninferior;F6E5 hyphenmonospace;FF0D hyphensmall;FE63 hyphensuperior;F6E6 hyphentwo;2010 i;0069 iacute;00ED iacyrillic;044F ibengali;0987 ibopomofo;3127 ibreve;012D icaron;01D0 icircle;24D8 icircumflex;00EE icyrillic;0456 idblgrave;0209 ideographearthcircle;328F ideographfirecircle;328B ideographicallianceparen;323F ideographiccallparen;323A ideographiccentrecircle;32A5 ideographicclose;3006 ideographiccomma;3001 ideographiccommaleft;FF64 ideographiccongratulationparen;3237 ideographiccorrectcircle;32A3 ideographicearthparen;322F ideographicenterpriseparen;323D ideographicexcellentcircle;329D ideographicfestivalparen;3240 ideographicfinancialcircle;3296 ideographicfinancialparen;3236 ideographicfireparen;322B ideographichaveparen;3232 ideographichighcircle;32A4 ideographiciterationmark;3005 ideographiclaborcircle;3298 ideographiclaborparen;3238 ideographicleftcircle;32A7 ideographiclowcircle;32A6 ideographicmedicinecircle;32A9 ideographicmetalparen;322E ideographicmoonparen;322A ideographicnameparen;3234 ideographicperiod;3002 ideographicprintcircle;329E ideographicreachparen;3243 ideographicrepresentparen;3239 ideographicresourceparen;323E ideographicrightcircle;32A8 ideographicsecretcircle;3299 ideographicselfparen;3242 ideographicsocietyparen;3233 ideographicspace;3000 ideographicspecialparen;3235 ideographicstockparen;3231 ideographicstudyparen;323B ideographicsunparen;3230 ideographicsuperviseparen;323C ideographicwaterparen;322C ideographicwoodparen;322D ideographiczero;3007 ideographmetalcircle;328E ideographmooncircle;328A ideographnamecircle;3294 ideographsuncircle;3290 ideographwatercircle;328C ideographwoodcircle;328D ideva;0907 idieresis;00EF idieresisacute;1E2F idieresiscyrillic;04E5 idotbelow;1ECB iebrevecyrillic;04D7 iecyrillic;0435 ieungacirclekorean;3275 ieungaparenkorean;3215 ieungcirclekorean;3267 ieungkorean;3147 ieungparenkorean;3207 igrave;00EC igujarati;0A87 igurmukhi;0A07 ihiragana;3044 ihookabove;1EC9 iibengali;0988 iicyrillic;0438 iideva;0908 iigujarati;0A88 iigurmukhi;0A08 iimatragurmukhi;0A40 iinvertedbreve;020B iishortcyrillic;0439 iivowelsignbengali;09C0 iivowelsigndeva;0940 iivowelsigngujarati;0AC0 ij;0133 ikatakana;30A4 ikatakanahalfwidth;FF72 ikorean;3163 ilde;02DC iluyhebrew;05AC imacron;012B imacroncyrillic;04E3 imageorapproximatelyequal;2253 imatragurmukhi;0A3F imonospace;FF49 increment;2206 infinity;221E iniarmenian;056B integral;222B integralbottom;2321 integralbt;2321 integralex;F8F5 integraltop;2320 integraltp;2320 intersection;2229 intisquare;3305 invbullet;25D8 invcircle;25D9 invsmileface;263B iocyrillic;0451 iogonek;012F iota;03B9 iotadieresis;03CA iotadieresistonos;0390 iotalatin;0269 iotatonos;03AF iparen;24A4 irigurmukhi;0A72 ismallhiragana;3043 ismallkatakana;30A3 ismallkatakanahalfwidth;FF68 issharbengali;09FA istroke;0268 isuperior;F6ED iterationhiragana;309D iterationkatakana;30FD itilde;0129 itildebelow;1E2D iubopomofo;3129 iucyrillic;044E ivowelsignbengali;09BF ivowelsigndeva;093F ivowelsigngujarati;0ABF izhitsacyrillic;0475 izhitsadblgravecyrillic;0477 j;006A jaarmenian;0571 jabengali;099C jadeva;091C jagujarati;0A9C jagurmukhi;0A1C jbopomofo;3110 jcaron;01F0 jcircle;24D9 jcircumflex;0135 jcrossedtail;029D jdotlessstroke;025F jecyrillic;0458 jeemarabic;062C jeemfinalarabic;FE9E jeeminitialarabic;FE9F jeemmedialarabic;FEA0 jeharabic;0698 jehfinalarabic;FB8B jhabengali;099D jhadeva;091D jhagujarati;0A9D jhagurmukhi;0A1D jheharmenian;057B jis;3004 jmonospace;FF4A jparen;24A5 jsuperior;02B2 k;006B kabashkircyrillic;04A1 kabengali;0995 kacute;1E31 kacyrillic;043A kadescendercyrillic;049B kadeva;0915 kaf;05DB kafarabic;0643 kafdagesh;FB3B kafdageshhebrew;FB3B kaffinalarabic;FEDA kafhebrew;05DB kafinitialarabic;FEDB kafmedialarabic;FEDC kafrafehebrew;FB4D kagujarati;0A95 kagurmukhi;0A15 kahiragana;304B kahookcyrillic;04C4 kakatakana;30AB kakatakanahalfwidth;FF76 kappa;03BA kappasymbolgreek;03F0 kapyeounmieumkorean;3171 kapyeounphieuphkorean;3184 kapyeounpieupkorean;3178 kapyeounssangpieupkorean;3179 karoriisquare;330D kashidaautoarabic;0640 kashidaautonosidebearingarabic;0640 kasmallkatakana;30F5 kasquare;3384 kasraarabic;0650 kasratanarabic;064D kastrokecyrillic;049F katahiraprolongmarkhalfwidth;FF70 kaverticalstrokecyrillic;049D kbopomofo;310E kcalsquare;3389 kcaron;01E9 kcedilla;0137 kcircle;24DA kcommaaccent;0137 kdotbelow;1E33 keharmenian;0584 kehiragana;3051 kekatakana;30B1 kekatakanahalfwidth;FF79 kenarmenian;056F kesmallkatakana;30F6 kgreenlandic;0138 khabengali;0996 khacyrillic;0445 khadeva;0916 khagujarati;0A96 khagurmukhi;0A16 khaharabic;062E khahfinalarabic;FEA6 khahinitialarabic;FEA7 khahmedialarabic;FEA8 kheicoptic;03E7 khhadeva;0959 khhagurmukhi;0A59 khieukhacirclekorean;3278 khieukhaparenkorean;3218 khieukhcirclekorean;326A khieukhkorean;314B khieukhparenkorean;320A khokhaithai;0E02 khokhonthai;0E05 khokhuatthai;0E03 khokhwaithai;0E04 khomutthai;0E5B khook;0199 khorakhangthai;0E06 khzsquare;3391 kihiragana;304D kikatakana;30AD kikatakanahalfwidth;FF77 kiroguramusquare;3315 kiromeetorusquare;3316 kirosquare;3314 kiyeokacirclekorean;326E kiyeokaparenkorean;320E kiyeokcirclekorean;3260 kiyeokkorean;3131 kiyeokparenkorean;3200 kiyeoksioskorean;3133 kjecyrillic;045C klinebelow;1E35 klsquare;3398 kmcubedsquare;33A6 kmonospace;FF4B kmsquaredsquare;33A2 kohiragana;3053 kohmsquare;33C0 kokaithai;0E01 kokatakana;30B3 kokatakanahalfwidth;FF7A kooposquare;331E koppacyrillic;0481 koreanstandardsymbol;327F koroniscmb;0343 kparen;24A6 kpasquare;33AA ksicyrillic;046F ktsquare;33CF kturned;029E kuhiragana;304F kukatakana;30AF kukatakanahalfwidth;FF78 kvsquare;33B8 kwsquare;33BE l;006C labengali;09B2 lacute;013A ladeva;0932 lagujarati;0AB2 lagurmukhi;0A32 lakkhangyaothai;0E45 lamaleffinalarabic;FEFC lamalefhamzaabovefinalarabic;FEF8 lamalefhamzaaboveisolatedarabic;FEF7 lamalefhamzabelowfinalarabic;FEFA lamalefhamzabelowisolatedarabic;FEF9 lamalefisolatedarabic;FEFB lamalefmaddaabovefinalarabic;FEF6 lamalefmaddaaboveisolatedarabic;FEF5 lamarabic;0644 lambda;03BB lambdastroke;019B lamed;05DC lameddagesh;FB3C lameddageshhebrew;FB3C lamedhebrew;05DC lamedholam;05DC 05B9 lamedholamdagesh;05DC 05B9 05BC lamedholamdageshhebrew;05DC 05B9 05BC lamedholamhebrew;05DC 05B9 lamfinalarabic;FEDE lamhahinitialarabic;FCCA laminitialarabic;FEDF lamjeeminitialarabic;FCC9 lamkhahinitialarabic;FCCB lamlamhehisolatedarabic;FDF2 lammedialarabic;FEE0 lammeemhahinitialarabic;FD88 lammeeminitialarabic;FCCC lammeemjeeminitialarabic;FEDF FEE4 FEA0 lammeemkhahinitialarabic;FEDF FEE4 FEA8 largecircle;25EF lbar;019A lbelt;026C lbopomofo;310C lcaron;013E lcedilla;013C lcircle;24DB lcircumflexbelow;1E3D lcommaaccent;013C ldot;0140 ldotaccent;0140 ldotbelow;1E37 ldotbelowmacron;1E39 leftangleabovecmb;031A lefttackbelowcmb;0318 less;003C lessequal;2264 lessequalorgreater;22DA lessmonospace;FF1C lessorequivalent;2272 lessorgreater;2276 lessoverequal;2266 lesssmall;FE64 lezh;026E lfblock;258C lhookretroflex;026D lira;20A4 liwnarmenian;056C lj;01C9 ljecyrillic;0459 ll;F6C0 lladeva;0933 llagujarati;0AB3 llinebelow;1E3B llladeva;0934 llvocalicbengali;09E1 llvocalicdeva;0961 llvocalicvowelsignbengali;09E3 llvocalicvowelsigndeva;0963 lmiddletilde;026B lmonospace;FF4C lmsquare;33D0 lochulathai;0E2C logicaland;2227 logicalnot;00AC logicalnotreversed;2310 logicalor;2228 lolingthai;0E25 longs;017F lowlinecenterline;FE4E lowlinecmb;0332 lowlinedashed;FE4D lozenge;25CA lparen;24A7 lslash;0142 lsquare;2113 lsuperior;F6EE ltshade;2591 luthai;0E26 lvocalicbengali;098C lvocalicdeva;090C lvocalicvowelsignbengali;09E2 lvocalicvowelsigndeva;0962 lxsquare;33D3 m;006D mabengali;09AE macron;00AF macronbelowcmb;0331 macroncmb;0304 macronlowmod;02CD macronmonospace;FFE3 macute;1E3F madeva;092E magujarati;0AAE magurmukhi;0A2E mahapakhhebrew;05A4 mahapakhlefthebrew;05A4 mahiragana;307E maichattawalowleftthai;F895 maichattawalowrightthai;F894 maichattawathai;0E4B maichattawaupperleftthai;F893 maieklowleftthai;F88C maieklowrightthai;F88B maiekthai;0E48 maiekupperleftthai;F88A maihanakatleftthai;F884 maihanakatthai;0E31 maitaikhuleftthai;F889 maitaikhuthai;0E47 maitholowleftthai;F88F maitholowrightthai;F88E maithothai;0E49 maithoupperleftthai;F88D maitrilowleftthai;F892 maitrilowrightthai;F891 maitrithai;0E4A maitriupperleftthai;F890 maiyamokthai;0E46 makatakana;30DE makatakanahalfwidth;FF8F male;2642 mansyonsquare;3347 maqafhebrew;05BE mars;2642 masoracirclehebrew;05AF masquare;3383 mbopomofo;3107 mbsquare;33D4 mcircle;24DC mcubedsquare;33A5 mdotaccent;1E41 mdotbelow;1E43 meemarabic;0645 meemfinalarabic;FEE2 meeminitialarabic;FEE3 meemmedialarabic;FEE4 meemmeeminitialarabic;FCD1 meemmeemisolatedarabic;FC48 meetorusquare;334D mehiragana;3081 meizierasquare;337E mekatakana;30E1 mekatakanahalfwidth;FF92 mem;05DE memdagesh;FB3E memdageshhebrew;FB3E memhebrew;05DE menarmenian;0574 merkhahebrew;05A5 merkhakefulahebrew;05A6 merkhakefulalefthebrew;05A6 merkhalefthebrew;05A5 mhook;0271 mhzsquare;3392 middledotkatakanahalfwidth;FF65 middot;00B7 mieumacirclekorean;3272 mieumaparenkorean;3212 mieumcirclekorean;3264 mieumkorean;3141 mieumpansioskorean;3170 mieumparenkorean;3204 mieumpieupkorean;316E mieumsioskorean;316F mihiragana;307F mikatakana;30DF mikatakanahalfwidth;FF90 minus;2212 minusbelowcmb;0320 minuscircle;2296 minusmod;02D7 minusplus;2213 minute;2032 miribaarusquare;334A mirisquare;3349 mlonglegturned;0270 mlsquare;3396 mmcubedsquare;33A3 mmonospace;FF4D mmsquaredsquare;339F mohiragana;3082 mohmsquare;33C1 mokatakana;30E2 mokatakanahalfwidth;FF93 molsquare;33D6 momathai;0E21 moverssquare;33A7 moverssquaredsquare;33A8 mparen;24A8 mpasquare;33AB mssquare;33B3 msuperior;F6EF mturned;026F mu;00B5 mu1;00B5 muasquare;3382 muchgreater;226B muchless;226A mufsquare;338C mugreek;03BC mugsquare;338D muhiragana;3080 mukatakana;30E0 mukatakanahalfwidth;FF91 mulsquare;3395 multiply;00D7 mumsquare;339B munahhebrew;05A3 munahlefthebrew;05A3 musicalnote;266A musicalnotedbl;266B musicflatsign;266D musicsharpsign;266F mussquare;33B2 muvsquare;33B6 muwsquare;33BC mvmegasquare;33B9 mvsquare;33B7 mwmegasquare;33BF mwsquare;33BD n;006E nabengali;09A8 nabla;2207 nacute;0144 nadeva;0928 nagujarati;0AA8 nagurmukhi;0A28 nahiragana;306A nakatakana;30CA nakatakanahalfwidth;FF85 napostrophe;0149 nasquare;3381 nbopomofo;310B nbspace;00A0 ncaron;0148 ncedilla;0146 ncircle;24DD ncircumflexbelow;1E4B ncommaaccent;0146 ndotaccent;1E45 ndotbelow;1E47 nehiragana;306D nekatakana;30CD nekatakanahalfwidth;FF88 newsheqelsign;20AA nfsquare;338B ngabengali;0999 ngadeva;0919 ngagujarati;0A99 ngagurmukhi;0A19 ngonguthai;0E07 nhiragana;3093 nhookleft;0272 nhookretroflex;0273 nieunacirclekorean;326F nieunaparenkorean;320F nieuncieuckorean;3135 nieuncirclekorean;3261 nieunhieuhkorean;3136 nieunkorean;3134 nieunpansioskorean;3168 nieunparenkorean;3201 nieunsioskorean;3167 nieuntikeutkorean;3166 nihiragana;306B nikatakana;30CB nikatakanahalfwidth;FF86 nikhahitleftthai;F899 nikhahitthai;0E4D nine;0039 ninearabic;0669 ninebengali;09EF ninecircle;2468 ninecircleinversesansserif;2792 ninedeva;096F ninegujarati;0AEF ninegurmukhi;0A6F ninehackarabic;0669 ninehangzhou;3029 nineideographicparen;3228 nineinferior;2089 ninemonospace;FF19 nineoldstyle;F739 nineparen;247C nineperiod;2490 ninepersian;06F9 nineroman;2178 ninesuperior;2079 nineteencircle;2472 nineteenparen;2486 nineteenperiod;249A ninethai;0E59 nj;01CC njecyrillic;045A nkatakana;30F3 nkatakanahalfwidth;FF9D nlegrightlong;019E nlinebelow;1E49 nmonospace;FF4E nmsquare;339A nnabengali;09A3 nnadeva;0923 nnagujarati;0AA3 nnagurmukhi;0A23 nnnadeva;0929 nohiragana;306E nokatakana;30CE nokatakanahalfwidth;FF89 nonbreakingspace;00A0 nonenthai;0E13 nonuthai;0E19 noonarabic;0646 noonfinalarabic;FEE6 noonghunnaarabic;06BA noonghunnafinalarabic;FB9F noonhehinitialarabic;FEE7 FEEC nooninitialarabic;FEE7 noonjeeminitialarabic;FCD2 noonjeemisolatedarabic;FC4B noonmedialarabic;FEE8 noonmeeminitialarabic;FCD5 noonmeemisolatedarabic;FC4E noonnoonfinalarabic;FC8D notcontains;220C notelement;2209 notelementof;2209 notequal;2260 notgreater;226F notgreaternorequal;2271 notgreaternorless;2279 notidentical;2262 notless;226E notlessnorequal;2270 notparallel;2226 notprecedes;2280 notsubset;2284 notsucceeds;2281 notsuperset;2285 nowarmenian;0576 nparen;24A9 nssquare;33B1 nsuperior;207F ntilde;00F1 nu;03BD nuhiragana;306C nukatakana;30CC nukatakanahalfwidth;FF87 nuktabengali;09BC nuktadeva;093C nuktagujarati;0ABC nuktagurmukhi;0A3C numbersign;0023 numbersignmonospace;FF03 numbersignsmall;FE5F numeralsigngreek;0374 numeralsignlowergreek;0375 numero;2116 nun;05E0 nundagesh;FB40 nundageshhebrew;FB40 nunhebrew;05E0 nvsquare;33B5 nwsquare;33BB nyabengali;099E nyadeva;091E nyagujarati;0A9E nyagurmukhi;0A1E o;006F oacute;00F3 oangthai;0E2D obarred;0275 obarredcyrillic;04E9 obarreddieresiscyrillic;04EB obengali;0993 obopomofo;311B obreve;014F ocandradeva;0911 ocandragujarati;0A91 ocandravowelsigndeva;0949 ocandravowelsigngujarati;0AC9 ocaron;01D2 ocircle;24DE ocircumflex;00F4 ocircumflexacute;1ED1 ocircumflexdotbelow;1ED9 ocircumflexgrave;1ED3 ocircumflexhookabove;1ED5 ocircumflextilde;1ED7 ocyrillic;043E odblacute;0151 odblgrave;020D odeva;0913 odieresis;00F6 odieresiscyrillic;04E7 odotbelow;1ECD oe;0153 oekorean;315A ogonek;02DB ogonekcmb;0328 ograve;00F2 ogujarati;0A93 oharmenian;0585 ohiragana;304A ohookabove;1ECF ohorn;01A1 ohornacute;1EDB ohorndotbelow;1EE3 ohorngrave;1EDD ohornhookabove;1EDF ohorntilde;1EE1 ohungarumlaut;0151 oi;01A3 oinvertedbreve;020F okatakana;30AA okatakanahalfwidth;FF75 okorean;3157 olehebrew;05AB omacron;014D omacronacute;1E53 omacrongrave;1E51 omdeva;0950 omega;03C9 omega1;03D6 omegacyrillic;0461 omegalatinclosed;0277 omegaroundcyrillic;047B omegatitlocyrillic;047D omegatonos;03CE omgujarati;0AD0 omicron;03BF omicrontonos;03CC omonospace;FF4F one;0031 onearabic;0661 onebengali;09E7 onecircle;2460 onecircleinversesansserif;278A onedeva;0967 onedotenleader;2024 oneeighth;215B onefitted;F6DC onegujarati;0AE7 onegurmukhi;0A67 onehackarabic;0661 onehalf;00BD onehangzhou;3021 oneideographicparen;3220 oneinferior;2081 onemonospace;FF11 onenumeratorbengali;09F4 oneoldstyle;F731 oneparen;2474 oneperiod;2488 onepersian;06F1 onequarter;00BC oneroman;2170 onesuperior;00B9 onethai;0E51 onethird;2153 oogonek;01EB oogonekmacron;01ED oogurmukhi;0A13 oomatragurmukhi;0A4B oopen;0254 oparen;24AA openbullet;25E6 option;2325 ordfeminine;00AA ordmasculine;00BA orthogonal;221F oshortdeva;0912 oshortvowelsigndeva;094A oslash;00F8 oslashacute;01FF osmallhiragana;3049 osmallkatakana;30A9 osmallkatakanahalfwidth;FF6B ostrokeacute;01FF osuperior;F6F0 otcyrillic;047F otilde;00F5 otildeacute;1E4D otildedieresis;1E4F oubopomofo;3121 overline;203E overlinecenterline;FE4A overlinecmb;0305 overlinedashed;FE49 overlinedblwavy;FE4C overlinewavy;FE4B overscore;00AF ovowelsignbengali;09CB ovowelsigndeva;094B ovowelsigngujarati;0ACB p;0070 paampssquare;3380 paasentosquare;332B pabengali;09AA pacute;1E55 padeva;092A pagedown;21DF pageup;21DE pagujarati;0AAA pagurmukhi;0A2A pahiragana;3071 paiyannoithai;0E2F pakatakana;30D1 palatalizationcyrilliccmb;0484 palochkacyrillic;04C0 pansioskorean;317F paragraph;00B6 parallel;2225 parenleft;0028 parenleftaltonearabic;FD3E parenleftbt;F8ED parenleftex;F8EC parenleftinferior;208D parenleftmonospace;FF08 parenleftsmall;FE59 parenleftsuperior;207D parenlefttp;F8EB parenleftvertical;FE35 parenright;0029 parenrightaltonearabic;FD3F parenrightbt;F8F8 parenrightex;F8F7 parenrightinferior;208E parenrightmonospace;FF09 parenrightsmall;FE5A parenrightsuperior;207E parenrighttp;F8F6 parenrightvertical;FE36 partialdiff;2202 paseqhebrew;05C0 pashtahebrew;0599 pasquare;33A9 patah;05B7 patah11;05B7 patah1d;05B7 patah2a;05B7 patahhebrew;05B7 patahnarrowhebrew;05B7 patahquarterhebrew;05B7 patahwidehebrew;05B7 pazerhebrew;05A1 pbopomofo;3106 pcircle;24DF pdotaccent;1E57 pe;05E4 pecyrillic;043F pedagesh;FB44 pedageshhebrew;FB44 peezisquare;333B pefinaldageshhebrew;FB43 peharabic;067E peharmenian;057A pehebrew;05E4 pehfinalarabic;FB57 pehinitialarabic;FB58 pehiragana;307A pehmedialarabic;FB59 pekatakana;30DA pemiddlehookcyrillic;04A7 perafehebrew;FB4E percent;0025 percentarabic;066A percentmonospace;FF05 percentsmall;FE6A period;002E periodarmenian;0589 periodcentered;00B7 periodhalfwidth;FF61 periodinferior;F6E7 periodmonospace;FF0E periodsmall;FE52 periodsuperior;F6E8 perispomenigreekcmb;0342 perpendicular;22A5 perthousand;2030 peseta;20A7 pfsquare;338A phabengali;09AB phadeva;092B phagujarati;0AAB phagurmukhi;0A2B phi;03C6 phi1;03D5 phieuphacirclekorean;327A phieuphaparenkorean;321A phieuphcirclekorean;326C phieuphkorean;314D phieuphparenkorean;320C philatin;0278 phinthuthai;0E3A phisymbolgreek;03D5 phook;01A5 phophanthai;0E1E phophungthai;0E1C phosamphaothai;0E20 pi;03C0 pieupacirclekorean;3273 pieupaparenkorean;3213 pieupcieuckorean;3176 pieupcirclekorean;3265 pieupkiyeokkorean;3172 pieupkorean;3142 pieupparenkorean;3205 pieupsioskiyeokkorean;3174 pieupsioskorean;3144 pieupsiostikeutkorean;3175 pieupthieuthkorean;3177 pieuptikeutkorean;3173 pihiragana;3074 pikatakana;30D4 pisymbolgreek;03D6 piwrarmenian;0583 plus;002B plusbelowcmb;031F pluscircle;2295 plusminus;00B1 plusmod;02D6 plusmonospace;FF0B plussmall;FE62 plussuperior;207A pmonospace;FF50 pmsquare;33D8 pohiragana;307D pointingindexdownwhite;261F pointingindexleftwhite;261C pointingindexrightwhite;261E pointingindexupwhite;261D pokatakana;30DD poplathai;0E1B postalmark;3012 postalmarkface;3020 pparen;24AB precedes;227A prescription;211E primemod;02B9 primereversed;2035 product;220F projective;2305 prolongedkana;30FC propellor;2318 propersubset;2282 propersuperset;2283 proportion;2237 proportional;221D psi;03C8 psicyrillic;0471 psilipneumatacyrilliccmb;0486 pssquare;33B0 puhiragana;3077 pukatakana;30D7 pvsquare;33B4 pwsquare;33BA q;0071 qadeva;0958 qadmahebrew;05A8 qafarabic;0642 qaffinalarabic;FED6 qafinitialarabic;FED7 qafmedialarabic;FED8 qamats;05B8 qamats10;05B8 qamats1a;05B8 qamats1c;05B8 qamats27;05B8 qamats29;05B8 qamats33;05B8 qamatsde;05B8 qamatshebrew;05B8 qamatsnarrowhebrew;05B8 qamatsqatanhebrew;05B8 qamatsqatannarrowhebrew;05B8 qamatsqatanquarterhebrew;05B8 qamatsqatanwidehebrew;05B8 qamatsquarterhebrew;05B8 qamatswidehebrew;05B8 qarneyparahebrew;059F qbopomofo;3111 qcircle;24E0 qhook;02A0 qmonospace;FF51 qof;05E7 qofdagesh;FB47 qofdageshhebrew;FB47 qofhatafpatah;05E7 05B2 qofhatafpatahhebrew;05E7 05B2 qofhatafsegol;05E7 05B1 qofhatafsegolhebrew;05E7 05B1 qofhebrew;05E7 qofhiriq;05E7 05B4 qofhiriqhebrew;05E7 05B4 qofholam;05E7 05B9 qofholamhebrew;05E7 05B9 qofpatah;05E7 05B7 qofpatahhebrew;05E7 05B7 qofqamats;05E7 05B8 qofqamatshebrew;05E7 05B8 qofqubuts;05E7 05BB qofqubutshebrew;05E7 05BB qofsegol;05E7 05B6 qofsegolhebrew;05E7 05B6 qofsheva;05E7 05B0 qofshevahebrew;05E7 05B0 qoftsere;05E7 05B5 qoftserehebrew;05E7 05B5 qparen;24AC quarternote;2669 qubuts;05BB qubuts18;05BB qubuts25;05BB qubuts31;05BB qubutshebrew;05BB qubutsnarrowhebrew;05BB qubutsquarterhebrew;05BB qubutswidehebrew;05BB question;003F questionarabic;061F questionarmenian;055E questiondown;00BF questiondownsmall;F7BF questiongreek;037E questionmonospace;FF1F questionsmall;F73F quotedbl;0022 quotedblbase;201E quotedblleft;201C quotedblmonospace;FF02 quotedblprime;301E quotedblprimereversed;301D quotedblright;201D quoteleft;2018 quoteleftreversed;201B quotereversed;201B quoteright;2019 quoterightn;0149 quotesinglbase;201A quotesingle;0027 quotesinglemonospace;FF07 r;0072 raarmenian;057C rabengali;09B0 racute;0155 radeva;0930 radical;221A radicalex;F8E5 radoverssquare;33AE radoverssquaredsquare;33AF radsquare;33AD rafe;05BF rafehebrew;05BF ragujarati;0AB0 ragurmukhi;0A30 rahiragana;3089 rakatakana;30E9 rakatakanahalfwidth;FF97 ralowerdiagonalbengali;09F1 ramiddlediagonalbengali;09F0 ramshorn;0264 ratio;2236 rbopomofo;3116 rcaron;0159 rcedilla;0157 rcircle;24E1 rcommaaccent;0157 rdblgrave;0211 rdotaccent;1E59 rdotbelow;1E5B rdotbelowmacron;1E5D referencemark;203B reflexsubset;2286 reflexsuperset;2287 registered;00AE registersans;F8E8 registerserif;F6DA reharabic;0631 reharmenian;0580 rehfinalarabic;FEAE rehiragana;308C rehyehaleflamarabic;0631 FEF3 FE8E 0644 rekatakana;30EC rekatakanahalfwidth;FF9A resh;05E8 reshdageshhebrew;FB48 reshhatafpatah;05E8 05B2 reshhatafpatahhebrew;05E8 05B2 reshhatafsegol;05E8 05B1 reshhatafsegolhebrew;05E8 05B1 reshhebrew;05E8 reshhiriq;05E8 05B4 reshhiriqhebrew;05E8 05B4 reshholam;05E8 05B9 reshholamhebrew;05E8 05B9 reshpatah;05E8 05B7 reshpatahhebrew;05E8 05B7 reshqamats;05E8 05B8 reshqamatshebrew;05E8 05B8 reshqubuts;05E8 05BB reshqubutshebrew;05E8 05BB reshsegol;05E8 05B6 reshsegolhebrew;05E8 05B6 reshsheva;05E8 05B0 reshshevahebrew;05E8 05B0 reshtsere;05E8 05B5 reshtserehebrew;05E8 05B5 reversedtilde;223D reviahebrew;0597 reviamugrashhebrew;0597 revlogicalnot;2310 rfishhook;027E rfishhookreversed;027F rhabengali;09DD rhadeva;095D rho;03C1 rhook;027D rhookturned;027B rhookturnedsuperior;02B5 rhosymbolgreek;03F1 rhotichookmod;02DE rieulacirclekorean;3271 rieulaparenkorean;3211 rieulcirclekorean;3263 rieulhieuhkorean;3140 rieulkiyeokkorean;313A rieulkiyeoksioskorean;3169 rieulkorean;3139 rieulmieumkorean;313B rieulpansioskorean;316C rieulparenkorean;3203 rieulphieuphkorean;313F rieulpieupkorean;313C rieulpieupsioskorean;316B rieulsioskorean;313D rieulthieuthkorean;313E rieultikeutkorean;316A rieulyeorinhieuhkorean;316D rightangle;221F righttackbelowcmb;0319 righttriangle;22BF rihiragana;308A rikatakana;30EA rikatakanahalfwidth;FF98 ring;02DA ringbelowcmb;0325 ringcmb;030A ringhalfleft;02BF ringhalfleftarmenian;0559 ringhalfleftbelowcmb;031C ringhalfleftcentered;02D3 ringhalfright;02BE ringhalfrightbelowcmb;0339 ringhalfrightcentered;02D2 rinvertedbreve;0213 rittorusquare;3351 rlinebelow;1E5F rlongleg;027C rlonglegturned;027A rmonospace;FF52 rohiragana;308D rokatakana;30ED rokatakanahalfwidth;FF9B roruathai;0E23 rparen;24AD rrabengali;09DC rradeva;0931 rragurmukhi;0A5C rreharabic;0691 rrehfinalarabic;FB8D rrvocalicbengali;09E0 rrvocalicdeva;0960 rrvocalicgujarati;0AE0 rrvocalicvowelsignbengali;09C4 rrvocalicvowelsigndeva;0944 rrvocalicvowelsigngujarati;0AC4 rsuperior;F6F1 rtblock;2590 rturned;0279 rturnedsuperior;02B4 ruhiragana;308B rukatakana;30EB rukatakanahalfwidth;FF99 rupeemarkbengali;09F2 rupeesignbengali;09F3 rupiah;F6DD ruthai;0E24 rvocalicbengali;098B rvocalicdeva;090B rvocalicgujarati;0A8B rvocalicvowelsignbengali;09C3 rvocalicvowelsigndeva;0943 rvocalicvowelsigngujarati;0AC3 s;0073 sabengali;09B8 sacute;015B sacutedotaccent;1E65 sadarabic;0635 sadeva;0938 sadfinalarabic;FEBA sadinitialarabic;FEBB sadmedialarabic;FEBC sagujarati;0AB8 sagurmukhi;0A38 sahiragana;3055 sakatakana;30B5 sakatakanahalfwidth;FF7B sallallahoualayhewasallamarabic;FDFA samekh;05E1 samekhdagesh;FB41 samekhdageshhebrew;FB41 samekhhebrew;05E1 saraaathai;0E32 saraaethai;0E41 saraaimaimalaithai;0E44 saraaimaimuanthai;0E43 saraamthai;0E33 saraathai;0E30 saraethai;0E40 saraiileftthai;F886 saraiithai;0E35 saraileftthai;F885 saraithai;0E34 saraothai;0E42 saraueeleftthai;F888 saraueethai;0E37 saraueleftthai;F887 sarauethai;0E36 sarauthai;0E38 sarauuthai;0E39 sbopomofo;3119 scaron;0161 scarondotaccent;1E67 scedilla;015F schwa;0259 schwacyrillic;04D9 schwadieresiscyrillic;04DB schwahook;025A scircle;24E2 scircumflex;015D scommaaccent;0219 sdotaccent;1E61 sdotbelow;1E63 sdotbelowdotaccent;1E69 seagullbelowcmb;033C second;2033 secondtonechinese;02CA section;00A7 seenarabic;0633 seenfinalarabic;FEB2 seeninitialarabic;FEB3 seenmedialarabic;FEB4 segol;05B6 segol13;05B6 segol1f;05B6 segol2c;05B6 segolhebrew;05B6 segolnarrowhebrew;05B6 segolquarterhebrew;05B6 segoltahebrew;0592 segolwidehebrew;05B6 seharmenian;057D sehiragana;305B sekatakana;30BB sekatakanahalfwidth;FF7E semicolon;003B semicolonarabic;061B semicolonmonospace;FF1B semicolonsmall;FE54 semivoicedmarkkana;309C semivoicedmarkkanahalfwidth;FF9F sentisquare;3322 sentosquare;3323 seven;0037 sevenarabic;0667 sevenbengali;09ED sevencircle;2466 sevencircleinversesansserif;2790 sevendeva;096D seveneighths;215E sevengujarati;0AED sevengurmukhi;0A6D sevenhackarabic;0667 sevenhangzhou;3027 sevenideographicparen;3226 seveninferior;2087 sevenmonospace;FF17 sevenoldstyle;F737 sevenparen;247A sevenperiod;248E sevenpersian;06F7 sevenroman;2176 sevensuperior;2077 seventeencircle;2470 seventeenparen;2484 seventeenperiod;2498 seventhai;0E57 sfthyphen;00AD shaarmenian;0577 shabengali;09B6 shacyrillic;0448 shaddaarabic;0651 shaddadammaarabic;FC61 shaddadammatanarabic;FC5E shaddafathaarabic;FC60 shaddafathatanarabic;0651 064B shaddakasraarabic;FC62 shaddakasratanarabic;FC5F shade;2592 shadedark;2593 shadelight;2591 shademedium;2592 shadeva;0936 shagujarati;0AB6 shagurmukhi;0A36 shalshelethebrew;0593 shbopomofo;3115 shchacyrillic;0449 sheenarabic;0634 sheenfinalarabic;FEB6 sheeninitialarabic;FEB7 sheenmedialarabic;FEB8 sheicoptic;03E3 sheqel;20AA sheqelhebrew;20AA sheva;05B0 sheva115;05B0 sheva15;05B0 sheva22;05B0 sheva2e;05B0 shevahebrew;05B0 shevanarrowhebrew;05B0 shevaquarterhebrew;05B0 shevawidehebrew;05B0 shhacyrillic;04BB shimacoptic;03ED shin;05E9 shindagesh;FB49 shindageshhebrew;FB49 shindageshshindot;FB2C shindageshshindothebrew;FB2C shindageshsindot;FB2D shindageshsindothebrew;FB2D shindothebrew;05C1 shinhebrew;05E9 shinshindot;FB2A shinshindothebrew;FB2A shinsindot;FB2B shinsindothebrew;FB2B shook;0282 sigma;03C3 sigma1;03C2 sigmafinal;03C2 sigmalunatesymbolgreek;03F2 sihiragana;3057 sikatakana;30B7 sikatakanahalfwidth;FF7C siluqhebrew;05BD siluqlefthebrew;05BD similar;223C sindothebrew;05C2 siosacirclekorean;3274 siosaparenkorean;3214 sioscieuckorean;317E sioscirclekorean;3266 sioskiyeokkorean;317A sioskorean;3145 siosnieunkorean;317B siosparenkorean;3206 siospieupkorean;317D siostikeutkorean;317C six;0036 sixarabic;0666 sixbengali;09EC sixcircle;2465 sixcircleinversesansserif;278F sixdeva;096C sixgujarati;0AEC sixgurmukhi;0A6C sixhackarabic;0666 sixhangzhou;3026 sixideographicparen;3225 sixinferior;2086 sixmonospace;FF16 sixoldstyle;F736 sixparen;2479 sixperiod;248D sixpersian;06F6 sixroman;2175 sixsuperior;2076 sixteencircle;246F sixteencurrencydenominatorbengali;09F9 sixteenparen;2483 sixteenperiod;2497 sixthai;0E56 slash;002F slashmonospace;FF0F slong;017F slongdotaccent;1E9B smileface;263A smonospace;FF53 sofpasuqhebrew;05C3 softhyphen;00AD softsigncyrillic;044C sohiragana;305D sokatakana;30BD sokatakanahalfwidth;FF7F soliduslongoverlaycmb;0338 solidusshortoverlaycmb;0337 sorusithai;0E29 sosalathai;0E28 sosothai;0E0B sosuathai;0E2A space;0020 spacehackarabic;0020 spade;2660 spadesuitblack;2660 spadesuitwhite;2664 sparen;24AE squarebelowcmb;033B squarecc;33C4 squarecm;339D squarediagonalcrosshatchfill;25A9 squarehorizontalfill;25A4 squarekg;338F squarekm;339E squarekmcapital;33CE squareln;33D1 squarelog;33D2 squaremg;338E squaremil;33D5 squaremm;339C squaremsquared;33A1 squareorthogonalcrosshatchfill;25A6 squareupperlefttolowerrightfill;25A7 squareupperrighttolowerleftfill;25A8 squareverticalfill;25A5 squarewhitewithsmallblack;25A3 srsquare;33DB ssabengali;09B7 ssadeva;0937 ssagujarati;0AB7 ssangcieuckorean;3149 ssanghieuhkorean;3185 ssangieungkorean;3180 ssangkiyeokkorean;3132 ssangnieunkorean;3165 ssangpieupkorean;3143 ssangsioskorean;3146 ssangtikeutkorean;3138 ssuperior;F6F2 sterling;00A3 sterlingmonospace;FFE1 strokelongoverlaycmb;0336 strokeshortoverlaycmb;0335 subset;2282 subsetnotequal;228A subsetorequal;2286 succeeds;227B suchthat;220B suhiragana;3059 sukatakana;30B9 sukatakanahalfwidth;FF7D sukunarabic;0652 summation;2211 sun;263C superset;2283 supersetnotequal;228B supersetorequal;2287 svsquare;33DC syouwaerasquare;337C t;0074 tabengali;09A4 tackdown;22A4 tackleft;22A3 tadeva;0924 tagujarati;0AA4 tagurmukhi;0A24 taharabic;0637 tahfinalarabic;FEC2 tahinitialarabic;FEC3 tahiragana;305F tahmedialarabic;FEC4 taisyouerasquare;337D takatakana;30BF takatakanahalfwidth;FF80 tatweelarabic;0640 tau;03C4 tav;05EA tavdages;FB4A tavdagesh;FB4A tavdageshhebrew;FB4A tavhebrew;05EA tbar;0167 tbopomofo;310A tcaron;0165 tccurl;02A8 tcedilla;0163 tcheharabic;0686 tchehfinalarabic;FB7B tchehinitialarabic;FB7C tchehmedialarabic;FB7D tchehmeeminitialarabic;FB7C FEE4 tcircle;24E3 tcircumflexbelow;1E71 tcommaaccent;0163 tdieresis;1E97 tdotaccent;1E6B tdotbelow;1E6D tecyrillic;0442 tedescendercyrillic;04AD teharabic;062A tehfinalarabic;FE96 tehhahinitialarabic;FCA2 tehhahisolatedarabic;FC0C tehinitialarabic;FE97 tehiragana;3066 tehjeeminitialarabic;FCA1 tehjeemisolatedarabic;FC0B tehmarbutaarabic;0629 tehmarbutafinalarabic;FE94 tehmedialarabic;FE98 tehmeeminitialarabic;FCA4 tehmeemisolatedarabic;FC0E tehnoonfinalarabic;FC73 tekatakana;30C6 tekatakanahalfwidth;FF83 telephone;2121 telephoneblack;260E telishagedolahebrew;05A0 telishaqetanahebrew;05A9 tencircle;2469 tenideographicparen;3229 tenparen;247D tenperiod;2491 tenroman;2179 tesh;02A7 tet;05D8 tetdagesh;FB38 tetdageshhebrew;FB38 tethebrew;05D8 tetsecyrillic;04B5 tevirhebrew;059B tevirlefthebrew;059B thabengali;09A5 thadeva;0925 thagujarati;0AA5 thagurmukhi;0A25 thalarabic;0630 thalfinalarabic;FEAC thanthakhatlowleftthai;F898 thanthakhatlowrightthai;F897 thanthakhatthai;0E4C thanthakhatupperleftthai;F896 theharabic;062B thehfinalarabic;FE9A thehinitialarabic;FE9B thehmedialarabic;FE9C thereexists;2203 therefore;2234 theta;03B8 theta1;03D1 thetasymbolgreek;03D1 thieuthacirclekorean;3279 thieuthaparenkorean;3219 thieuthcirclekorean;326B thieuthkorean;314C thieuthparenkorean;320B thirteencircle;246C thirteenparen;2480 thirteenperiod;2494 thonangmonthothai;0E11 thook;01AD thophuthaothai;0E12 thorn;00FE thothahanthai;0E17 thothanthai;0E10 thothongthai;0E18 thothungthai;0E16 thousandcyrillic;0482 thousandsseparatorarabic;066C thousandsseparatorpersian;066C three;0033 threearabic;0663 threebengali;09E9 threecircle;2462 threecircleinversesansserif;278C threedeva;0969 threeeighths;215C threegujarati;0AE9 threegurmukhi;0A69 threehackarabic;0663 threehangzhou;3023 threeideographicparen;3222 threeinferior;2083 threemonospace;FF13 threenumeratorbengali;09F6 threeoldstyle;F733 threeparen;2476 threeperiod;248A threepersian;06F3 threequarters;00BE threequartersemdash;F6DE threeroman;2172 threesuperior;00B3 threethai;0E53 thzsquare;3394 tihiragana;3061 tikatakana;30C1 tikatakanahalfwidth;FF81 tikeutacirclekorean;3270 tikeutaparenkorean;3210 tikeutcirclekorean;3262 tikeutkorean;3137 tikeutparenkorean;3202 tilde;02DC tildebelowcmb;0330 tildecmb;0303 tildecomb;0303 tildedoublecmb;0360 tildeoperator;223C tildeoverlaycmb;0334 tildeverticalcmb;033E timescircle;2297 tipehahebrew;0596 tipehalefthebrew;0596 tippigurmukhi;0A70 titlocyrilliccmb;0483 tiwnarmenian;057F tlinebelow;1E6F tmonospace;FF54 toarmenian;0569 tohiragana;3068 tokatakana;30C8 tokatakanahalfwidth;FF84 tonebarextrahighmod;02E5 tonebarextralowmod;02E9 tonebarhighmod;02E6 tonebarlowmod;02E8 tonebarmidmod;02E7 tonefive;01BD tonesix;0185 tonetwo;01A8 tonos;0384 tonsquare;3327 topatakthai;0E0F tortoiseshellbracketleft;3014 tortoiseshellbracketleftsmall;FE5D tortoiseshellbracketleftvertical;FE39 tortoiseshellbracketright;3015 tortoiseshellbracketrightsmall;FE5E tortoiseshellbracketrightvertical;FE3A totaothai;0E15 tpalatalhook;01AB tparen;24AF trademark;2122 trademarksans;F8EA trademarkserif;F6DB tretroflexhook;0288 triagdn;25BC triaglf;25C4 triagrt;25BA triagup;25B2 ts;02A6 tsadi;05E6 tsadidagesh;FB46 tsadidageshhebrew;FB46 tsadihebrew;05E6 tsecyrillic;0446 tsere;05B5 tsere12;05B5 tsere1e;05B5 tsere2b;05B5 tserehebrew;05B5 tserenarrowhebrew;05B5 tserequarterhebrew;05B5 tserewidehebrew;05B5 tshecyrillic;045B tsuperior;F6F3 ttabengali;099F ttadeva;091F ttagujarati;0A9F ttagurmukhi;0A1F tteharabic;0679 ttehfinalarabic;FB67 ttehinitialarabic;FB68 ttehmedialarabic;FB69 tthabengali;09A0 tthadeva;0920 tthagujarati;0AA0 tthagurmukhi;0A20 tturned;0287 tuhiragana;3064 tukatakana;30C4 tukatakanahalfwidth;FF82 tusmallhiragana;3063 tusmallkatakana;30C3 tusmallkatakanahalfwidth;FF6F twelvecircle;246B twelveparen;247F twelveperiod;2493 twelveroman;217B twentycircle;2473 twentyhangzhou;5344 twentyparen;2487 twentyperiod;249B two;0032 twoarabic;0662 twobengali;09E8 twocircle;2461 twocircleinversesansserif;278B twodeva;0968 twodotenleader;2025 twodotleader;2025 twodotleadervertical;FE30 twogujarati;0AE8 twogurmukhi;0A68 twohackarabic;0662 twohangzhou;3022 twoideographicparen;3221 twoinferior;2082 twomonospace;FF12 twonumeratorbengali;09F5 twooldstyle;F732 twoparen;2475 twoperiod;2489 twopersian;06F2 tworoman;2171 twostroke;01BB twosuperior;00B2 twothai;0E52 twothirds;2154 u;0075 uacute;00FA ubar;0289 ubengali;0989 ubopomofo;3128 ubreve;016D ucaron;01D4 ucircle;24E4 ucircumflex;00FB ucircumflexbelow;1E77 ucyrillic;0443 udattadeva;0951 udblacute;0171 udblgrave;0215 udeva;0909 udieresis;00FC udieresisacute;01D8 udieresisbelow;1E73 udieresiscaron;01DA udieresiscyrillic;04F1 udieresisgrave;01DC udieresismacron;01D6 udotbelow;1EE5 ugrave;00F9 ugujarati;0A89 ugurmukhi;0A09 uhiragana;3046 uhookabove;1EE7 uhorn;01B0 uhornacute;1EE9 uhorndotbelow;1EF1 uhorngrave;1EEB uhornhookabove;1EED uhorntilde;1EEF uhungarumlaut;0171 uhungarumlautcyrillic;04F3 uinvertedbreve;0217 ukatakana;30A6 ukatakanahalfwidth;FF73 ukcyrillic;0479 ukorean;315C umacron;016B umacroncyrillic;04EF umacrondieresis;1E7B umatragurmukhi;0A41 umonospace;FF55 underscore;005F underscoredbl;2017 underscoremonospace;FF3F underscorevertical;FE33 underscorewavy;FE4F union;222A universal;2200 uogonek;0173 uparen;24B0 upblock;2580 upperdothebrew;05C4 upsilon;03C5 upsilondieresis;03CB upsilondieresistonos;03B0 upsilonlatin;028A upsilontonos;03CD uptackbelowcmb;031D uptackmod;02D4 uragurmukhi;0A73 uring;016F ushortcyrillic;045E usmallhiragana;3045 usmallkatakana;30A5 usmallkatakanahalfwidth;FF69 ustraightcyrillic;04AF ustraightstrokecyrillic;04B1 utilde;0169 utildeacute;1E79 utildebelow;1E75 uubengali;098A uudeva;090A uugujarati;0A8A uugurmukhi;0A0A uumatragurmukhi;0A42 uuvowelsignbengali;09C2 uuvowelsigndeva;0942 uuvowelsigngujarati;0AC2 uvowelsignbengali;09C1 uvowelsigndeva;0941 uvowelsigngujarati;0AC1 v;0076 vadeva;0935 vagujarati;0AB5 vagurmukhi;0A35 vakatakana;30F7 vav;05D5 vavdagesh;FB35 vavdagesh65;FB35 vavdageshhebrew;FB35 vavhebrew;05D5 vavholam;FB4B vavholamhebrew;FB4B vavvavhebrew;05F0 vavyodhebrew;05F1 vcircle;24E5 vdotbelow;1E7F vecyrillic;0432 veharabic;06A4 vehfinalarabic;FB6B vehinitialarabic;FB6C vehmedialarabic;FB6D vekatakana;30F9 venus;2640 verticalbar;007C verticallineabovecmb;030D verticallinebelowcmb;0329 verticallinelowmod;02CC verticallinemod;02C8 vewarmenian;057E vhook;028B vikatakana;30F8 viramabengali;09CD viramadeva;094D viramagujarati;0ACD visargabengali;0983 visargadeva;0903 visargagujarati;0A83 vmonospace;FF56 voarmenian;0578 voicediterationhiragana;309E voicediterationkatakana;30FE voicedmarkkana;309B voicedmarkkanahalfwidth;FF9E vokatakana;30FA vparen;24B1 vtilde;1E7D vturned;028C vuhiragana;3094 vukatakana;30F4 w;0077 wacute;1E83 waekorean;3159 wahiragana;308F wakatakana;30EF wakatakanahalfwidth;FF9C wakorean;3158 wasmallhiragana;308E wasmallkatakana;30EE wattosquare;3357 wavedash;301C wavyunderscorevertical;FE34 wawarabic;0648 wawfinalarabic;FEEE wawhamzaabovearabic;0624 wawhamzaabovefinalarabic;FE86 wbsquare;33DD wcircle;24E6 wcircumflex;0175 wdieresis;1E85 wdotaccent;1E87 wdotbelow;1E89 wehiragana;3091 weierstrass;2118 wekatakana;30F1 wekorean;315E weokorean;315D wgrave;1E81 whitebullet;25E6 whitecircle;25CB whitecircleinverse;25D9 whitecornerbracketleft;300E whitecornerbracketleftvertical;FE43 whitecornerbracketright;300F whitecornerbracketrightvertical;FE44 whitediamond;25C7 whitediamondcontainingblacksmalldiamond;25C8 whitedownpointingsmalltriangle;25BF whitedownpointingtriangle;25BD whiteleftpointingsmalltriangle;25C3 whiteleftpointingtriangle;25C1 whitelenticularbracketleft;3016 whitelenticularbracketright;3017 whiterightpointingsmalltriangle;25B9 whiterightpointingtriangle;25B7 whitesmallsquare;25AB whitesmilingface;263A whitesquare;25A1 whitestar;2606 whitetelephone;260F whitetortoiseshellbracketleft;3018 whitetortoiseshellbracketright;3019 whiteuppointingsmalltriangle;25B5 whiteuppointingtriangle;25B3 wihiragana;3090 wikatakana;30F0 wikorean;315F wmonospace;FF57 wohiragana;3092 wokatakana;30F2 wokatakanahalfwidth;FF66 won;20A9 wonmonospace;FFE6 wowaenthai;0E27 wparen;24B2 wring;1E98 wsuperior;02B7 wturned;028D wynn;01BF x;0078 xabovecmb;033D xbopomofo;3112 xcircle;24E7 xdieresis;1E8D xdotaccent;1E8B xeharmenian;056D xi;03BE xmonospace;FF58 xparen;24B3 xsuperior;02E3 y;0079 yaadosquare;334E yabengali;09AF yacute;00FD yadeva;092F yaekorean;3152 yagujarati;0AAF yagurmukhi;0A2F yahiragana;3084 yakatakana;30E4 yakatakanahalfwidth;FF94 yakorean;3151 yamakkanthai;0E4E yasmallhiragana;3083 yasmallkatakana;30E3 yasmallkatakanahalfwidth;FF6C yatcyrillic;0463 ycircle;24E8 ycircumflex;0177 ydieresis;00FF ydotaccent;1E8F ydotbelow;1EF5 yeharabic;064A yehbarreearabic;06D2 yehbarreefinalarabic;FBAF yehfinalarabic;FEF2 yehhamzaabovearabic;0626 yehhamzaabovefinalarabic;FE8A yehhamzaaboveinitialarabic;FE8B yehhamzaabovemedialarabic;FE8C yehinitialarabic;FEF3 yehmedialarabic;FEF4 yehmeeminitialarabic;FCDD yehmeemisolatedarabic;FC58 yehnoonfinalarabic;FC94 yehthreedotsbelowarabic;06D1 yekorean;3156 yen;00A5 yenmonospace;FFE5 yeokorean;3155 yeorinhieuhkorean;3186 yerahbenyomohebrew;05AA yerahbenyomolefthebrew;05AA yericyrillic;044B yerudieresiscyrillic;04F9 yesieungkorean;3181 yesieungpansioskorean;3183 yesieungsioskorean;3182 yetivhebrew;059A ygrave;1EF3 yhook;01B4 yhookabove;1EF7 yiarmenian;0575 yicyrillic;0457 yikorean;3162 yinyang;262F yiwnarmenian;0582 ymonospace;FF59 yod;05D9 yoddagesh;FB39 yoddageshhebrew;FB39 yodhebrew;05D9 yodyodhebrew;05F2 yodyodpatahhebrew;FB1F yohiragana;3088 yoikorean;3189 yokatakana;30E8 yokatakanahalfwidth;FF96 yokorean;315B yosmallhiragana;3087 yosmallkatakana;30E7 yosmallkatakanahalfwidth;FF6E yotgreek;03F3 yoyaekorean;3188 yoyakorean;3187 yoyakthai;0E22 yoyingthai;0E0D yparen;24B4 ypogegrammeni;037A ypogegrammenigreekcmb;0345 yr;01A6 yring;1E99 ysuperior;02B8 ytilde;1EF9 yturned;028E yuhiragana;3086 yuikorean;318C yukatakana;30E6 yukatakanahalfwidth;FF95 yukorean;3160 yusbigcyrillic;046B yusbigiotifiedcyrillic;046D yuslittlecyrillic;0467 yuslittleiotifiedcyrillic;0469 yusmallhiragana;3085 yusmallkatakana;30E5 yusmallkatakanahalfwidth;FF6D yuyekorean;318B yuyeokorean;318A yyabengali;09DF yyadeva;095F z;007A zaarmenian;0566 zacute;017A zadeva;095B zagurmukhi;0A5B zaharabic;0638 zahfinalarabic;FEC6 zahinitialarabic;FEC7 zahiragana;3056 zahmedialarabic;FEC8 zainarabic;0632 zainfinalarabic;FEB0 zakatakana;30B6 zaqefgadolhebrew;0595 zaqefqatanhebrew;0594 zarqahebrew;0598 zayin;05D6 zayindagesh;FB36 zayindageshhebrew;FB36 zayinhebrew;05D6 zbopomofo;3117 zcaron;017E zcircle;24E9 zcircumflex;1E91 zcurl;0291 zdot;017C zdotaccent;017C zdotbelow;1E93 zecyrillic;0437 zedescendercyrillic;0499 zedieresiscyrillic;04DF zehiragana;305C zekatakana;30BC zero;0030 zeroarabic;0660 zerobengali;09E6 zerodeva;0966 zerogujarati;0AE6 zerogurmukhi;0A66 zerohackarabic;0660 zeroinferior;2080 zeromonospace;FF10 zerooldstyle;F730 zeropersian;06F0 zerosuperior;2070 zerothai;0E50 zerowidthjoiner;FEFF zerowidthnonjoiner;200C zerowidthspace;200B zeta;03B6 zhbopomofo;3113 zhearmenian;056A zhebrevecyrillic;04C2 zhecyrillic;0436 zhedescendercyrillic;0497 zhedieresiscyrillic;04DD zihiragana;3058 zikatakana;30B8 zinorhebrew;05AE zlinebelow;1E95 zmonospace;FF5A zohiragana;305E zokatakana;30BE zparen;24B5 zretroflexhook;0290 zstroke;01B6 zuhiragana;305A zukatakana;30BA #END sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/glyphlist/zapfdingbats.txt000066400000000000000000000075231320103431700312360ustar00rootroot00000000000000# ----------------------------------------------------------- # Copyright 2002, 2010 Adobe Systems Incorporated. # All rights reserved. # # Redistribution and use in source and binary forms, with or # without modification, are permitted provided that the # following conditions are met: # # Redistributions of source code must retain the above # copyright notice, this list of conditions and the following # disclaimer. # # Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials # provided with the distribution. # # Neither the name of Adobe Systems Incorporated nor the names # of its contributors may be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------- # Name: ITC Zapf Dingbats Glyph List # Table version: 2.0 # Date: September 20, 2002 # URL: http://sourceforge.net/adobe/aglfn/ # # Format: two semicolon-delimited fields: # (1) glyph name--upper/lowercase letters and digits # (2) Unicode scalar value--four uppercase hexadecimal digits # a100;275E a101;2761 a102;2762 a103;2763 a104;2764 a105;2710 a106;2765 a107;2766 a108;2767 a109;2660 a10;2721 a110;2665 a111;2666 a112;2663 a117;2709 a118;2708 a119;2707 a11;261B a120;2460 a121;2461 a122;2462 a123;2463 a124;2464 a125;2465 a126;2466 a127;2467 a128;2468 a129;2469 a12;261E a130;2776 a131;2777 a132;2778 a133;2779 a134;277A a135;277B a136;277C a137;277D a138;277E a139;277F a13;270C a140;2780 a141;2781 a142;2782 a143;2783 a144;2784 a145;2785 a146;2786 a147;2787 a148;2788 a149;2789 a14;270D a150;278A a151;278B a152;278C a153;278D a154;278E a155;278F a156;2790 a157;2791 a158;2792 a159;2793 a15;270E a160;2794 a161;2192 a162;27A3 a163;2194 a164;2195 a165;2799 a166;279B a167;279C a168;279D a169;279E a16;270F a170;279F a171;27A0 a172;27A1 a173;27A2 a174;27A4 a175;27A5 a176;27A6 a177;27A7 a178;27A8 a179;27A9 a17;2711 a180;27AB a181;27AD a182;27AF a183;27B2 a184;27B3 a185;27B5 a186;27B8 a187;27BA a188;27BB a189;27BC a18;2712 a190;27BD a191;27BE a192;279A a193;27AA a194;27B6 a195;27B9 a196;2798 a197;27B4 a198;27B7 a199;27AC a19;2713 a1;2701 a200;27AE a201;27B1 a202;2703 a203;2750 a204;2752 a205;276E a206;2770 a20;2714 a21;2715 a22;2716 a23;2717 a24;2718 a25;2719 a26;271A a27;271B a28;271C a29;2722 a2;2702 a30;2723 a31;2724 a32;2725 a33;2726 a34;2727 a35;2605 a36;2729 a37;272A a38;272B a39;272C a3;2704 a40;272D a41;272E a42;272F a43;2730 a44;2731 a45;2732 a46;2733 a47;2734 a48;2735 a49;2736 a4;260E a50;2737 a51;2738 a52;2739 a53;273A a54;273B a55;273C a56;273D a57;273E a58;273F a59;2740 a5;2706 a60;2741 a61;2742 a62;2743 a63;2744 a64;2745 a65;2746 a66;2747 a67;2748 a68;2749 a69;274A a6;271D a70;274B a71;25CF a72;274D a73;25A0 a74;274F a75;2751 a76;25B2 a77;25BC a78;25C6 a79;2756 a7;271E a81;25D7 a82;2758 a83;2759 a84;275A a85;276F a86;2771 a87;2772 a88;2773 a89;2768 a8;271F a90;2769 a91;276C a92;276D a93;276A a94;276B a95;2774 a96;2775 a97;275B a98;275C a99;275D a9;2720 space;0020 #END sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/icc/000077500000000000000000000000001320103431700245315ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/icc/ISOcoated_v2_300_bas.icc000066400000000000000000040115601320103431700306500ustar00rootroot00000000000000pAPPLprtrCMYKLab  1acspAPPL-bICC desc8cprtL wtptXbkptlA2B0e\A2B1je\A2B28e\B2A05qJB2A1qJB2A2 ,gamt targ8rdesc ISO Coated v2 300% (basICColor)textbasICColor CMYKick v1.2 - Copyright (c) 2006-2007 Color Solutions, All Rights Reserved. http://www.freedesktop.org/wiki/OpenIcc/ProfilePackages/ The zlib/libpng License Copyright (c) 2007-2010, basICColor GmbH This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, and acknowlegement in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. XYZ DMXYZ Jqmft2   )2 9"=$?&@(?*>,=.:08253456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~RNK􆞇H񈛉EB쌗A뎖@됖B풙E򔟕MZ mjik¡nţqɥu!ͧy%ѩ})ի-ح0ۯ2ݱ3ݳ:鵘FNRXdq|)ă/Ƅ-}#m˳T'Α_"Հ6ٍ5݇#oIZ{24"o ]  $, 2"6$8&8(7*5,3.202223456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~G삒8߄.׆)ӈ}(ӊ*׌0ݎ7吓AJT_mfb b dgj§nƩrʫv"ϭ{'ӯ+ر0ܳ2ܵ0ڷ0ܹ5㻒B򽢾Rds",Ƅ/ȁ(r˶V+ΡсQճlgݴGPb(AA(r \  !#%')+. 02*3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~G삒8ބ-Ն}&ψy#͊x#Όz&Ҏ~+ؐ3<ꔙHWl]TRTY_ elƫs ͭz&ӯ,ر1ݳ6㵏;緑<湐;滒?콛JWdnsqg ˰S'ΎVҶp"x r`DJY a a[V   !"#$%&'()*+,-./01 2)334,5&6#7!8!9!:#;%<(=+>/?2@6A9B;C>D@EAFCGDHFIGJIKJLLMJNEO@P;Q7R3S/T,U)V'W%X$Y$Z$[$\&](^+_/`4a:b@cHdQeZffg]hViOjJkFlCmBnAoApAqCrEsHtKuOvSwXx]ybzg{l|q}u~z~}xrmga\VQMIFDCCDFINT[do{|tojfdbbbdfilqu{ÜěŜƞǡȦɬʴ˿ζϮЩѥңӢԢգ֥רث٭ڰ۲ܵݸ޻߾ƀ̦%2C€Ruˀe[T{<*Ԁ&.zr uU_-QuBĂZځ;끕= E?v~놾 ~ڷ QBOXxt}ZV;‚[~gK~RD~Q%F~W>~eA~z"~s~݇X:;GT*y}v+Q}p[}pmg}{8r}N}-}>r{~1>X~~39~;~…|.|ys|gŊ|œs|ՙ}ĉ}7qY}W}+8~x}|2D|'#||ҰK|+z|K|Co|јU} 88}G} G|'M|H҈{߽{θ{γ [{w9|n#|_T|7||ŗg|w{|L.E|(a{P{Ƕ{w{غ{l|jS|@d6|f||ke|<ϋ|G{"*{×{M{鸐l{3S{|6|(@{ʏ|߭|M|x {ߪ{'{k{k{~Sa{76{̡6{QJU~l~ ~6Am~'psT'Y7݀9:f酂\%XdĂ|yZ܊ r끪X:)xuH{@i蜁:|ƏQہ|̀R?ʃr(W 9_^u9فրɍI倏Oav0q+ jWG83փϙ"fԟcۓIHxdp dVe8COT%á{*:@W6~~Ɠ?n~ǐU>~ʍ7~OH~[zk+w<~ݬ~{m~Su~*}7~jm[~FT~ .7}ᐩK}b*Ɯ1R~;u~:3}籙}7Z}k} R}6b}\$C|Ōؒ.,}~Cd~;٨5}޿E$}}ji}SR}-5|bO|[2!XAڳ~~;$b}b}ƀ }oGi})CQ|5|}|NڶH#M~!~+ɦ}*}mh}:Yia|곍Q|s5|fE{Y}߅}܅D}È}ܱ !~c~9%pV8GāLs#ma¸:^ݜK ΁.oS5VEہ88slFVF*򈅉o"ޅ:ńUS͜僋x`od U8Fm$ք.r{rpV嚺%񅳁njtTʁ$7FЅQPߵ`Ϥ[^\\wI% mPًBS퀐|69h腐&rݽ΂wؚ:^倔l;DSa62l^꿂㲋dYMԫL("S>k9oߛjQ85~̐p}~ ;t؂6X灒vԵQ;[Ъ2vi,5P~4~D}|F$9݂b܁*',%}Jg~ЩO~_4g}} ?eNށs> ѵ$X#98|/g@~QO~%B4B}$|ރZ(Ճdߚn|ѯO>u|w~f~eO|}Ӫ4J}O%|D,T|/te|Yl|~ĉ||ѕX}[}j~Q̈́34 уx7k =ءՀ Ɏ߸aʀe iDQB|4z΀ {q~xqɋJȭi4覮0 8[ІFDipZPm4 9S{*5jk _4϶ֈBP]ˆt~hrÆ O݃3x9y ̃)2%Իٔ)Ō_юܑCV}\g\97Od2j aҲcù6M>Wta|fUNXΌ2 CQУūE񆘤p܍Yz7d3MJ22 0D!x䈦OZĮGG3]hxlcmL21_N =]چ˗݇jB7093:v܃Gb&BKBE0ԀJ L8ٻ+YEӊp͎ֆA6څ$''v6&oaJ 0 t~΍"Uʥlk`5/͡AƥuhaGװJĩC0O ~W Q8zxD{=ؑp{l{%K{f|y}!=Ȗ,މUx-[¯2%ڹ1y(Qj͗o[qMJ7!Iԫاͮ _ۓx+IϾx{j7GZJ#ު$7_Ǣ! +* z"ߴzPz{~vzq]{MdK{UM4|D}c2_B~F*WA^|mI|튨L|~R9}qQ#}Mc}U ~D~2&2@ 92w}mq|czTҟD€1 vڕQ{eMZىg }`^pkTbQT_JDBb1JkQdhȌ>ˮ̋|ɊoʼnbEȈSSʞʇC1ZC`1h0@h{?n=iaC+S1G C6#1K ˓݉6ۆzl(m`RFBc0}ӍȅzhĐNTy;^lx!G_VmQDA)0<Eĵq K6;wRk֪x^ æP$w@ǘ|_/uCwNDDv?qN3,vji]vO}8@cG/3J,Fٵu?CNvVϹiऄ\]O>8I@/9ەӈ.{'{xy{mG{a|UUT|H }9A"~i(bj〛߀n w~Gv~x0}m }ag~U'~sG~9*(=Hˁ0̀~w>ElηUa5dT~9GΨ9À(kɌ"Ał&[wGlf`ϱĄQTބG8٣7'.Rı'w 犸k`m,TEGGG8b8d'ޞ?f!܋4܁/$,vQky`S櫲Fʋ8h'˝s+?EրC]uj_1S.ГFW[8'p@rwީGMcti^C'RZ-E97E'`dXV~~?xs h]IoQD 6'$hאRR}fs~Wh$w\ԮgJQjDDi6ğnY'ɛ7zo) }?8rP=g=}\u P̩EDl6d'a蒝P|zsǥ|p$w|ek6|ZL}N}B_~43Ϩ' #ȀQ/D̃VzJOo'~e>~Z-~Nsd A%3# ,)zoс e ZQNPAŬځ3k#zpjǁpYgy,oh dޅ$YN gwA(d3n#a˄*a+ÍoysŌ3o'`d7Y0MٰĈAd3wk#Z!M)+y*$n࿼dB'YH`M A;ތ3W#\( $0]xjGn%Й7c4XebM"@ԩגp3#b񜛋\مlqȚ6wZgmSbøW⳧Ls^h@R2ţ՘#C#~9v߯lb`fW" rKҭ?רio2s)#}=,|w,|⢰0|}k8}-1RT}WY5}\U}<}9æ||̸|˦|u|z|Fi|Q&|֚5|ϖT|{ }km׷}Ϝǯ|ɨ|Æ|d<7|Z~-|g;hG|fRPC|o4|gZ|E}u}"׽|[|ʻ|Yr|E}|Kg|9 O|-4_|( }{}{:}-|Q3|qHx|7||}|gK{SO{Ѫ4e{Š{6 ﯄{LA{҃{ҿكD|ł|[f}."}nE~^Tށc~7S`ׄ5߶t +V,m2"māTj5G6Հ|탟݃$p):U|=$@s m7AN芁ك l뀯͹(BTe㔬gBjdQXG4x^~؆ ȯ`V^ƶ%-~~~ʚ=h~PE~4A~E}׉luzԈ#]+/~îC~R|~[=g~/O ~!3}S}/Uv?OǝO~~ad{~(e}gN9}73}P<|WѦK[1uʠ~$~Ifzm~ ne)}M}a`2}x|gfGܙb?{~}'Y~y}Ϲed}kcM}2|,{㎪ȋyQz+Ƌ\z3zzդև{?r{~r|ha}O ~O3iv 5}Ԃ}}o }D}Ɔb~c}ׅ~gׄ6Os2Z Y!%ӭmoZ݀ }=CgMNˁ279 y0z~C΢FR|2fNNTM2ǃ 邂[КR#zi=rzeK[3MU1' ńreΡoοxㅥ=߄.ydGύL 14 5ڔcf©(ϥ孁)(tw҂͘b,K\0m f򇧽5ɺɻ-iūd v])ak}Jߙ 0! vC⇶ȆÚ'G!ٲtv`"MI /' ~ÌI솦иL闼easс_ PI?L,/jv8 ~nfLݥ9ǷgrѮh;"nċڂs[_?ׯIV/j })xwgyyzRy zm{.v|a|Jl^}.d~ EZ|{ȴ|TW|ٍ|њ`} 6}}v%}a0~xII~.[ T՝vjF㙵>ψfu`K@Ixc.81 4thи *Ն;f4>t_‡ĄH-U #XTێ揾Y<2닦鉧sfkb^҇2.H -jL L8ŏ֚b4eVD5[5]rA莆]܆Ghu-u p~_6ݍ̦gxw邲np6\Fq, n1|Q'?`Oňynˠ8[>Em<,  ҁ֏`>hu7/z6Js:~߯mOGY7Dl+w ށ'l%M6ƋU鎠.~9ǵQloiY_ vD+Hi Ҍ:DoAƍҹ1}l!68XʭCσf+B VWT(wZxCx1y?yր/znd{Z|`DU}8)~L ȅ{f{$n{ßo|w|T|mݎ}[Z}C~m)*dyǪD 5W~˔~K0~ Am[TbYzC)hCG KɹR$ vuڎV}鏳lZ̃XFBO)@cΘɎƪŒ7֋W3|Ώ=kc?QWlGBL^(ˈt4T˶w&N(,=P[{jfNVڋAÉ$(ĉ6؄u^ӑyۘh `U)@߈g(0뎿$·ӗrnN_UӇΏXx(y)gQVTy?'U6u@oVVHvaSegS\w>凞'dDz}Y{ّZ߅Du?Ae HRʉP>|nf&Ӆ]% ҕ͂:{=MµV)tWdR_>6#&ɅLES]wF\;w̛ɢAx=`Pxƀ%dyoqIyz7a!{O#| :ː|"1S~d5~szzћ1z؟{F#{pݙC|3``|Nȓ}r:~~!J~O(Bѧ~ }}N}3~pj~W`[&~NpU :6f!ȏ'Kñ ȄO!{~@Kov_Mɒق9I_!u~r%ף0mYw:x}Dn^ЕM!JW9N!oǂ2=ˑp|>"`m[^Lsq8W!]=!D+m;dW(zylE|\“GK 81V.!U p(ۯQáެȆfyjG[oxJsx7bו ZN`rc՛*wNi1Z /IOA6| (F V v߲thYsHАc6)IM݆,ʞt*aWҼu蘽g²Xͮ4HsѨ5X ԞW\wɓ{zx1x{yo yaңz~Sb {[CE;|\0ᗶ}d~dz#zهZ{{~{RnҦ{a|ES |C }0~bǕKo}}:}{90}nY}aE~=R螳~B՚0uZ>Æ^;zm`uRo"oBq_d0;t/4Q~É#yèLm0+`bQܝBɆ/Pb!pi'Ǭ7qxjln_]֋QEA1/@Ǔ{RILqwhk>\^LP^P@i/RώqvgתT'S 9vHTim]$O]љ{@#.=iNؖ)߳} tΧXh[DRNEk#?1yC.ɚ=!5{v׹Ītg[R9Mś5>͗B-ޓ*)˔EUtⲱ5]ÿ)$ڻ sѶgsƲZݞڮMh>-16S&+jyFyuyj.z$_*ozS/{mF?|J7X}O&]~uz{9{uV{jj|(_)|Sw}F}7+~p&ٟ<TxX$IÎ~~u9~\j1~W^Ѱ~Rݬ-~E}@7t&ğzxODUrɄv~tXiĴ^n4REՂ7JY&hEo~ekt׈iP8^ _R.Eh67[&țӂmJ}ۼƏs2hҳX]LQЪCeE6&׊H߃~|rfgﲾ\ϮQ]xD"6~ &j̏S6iݘN1E!yR Y}bostcih__xmTűbIRS> ;0;"[ ߇+|*s87iSDR_5,TpDIw=ۦU0ס"Ol|<麦rֿ+h^氞T1IE=0̡u"ؙt y]{pL{fW>{\{Q|#F|:ðO}x-g-~l* Q퀓y~6p}fC}\ }Qz}F~@~*:~-`x ZP̀yhΣpɌ^f)[XQuFl:7-Y񦱁 ]yՅoȿ5eðy[QIZFG!}:僒-X섆 y oeቑ[ȈQ>F;_:!F-W)I š‚xKoN/He}[xQ/یF-:zV9-YK: ؇xHEn)e 6ZiPEȱx}:8-y-CL M%X7w~6nK[d4(ZkP֛EJ9ܫ?-"W ӝ;M9w&ɯemİ[drY]ODС9m,P}# ) vɅ|ma/cÿ=YOWxDr9}.,❊6o _(&v8m5 cSYwIO*PD9uO,^ϟi C5l|z9|z֜|z}#{7x}f{y}|~ }9q0~c}WH~~h8~~{~| ~վ|T~A|~~|~}d q}Vp~V~8^~{D2{{˂lb|+|1|聑}Up }ЁV~IX7~#8azHzz{VԯG{ٛ| |bo } U }7.~3~͂ty[>y z$z~NzᎡ,{N%{̋m|jT=} Y6}~vx̟>y"}6yݾyƘfzz{:l{Sh|ha6|}Le[wxJ̨xLx𥢩yQsyȞCzT{kDzR9{5h||]w¾7w;x1x{=x߯Ryܤiz{pQ{=4{*{‹nwԠ xx$xvx˼dy3X~FyhFz+Pzz4${r{Ow#)xx ВxpAx[yo}y@gyOze3zϞz_w։xD_xzexUОxjx}y\?gJyOz44 zcVzry{yhzYjzxd{$r|r|n}U~57k~2 }~}~}~}⭩~~O~~Y ~n*;@TV6~m4~j ~]~o<~~~'m~&T6hW _}o}u+}x@} O}*4}نy~l~I(S*~j5~Ӄ 'z@|T5|l||ȏo|ݖ} sB}E k}R^}5M~-~lvc{M{;ʫ||b|-D|^؀|j|GQ}N4}#}[{4{9a{?߷g{J^{`ғ({L{יh|/P|n4L|hU|BzBzػŢzзC zٲz쭹{}9{`gr{OS{3|`|{)z̝uzzwzӻ-z{{/f{^Nl{3{`{/{ڞ-~thPC3 C'ZG' ~טt~\ ~n"~_ }~^g~jO~y3k~p m~d='~"Ҝ~WH~n}ԣ}}L{}fg}aN{}2}8 }bވ~Si}}{}l}5N}#z#}%d}(MX}+52A} |>_~pϬ~ c}}UL}x|x}|oc|էLm|=1|} |<.۔~|~Y}u}IO}|ܹww|ɲb|L|~_1|L {~}ߏw~ ل5}@}/̮|X|wW|b|V K|1{-;{[wG wˆxxxyFynz}{˄{rf|lN e}X1ۂ~M ~X{X?2{χ<{۱| |V-|{'}e~MC~1`G ){ݏlre@|<qz2eM &1 w DՇXцep*㞯BytbdLAd0* (S⏇Ņ"2򄂌֝+aexSމEc%`K0/ GT5hjlw^RSVQϐnwRb%ΌJށS/πU )ճk:b` LuO+`ā I퀆/i {.GT]j}+,(ܭڗ),})s _KRH׀&.l ~Ɋif_s"F;@gq۫^<]G.<~f ~B>kpr̴~Iƒ}GqPR]| Gg/.~p }VgIeԱBgޣȺUpڀo,] BGQ L.~M %}c(ҮvHVvDw|x؋xنĊ?yt,z_{H|-k}9~xz!Ĝzoz\b{0+{q(| s|_ }cH~-~Z,#H~!ێ~č>~~~0~{ss~^Z+GY|,DϧdžKFY54_1VrуU]̂Fڂ,U&GxÌҍ~ŋKĊ׉eM p<\ņ3FR=3, }GVwտG =5KYuʐׇo׆=[ՅE+Ã؈Ìb׊{@ Z)bn+Z蒿Dă+KaS|ALmy6Èǎ?}lvY2rCÃa*aI 6l48%sW"󛿈ѸY|:djGW"B *& #"O>s qƾz {zG~j? WY0B[΢) DKÖfϽH.ĒUzi„ಙVuB x)D@ ɌuVV v9vŜٓ(wSxL}z{y?lzLX{{PBI|O(=}y~s8xyf}yԜ.z@z|8{fk~n| X|B'0}(5.~Z~b|ͶĖ>|ީM} ~}!J}R|W}j.~"W~AЊ~(~iCO[xcn,Ѓ {5VijV5A/ '$2Ԋcz$i Uk<@'2ن*[ͥ,-t~Îy ph{ UԊ@"TC'S':X\ѕ! Yt2ӛwHlfɔS ?CE&F|QX-61bBa΍u|d[RT>K.&^Rwab(0/6KבmlU썑sϫcszQg`=H%@ $ūP0:YE_s{bʉgPۈ1s<݆f%<ȕ j&ʜ1ŝޏFXn\rebGPsꪦ<6%sl1u9u[dvOEw }ZwnȘ&x^iyM1z9E| Ǝ}C~xpxWxϠyƝRy|⚜znZz^5{Lڒ|b8ِ,}. ~~ŮԥJ{ԣok{;|<|2|fO|m闣|^)}uL\}8~ o/{JƤ_[c'0X{mm z]ffK؁y8+ J"^RR!zvl&rc\ׅKEEN7 +s6B%,ˠgΓٝqytxk;⋢[ՓLJi7YI~ et*؜'HuKwڗǖiȕ.ZI6TۋKiQ󟎩"ᦓgv2^@hC9YA$AH|\5܌p DGq*I t,f~ WգLGx&4銡 Vʖ-Kj}V#un~)a}AʭqGfQYLE =s^,8ވm󺚇cO|Tq1Kr`<ΕϤe,4LA*Yv|WwUrwgӴbxO\y PWyD>z5t|#%a}g#H~(uy|myr]ygzV\xzPǫ{D |T5ԢA}E%N~H-e |A|1|Wr%!|egw|\K>|P}PCK}5~%A_q7ՀSր C{΁qk_g([ꮏPJ4Cr5^%-?f ,Zk"{uqfM3[ޅOgqCtI5g;%#PA̾zfWpdf[2OC&Da55%ښn__ ySo-e'“ZHJ[N٧АBT4՞% .&޽Rxnsd/"YWN!A㢣4U=$Ú@O.6&8wvsm4c-Xg"M1YA5=3ѝV${Tŏb}5v_mb{W䨒LҦ_@٣3_$bD8^QKf~0vx%lƳcbQ6#WL @ 3{ $eϚ}Bwt .xCjPwx_yUPyJ% z~>>{|0q| } mga}!džztszj6z_]zU9{cJ|> |0l`} ~ ʠ2B|}sc}j} _ù}U9}MIn}=~U0b d ܟ〴O|p[sR0i_uET~hIĮ=Щ0\b ME|ygr·i6+_)~aTI=(0G Ɇ# U_d{Ĺrj hd^϶TnI_5=g0/ˉN dq{嘆qۿ.h"{%^&œS±HϬ/=\/B ez%( qzgq̞z]`0R8!H%fU;!yLpEutɐ(kĸbQXN*C#J8+[h" =tȔ}kùaݕX<}N, C8B+#բp {\t΢ji`V!L1B[7yu+NoWP s/,yOt#yuͶzvUzwzw՗{_x9{z lW|{1S}&|:5}} c~}>kxy Vxy{yuy"yzTM.y-#1yϔ zFIs='ΥsVtXÃtu/vAYxwRcw LBxB1(yR z$VYsGֈ͹s߾ct\ t ur0v%Gxvc wKxid1y yˍ ۦsJstOͭgtM[uJčuwwvbw]Kw1 x y}T ~ yyԗyх<ţz&az|!'z܃{B[}{g|HO9|݂g2}U ~xc!xǍy2ytyHsz[B|z;f{*No|(2;| }Itw*wB+xfHxO ycy{lz)ezԋM{w1{` |vΞwww6w̠x0xyyOdSyLzq1]{ `{lv8vWLvKXwIwx.0wxƠbynKz0zc\ sz݊5vFvƸvw'0jwwvx8a{yEJy02yD xz^6%vJvΊv/wwo wܶu\xf{`xߩJOyO0 y zevG>4vՂLvϏ|vWwB߇w4tx"I`xJ-x$0y yr0Ast=~ui:~v.~w~x;}&~yvgF~zN {2.| }4~w~x@k~uxճb~gyMJ~oy-~uz|n~{f~|pNi~}?1 } a~~.L~{3}|!“}|i}|n}|ڏ{~ }X{~6}f~a~XM~~1~1 YH|[|]||߃`} '}3z}le%}[M~1~? ~À({{{Ưo{񊝞||My|d=|Lo}L0} @}zzx{8;{1Y({L{xf{c>|E`K|0b|؈6 _}?ZyۧQɍzzDޫkzGݚzm݉uzv{ a{nJ{Ɛ /{데 |8yyyyǬwy>iz.tz`]zSI{7,/c{I {Ҽy|Yyą\yylyys%z@_zeHzğ.zʙ {ѷyzay~,lyɦ y9yiyӵqrwzK^|zOH^z}(.z| zyr+ylycFVye_&yu(0yqyӴ^&y&H9zp.zf izÅrn= stqatsuWnvU@wv[xauzJ{`.́| s}sօv^fww1sxO҃ xyu8z`{I|.Ax} ~ zuƆzָf{+U{o{·Ԃ?|^u}`h}I!e~.-B~ׁg~@Nۂ0_^Ćat_HqЁx-|{ـsåڵrx `݈ lGsf^?Gـ!-? 0i5 ^d DŽ$Iq]XGCt-F QzZ>G0|ٟ"gKp~R\b~ˑFU~*,~TH ~@ɢN߰ԯpG*p~~ϥ~nR~sZ~DEB~),}y }>yBĽ`Pܞ5~~~V~Tl~#Y}᢬DK}+k}< }ƵL̫*Fe~׽~v"}~-l*}Y!}7C}_+9| &|%PӾ5Χ.X~çF~D}}k}X}T_C|+5|y t|׌qrs鉛tOu䇥vnx3[yDz*Y|+}ALuR46v ʊ.v31wi?x,JNyntezZ{#DC|)x}j} y;Xyz tzhz{m|6Z#O|C})B~@h~ƨWۉtĀӝ† ~p4lمeYLCJ)W=?Y$ T^?}NvkڄnX{ BuS)ÂYҒp?M|#ˋjքWmIB ψ(ڂOQʈ%(O6ۧ]y􄿗z5)i*SVWnAA:(^]2iޱԆ֭mzӄKxBdgsڛTS@Dk''TuˆļE׷p vB@eS?D'a:Td/ᆲeӅʾչЅ6uۃeVQS-w>Ġ',$p}·)ѡ&<9uQٴd/R΁5>ex'!tk~{qpưRqyrt]fu$wvdgNwT+y>䈤za%w{]}0HtVu =uqvŽ ww[xf ySz>{%)|džL}x ےBxy-y zvȌ2zf{SVs|N>5P}$}"~5GLB}q]V`ٌfueR=À($"߷ȑLJOx'у%u|CpX}͠w8)wwx|xv yGhz YuzH<{5,P|s}^,~3B~M~.~Ș~!uh~FgҔ~X̑~HA4ЍjԀƈNՅX=tfluX-HGm4m \9#"I ʌg虊tFmsyfʈWKFҎa04t]Cld>ϖT~喆wr?dV ̏KEۍ3go#AˆT;f}TpmÜcTLfD2 'OYۜԴВz[6@{lتnܓz$a4SyEC'1( <˻~Țu󆕘&?zan,Vxa RC7|1U_|2J?|2_zm t`ȭZRuhBߌ(14gLj¯IpЫq~wrs*ssg3 tZv#Mw=Hx+ӕzaL{=}_sxtc~!Yu/r֥ufvZnwLۛ:y =|z8+a{^O|a ~#Wvw:}§wrvxdfpyZ+yLz=J{+$|Ta}s~խ}# J}F|ħ0}\q}|eҠ}Yyv~L <~<*+k_3T%M{lIpѣ7eXԜȂK|<}+=ӂăN ʫ͊OzodWTX#!xJ<ֆ+ \φ>qKNyanc-y[WG J;t*,(@̪_y!j{" [|~~/tuk{uBl rua_vW@EwuL*xt?y2,Bz!1| u}_~~wuu@ wkܴ xUaxWyuKzA?ף{,2|+!}- I~Ou(}.}Qtѷ-}RkQ>}OaC7}rV'}K~*?~1;R!+ Țlŀ2|׺X+t'UjӲ``Ю`VSXfKHHk?Q<1Ǟq!QR e+o|1s:jH`QU䩒VJ祁?u1!sSR{mtriDc__U J)>l{ 19|!ޙN{䈱ʄ-zOqh/^ZTܗ+IS͕= 0ߓ7!tJŕ΋Iڅ\y@epvcg5]gumS4]HM=?30=P!SʕAǔsKx9hpGf¯T\=7RΧH3 o<Ϟ0[!5lϔk\x }oyfa~\Rͩ Gjd dtO\l-%c YOBD߂9,}YL !tTokӻ3 bȶnY3O=hKD9d,lVw >ՅJsl^k -aXpNWD$8,S} ^ Q*rj]a!WؘMì{Cv 8g,*} | mƅ>q!icϥ`TnVݰJ MBߧ:7(+`C "Yquŭeh__,VLīuB;7۞+ZՕR -)YqhRI_VGL=Bs٤7ub+%uCx enit eìt\ǿNu$S_uI$v?`w4Lx'zZ{}!?nyǟve?v\׾wASj\wIx\?i0y94Wz;'{c|w~@>n1y$eyV\޾xySmyI^z=?lz4^B{'ɨ#|ť"}Ϡ~nZ?~[e~6\~SY~I~I?i~4fd-'ۧD7Հtm1eރ\جڍ4A'֤){ +) lw#dΛ)[sVQֵHJ#>N3P'x މlgcZ΢gQTT?G˯Ǟ]=53Y'J Șыmk&cL֩fZ]|QGi=נ3>C'♤˞ G(QklްXc Z+!P볘GmN=hr3:ҟ'R/_ 6φ=uro(]v'p\Cvq wtrxsxul|yvfzhxzN{My1|{" }H{}tsjҐuTtIÌvu#lvuFwvxDw|Dyyfpz zHN/{{Z1{|5 } |Hswѷtx\uPx岧vydvymwz{x{|ey|AMz|17{}s w|}85r*s!ttl ùUv_zwndxԀlLyU0zЀ7 C|HBJpԊq׉rӈKsΞ}tuQyYvЅcwLy#03zL X{b#oϔpPqOrs,AtՌxu/bw;KPxc&/yD nzƃr~nto躹pAqrÉ.s֗vNu 4a\vMXJ0w~/+xP- y܆DԜmoDpIqZ^r[$s9Kttp_uIv.xw yGҾmİnOoнlpĸhKqų?7rrt^uEHva-w# xЊmAnoĵp4qqlrrs᭴^uGv-vΚ xxmq!nro2puOqc.roqs`]tGu5-vMr wwxmn΄xoy!q$yr\Jys|zfu-zzvd{{xSL|y0|{6 }{ۢwrͼwsxnt[xuylvywyxzxd{*zLH{{>01|r|8 }x|vww:wUwxTxExxy)yizXxz{0cz|K{|/|"}r }<}pu'"uʀvswǜgwxgwy$by!K z΀4/_{t1 |ssqDtuIԫUuvYwlvx?)ay(JQz.zC {灁 rד_^sܹtLPuՙ\uc)v uxwr`xg=IyH.y熔 %{Pqr{rs5=sa tƆ u.sv_AwzHx_7.&x _z*LϤq*qװ\rsLt!t=qu<]vݙ4GwÕZ-x< ~y͕pqJrJrƒsт>tpAu\vm-FwB3- wA y̨pݿ"qr.rؽxsztegouN[v-FBv3,w]n xzppqg΢2r%9rwsUtot<[uͪFv,vߛ x{nm{ol6{p{rh|sa|;tw|v}b|xJ}/y/$}{! ~?{(zr$ȣzs/ozt-{1u!{v)W{wVvl|xa|yJ.|{ .}Q| >~&|byvkzwz[wNzxdzy{Gyu{za|'{I||.Q}}S }}}ӄxI8x6y,yfy&mzHVtz`{]H{-|e }fmrw$Zzֆp {؃ztqu}u򜱣$va=jv‚wMpx8\xCFy,y؋ z܆-sth!QtٮuKu@vFۀv$owS[xxKEx,Iy%= z+5t¨t>KtuhDuY~vwmmw7qZ(wܡZDxl+x yKtn8trűdtuAsu¶t~{ͿqpH^,qfir}s"tz uhwPUbx@Ay&{/|XKt+tӅu˗:vw_yxxXhpycUZzs?ꂲ{n&i|X`}%| Y|>A|fw7|ǃ|xL5}Tgm}TZ~=?R~&~ڻt{h3ǃ:OȂw.YfwSρh>āc8%2)$ԍ}ƒU7ߋ}mr8uuenHR>?%g~GX~KaTҁƑfw,t$cրeQ=jŒ%ebWЂΨ򁺧|S2#xrIb6͘Poz8 sE!ŊA+=L녂L,xMRhjNg[(JAD72ӑ :'m% $vyhYʄ I @~61e€Dϥ\2ۆuׅ۬hY5H5ǂ &6g‰辳Ժ9juDgXɃ-H152` Wwk'm (nw~2oqxq`drrU{gtDĎxva1ƌwUhyT|4npq2}rSq sctU%vWD8w1yA>z'|iSrhsB%t}>tpucLwTɏx5D@yX1eize+{@|̚yyy|*zbozbq{T |:C<|1}n8?}~lX_Y{ۀBn@a]SK?C,0,v%j3ȇy~m9``R}Bׄ0iYy4:{GcNד*x/'hlP_9}QJpA4/ȉoDqqHFÌ[OÓnavmjr]Í䖗Pn@]`/ɐƃ˅P! 3%0t‘A#h]\\ Nˋ?[.P'ɵkĔE7 th<2;[WNF?-7І5Ƈ< ~Asn찭g [IlM1>Z-|B(9%CYljjmhwnlӡpavqUs!Ht9v(x̓yH|ӊTnpw7q*l&rIa-KsUMatHOvV9\w'ybz}:rVrvڣzsl+t`ݝuUvH!x93wy='UzM {h}l,xOcxuyAkay`-zVTj){GV{8ڔ|'}47}~F~~ˤ{~uj_m+9S[F8w''܀>@\}£t$i&^cfS5F_ф8 ?'kd@,xtL|XrhW0]zlRE7jL$' MŤzš&q8KSgu\EPD8^6&QT+:Z8dʜ,OrH3 Gpw hKF_̩4V~3LB87h+ۛ} 7ߍnGnfo]LpT $q3J.r@k0s5aDu(w<:Rxv{fo~ n&wpeqa]9r1Ts!Jwt:@au5]v(cxKHy{7~ms1eйs]+tSHu:JaZv%@Pew95Txi(ySz|~`mxe}x\δ6y0S]yJ&zzC@"{56{(!|q} !~,_m,~ae~i\l6~iSYf~Iݫ~?姘'5 (0>`JlᄬdJ[DRyI?4բă(~>׃V\k븰cִ[<>RDRpH{?:4t(R ڛ4 B#@kbZl5)QiH8>~3(G ^q&MjRb'D`YjP«Gޚ=3{@'fL& [e(i\a-YI9PlcG98=3Hɛ'Ӓ _/~i?iad|Xܧ3P*"GG=x83-\ 'Ϙ2 Ғsgo^oV5pMNqD r:St>/u#zwWyx{t~8g{q~^߾xrV5rMPosDSt:Y1u/Ū$w#x`sy!E|33~/ft ^ҽtV. uMKuD vg:Ywg/ʩx#&yzE|~pfy0^yXVyM.yCzp:K{!/˨{#4|}~lf~^s~Uٷ~M~Cү~:2A/#)éG蚗'fW^5ׄpULȳC:؃/#5B5_+e]ULF7C09"/p}#Ċ/6e*Z]TsK6B$9A8/#ǝŌ \5dRW\`LSܝQK-盞B+*8էљ .ȣė#xᔮٜȏIHc߻G\^SxJ~AvX8j.aM#ns@p?8cr;[Su Jȯ AӪ8.眀#1!/mSqinrijٺ;sIlt n8guouqvwsa7xuIy>w.#z)y}|yՅp{mgǴqknrbp#sVqstKrkubtVu~vu`wwrIzxx-yz!N{zԼocqpzrǸqsשartԙ sutwtux2`;w=yjI x}z-uy}{_+{{~Ҡmzn{3p{kq0{rb|(s|st}_CvO}HAw}-x~+z}k>Ym>,nso҃q"Ʌ,r|rsL^&ucGv,wŁG/z+jfX>k΍+jm>xn딞oqvqr\tFu,:vꄨQyoiMᾺjʰkm\rPnѕ9pRoq搵[s]Et+uۊ(xHhXiǫJk3Llnonq4bZvr͖DtD6+u!wB&guiTE0jl.m}oQlup}Y-rECs*~tgwXUgb3i(ū[jkܹhmH }@nήkpiyXqC`sa*\t.w*gVhajG90kl||nxk\p XaqC?rO*gs vkZt]htjul:v1mvowqsgx`s_y2uH2zw{,zy(&|Lz$Љsbl tnKWtoIuuq;vDrWwtrwu^~xwQGyx,zrz(|"zϹrVqMRsr>ss]ttkuuvnvrWw]w]xZyt)uwDjj5kʧAl4n 1o9znp|ihqӨrVs6At=k)twZjkSl_mȽKnyp$hqrVnr$Asˡ)t8 !vNJU2wBh[wj%wkxlm5xoymqgpys_\zuhF-{wZ+i{ys|zU]vSlxVvmڰw3oO=wpŒxOr=xsoy}um[z&wEzx+{UzD|z~unpuq&vxrᡂwswu ixGv^otxw[yyE?ztzC*{{J.|{ȹsyyt4y֮tz>uz}v\{.w {nhw|VZx}D{y}*JzB~| }Ʊr9fr݂5sZtbu=~v|mWwmYw_Cx΁O*y\%X{?ĭp qrrsQAt?p}u*lAv%Xw#CRw>)x}z{o|pS-Oq"5XrQwsb{tkjuiWvbB\vp)hwhy|AnNoq3pRCq6Dfr./ys8htQV;u_sAYvH_(v*x·0n6Tn}o˳p{{qKx rgWsȢTt֞@uu(gv?xPHmn̾Ţo_8pqކqd1wRrj^fs~jTzte@'uh(Budwm¯Jn9Bo^zp-!qvrBfCsT+t P?t(Iuwa}g}si}Kkl}Tm@f}ko{}}pj?}rW}tA}v'~xd~zo|kʹg|m3|un|p%|qz|s9i}-tV}ZvA }x='X}y3~{{oҳ{p!{r!{s+|*t[z\|dui7|vV)|xi@}Ay'}z~{ͽzwz-xRGzMxȗpzyXzyy"{Kzh+{{nUP||G?|}&|}~+}NxyOxʀyhy~SbyPwzYZg)zҀmT{9?p{&|{S}jcwxUw>xzx yvy`f"z Szs>z|&R{ |u-vXv ;w1yՊ&0ya{sEBu&buXuߢ}vavswbx&Q(x=0y$q%ypz̆t:ptț;uW1 uجkvdq]vanwOx*xa}yqyazOu{:|Z"||Ѐ}b,~ҥJ~ ~D~p~N`~N~:w"]D~CQ~CHk~7~/$}~/oj~,_~2M~?):~B"K~4o~||6|ɉl|:{}m}-^$}#L}9!9H}>"} }񃒪f|:r|/|'S|+ z |7k|L\|g[K|8c|x!|4z_})[{!l{Ű{g{OxC{ťjN{֢4[2{Ja|7o|! {x|wc{ĺ {{{?w{Ui{NZ{=I{77 { {2|5{N{|N{_{Qv{Lei{LZ/{TI{[,6{A+ z{uЋf܊hjm l;ynkp\2rJI\t%6u`w]zc"jSEk֓ mvnyHPpk2r1[΅sJ u6\w/n`zǰn34aES]C1~sH~}뇕Df haMj=wkjm^7OoPBq@s-uvz֕i)\k|lvnsjp]ߎqODsz?ۋu2-uvws{'imGnoov'qj)rg]hsOuU?pv-L[xcx?{ؕssztڀOuu%|vliDwP\yxJNyW?+z<- {{}'iz"c{ {sq|4h=|[Ȍ}N,}>~ ,~x~~‘%ʂۇʑR}₉rȎ}Qg+*ZҋMYB> ,k:VԐ-{⎾qae"Yz:L5A\=$ +ֆ_ #u"Y!zlojd"@XK><+U+"HA:`5)癁UĆ9g yehpCjf&l;[fnPoCq5s# ucvE{äjyktofleޜnl[#pOۘqCvsu4`u#ޑvNw{ہbmxDnDooeqpZٚr*OsC@*u,4̒v#ёhwx|FusJwנ%t!nȝtd͛luZ3vOwBƔ=x4}'y#z{m2}bryv zmǜzcazYj{NT|$B-Q|4G}o#}~k~-yul{bkzXMŀAn3m8#tف(ׁj|Пqtb̊kY"aIoWvL?@b3]Y#*JBd<{e=rio`gwVN/KZ?֐'2j"؍p/GytqClhu-_UJnc>ݐ#+1 !=s֊\qxtip3Cgǘ^gTcI `>ss1/!}Ό[x ;o+g7]aJTI̡>([0T!o`vo{hg i^k,UlLknyA!pR6rC)ќt-Ru]x~|njf׮l^qIm\Um!nKpH_H?)Be4*(Q]= nr"}i)aSYyP⡱/Gឩ>_g4c(דP ilEqhUaY:Y[nPtXGN > D3>''IJ= nۊ1g`XPG8#=Ϛٞ3Ԝ!'͔Θ^H 'f{ih^j:VylkMխmDn;/p|0r\$8t9 uؘ'yS} fAkN^ZlV\mMnD pc;#q0s$>-uDvy}eAm^[nV3hoMqDrH;s0u)$?v"w/\z3}He¶sK^sUOtMRujDMv`:٥wt0ux$6yGzN|m~Nex]yZUp6yLz;Cz:{0>|Z$.}0l}ԕ"d{#\$T.#LsHC:8/N$Ɂ+Mca\A/T=݇fKӨCC9ȡІ/g$X̙4_%mFb\G[f:SА-K'=&n5 yp*rUt6euy}+_-lW~WmOYnFFo4>;p5 r*|s uYveCz*t}_ WnWaoqO>[pHFq:>/-r^5s*uvpwiwטz}^^ܻsW:t6O0tF°u|>+va4멇we*x~+yDz}#|J~Z^ny4V޶pyNѳyFzC=Ѭz4r{*Ѥ|E6}#~ y\^DVe1N|!F2?=4f*ЀA9ŀρЕN-j]d|UN Eɭ%=5{4G V*udNFvN(<\"UuƐMhiER<̨3R*Q`Ge2B\C5TܱLlDΫo{oqQjBqsVsCtA5tv'+uwzx(f qٴgrKiOsGjt\luznfviDp>wVry@sz#&tzyL{ ^c{e{;gk{gi8{ňk|3yQl|h>n}U'p}?r}&is}x}D3bc7e,gdixkg5mՂT>o-?4qˁ&"rMwj_ҔIaޒ}Vcސe폆hh v+j5elm#Sn>Ap%qZ4v҂^^3`bd{ZftSi)Um`#nbodpg%qizrlht'nUuqqD@fvs&wsuzwelwd<mf>nh& oj=4plhyr3nhaspUetr@vtz %uzpy{Nfz*hVziz͔?kf{9m{vn|9epe|SBr}H>bs}%|tg}x}ef`h=i삷okuVmrOdoEReq=r%BsSOwbݒdUf_^h)[jPs{kcLmފQ0oƉ6ws%LxWu1{fwwofcܮp5eq gܔKqjrl+vttnPeu#pSvDr>&wVu%wv{4xn/hoip kYq m) r+ousMpe7trRut=vv$wwzyokptlqnn.roWspttqvd=sCw\Qtx=)uy$qv|zzF{wiyEk/yƝltz.mzuo%{2sp{c@r|]Q s|u} u"y-.jl5m<n}v5oʕhqYrmxIs\6]tp txsjkpCly=mtofpgXLqHru5s町sǐxfZx iܹjKk벻m$sSnhDeoTWiqSGfr4M5,s 5rwv}0_E}a|c>} fKw}&hi5}Fk?Z}smH}pi5}rR~tUxH |bܛI|d| f|8i v|ok5h|mcY|oHX},q4}ut )}uQjxȥWzfzhqJ{!j0{hku{mh/{oY|VqH|s4} u~ }Wvb+yvxn?yosy}pyqtzQs,gztX1{0uGV{wj4|x|cy~{YvvnZwgw7wwKx^x[sxyfywyWTzzFz{3{#|C{N|}}Kuu3vr5~wDrUw]dx[V]yEy3Ez2z= 2}$\szI7t\t|unpsv0%cJvRTwDxd2xox؅}|rr˙ AsAztHqnuauSvCw{1xwȊ{Eݙq r_frϣysmtmZ`HuDR^vBvە0wXv3zRq?Aq۬rx_sUlpt$_tiQu͝4BQv0v-qv}RzQRpeqMrOws ksҨ__NtQukMBv0vvzxuגydž{_׃aaRSczÂfnsh{af:jSmBtp/rT]txuYbyBd}fz?.hmj`mRhoJB+Bq/V8sFtxi=fg$iyokimo=mI`{o9RIq{,{Ƌ{Xw$}D"ȳyc{yspkye2z7Y%zMLzϖ= {+{zQS|ބ/x y,zHyn.oydyXz=Kz7yi{H_>axvcke`{hWT̊_jGډOmT8bo&q&s:y:bT/dVufNkChQ`0juTulGn8hq&ʇtrsyI`1e~gcu' i jˋ$j_Ê7lT:nG;Hp83r& t>VuPyօڌl/}Jmsnip^ڈqSFsF3t7}v&|wEφ|t"r/thku]vRgwE͆x7:zy&>zz} [zzΈ{8q{tg#{\w|BQ|E}R6„{}& ~P~x+~0ņRx͆o;e܅J[kP:ZC􃰄f58%%҄k6"7qvmdO~dY?NZ Bʌ5I$ЊeI:S}ruKUlpYb߃͙XAMւA/$40$"Dvn|btkńb@X1M]meAV3ՁS#р S{)srk?ͥaσ4JW͂MAGS3#0S~хu_madd[%f4QIhGsaj;告mg. ol'q $sHz ubzm8#dsd8fa[yrhUQjjG8l; n-pmr tJz*tTesl̙Wg4d\kh[&jQU lF>n|;~p-ҏ rTkEs ܌NuOzsnkjkǗlcy(nZ_|oPpFbr;at4-uc=v =x{r=qjyrb}sY|!tO㑇uEv:r8x-0y'U#z B{-P}]qzxiƕ&y-awfyXz ORzD{v9Ս|6,ы|B} y~5z~o0h9d`ʂWXQNԂDJ9‚,N5 i knvfƒ^V)LꍵTC=8FƊ+- ;l>lݒٙewH=]–TW{K'B27+͐Ď zVtl4bdX\kTw%K{Aҋ)77ƕ*m&‡T ^ى*kd_\&TkK+&A7]* k1 Bbު`q[KbxTdxKآfCh9˜k/wml#o|qOؑ"un{bHb[dSfKshvBjt9ll/en#plr u={bQe[GgKS.hK}jBž\l_9nL/KjpA#~r sB&Gv{caqkZ_lcSmJoBU!p92r/ Cs"Zu"ӕvp3y,)|v`ppZqRorJ`3sAћu8Ùov5.wt"ȕ.x]ym {P}`P"w%YCwQ:x>IxACy8LEz.Z{^"|M} ~Sq&^jXP֝H`@#7ˁP-}"q#'ekXH]֊W>OJH%A?ޗ7$-rg^"+]"?##d\ŸV(OMGK%?$ 6zŐL,꒛!֏ .K\@BU NܗJF똳/>Ֆ167XX,5!Z3Uf[ϞڝUTNHhVF7%>6٘.,!xÍވ҃Z`SbL29dDQzf;h3$k)I=mIe>op%q8vv[{zZaDc]S^eLfD8h; jn3Slw)Fnmppכqkv8{lZ2eSVg}KhD)j;ģ~lI3n%):pu"qsw{Yk/Rժ,l\K~wmCn;v=pS2şq)suqt*uy|Y>]pR[qJrC@s;t2wDu(⛪w!rxNXXyMN|-}XyvQlwDJlwBĢPx~:yK2#z(v{ik|||~`ECWYPIţ&B+:.,f1(h)ǵɕ$?V̦P79IɈ|AW$91XL(ʇN󓫆0`-U饃+Of,H_1@An9Ϗ0ԙUQ' ݒUzOH ,z@8ܛL0ړO'wWܑ;f.UNG͟Z@r4e80N'z|.>ԉ(Su'a~L%c3EH-d=Uf5~h-j#{ln `qHVvb{Sm_cLhe`E;~f=h5jR-l>#Gn,po ؞rw1{~S[faL{gE"i=j5r8l#,Vm#o%q> *s_w{SkiL1/loDӬcmu=QRlJתhC]<4^4g,AG#29 8ȀQ"Jb$C3;ӣN4T0,%#Q\ L`KeځPJUIːBJ;SK3#+"Ú.l% _ O,OזMIdA"Bh ;%N3}+]"tg* twzЂO\)I&B::3gw+uXf"ܓg|R ފ׆`S4d WueZmEg]Uh`cj]cp&l6f_n'iMp)l9ro rr XyrtbQ[c^"e`bgmcs~jiMfCokHh_smZkM}oyn9,qeq= pr%s Dy)uy`_bradHd?f6f}h;i-ojWk^lmMnpy8pr ;qrtH=xvv]h{(_jak1cm^|f3o1mhp^jrLMmNt84ocv&owIx xZq`d]r^p_^sEat[{:d@ulfv]&iZwKkx7n ynyzFw0zXzşZ{)]3{XI_{ybz|2ke,|\?g}Jj}s7l}em}vi}/UWWՑ_Z ]Xvw`9jc$Zf lIhՄ6PkkM#%uu3rR"UʗX$X[v^$hcaQYsdqHg5ij%EntقߣQsATsWWsIZqHt]f`X7cGf4i'qidtiQ^T`W EZ(s]fj`1WcN+GWfG4hlht ޢDP޵Sò48VÀ7YSs\f_¤W~bڠ+G1eϜe4hh|'=sRRfWUhZWiH]:j`N| l&cwmmf}]o{iLq8l8rosNr1Xyu4le[Pfw]g`&iwcL{kf!mlh]EnkKpn7r.qArs-HyYuzcY_p"da,fdphDfxzjhlkkc\mmKEopg7tqrjrtfAyvު~`mhKb:id&kvQZ]X_u/b),gd΅XgxHi\5k~k`u]hU֖X&[1]Ӓsc`FecPWgfFh4\jÊm$jՉtxT WI4Z~?\q_ysdbRV6e,Fgٔ#3i摱j ?tT6JV҉?Y}\Jq:_ da螜UdVEgp 3ix_i@2t4۞SVYAM}([p^cayUdOLEf3hR isIiW8jZ?k]Wl`5xn(c_jofh[IqiIrl6Csot8r+z-ug[i]ƒ(jW`Ykc#x`m$ejCnhZpPkIqnl6sLq'Nss!yv=f3_!gatkhcjf;wlhimk0Z`omI,q6pB5rrrtSyw&cpg6eiDfjՃh`lv}j+nhl p_YwmrBHtot05'q_uqwxyC(`pbqF&dlrVfFszuBhDtgjSuXlmw'Gnmxp4pyp z>\x{u4^Kya`Eyꌎb^z=dbztf{7fyh{Wk|SGm |4܏Y\ y^_m`إPa$crS>f mChj2 j#j%&\t>'oGW-oZ# p] q` rArc$dsf*UtiEE(ulz2vouvq{vmZٕHn]koV`#~opmbqqed5rhXUytk&Du'n1v"pvKr{vl*^m(a nc_}oSeqphBcqjUs?m8D{t}o1ur/us{dwniwfjh]kj|FmEkonmbp9oTqqCs$s1)tGurrtWvzyoTfo.h,p0qiqCzk:r~nlsanuS7p;vkC%qw0ry iry5y{@dpwZfxogxyi\y{mjk3z%`ymzRTn{yBzp~|20cq|Zq}y}aDcUewg Wki^kQm[An҃ /p":pqxEQ_ua^ccC"ueXigz]|i%Ok@mi/n8ngwf^5ɉ&`)~b%tdHYhfw\4hNjє~?l0.nnJn/v܄]ɨQi_~Tals}cgf>[h@N\jk?elT.=mmÎ]vE%]x_ja}afscge[kgԟN"i?5k֙.3m ~m.3v ZuV茀uYvX\vv_jwb]x#eOxi?yl-'znezWp~vJtZt]9}u/_v5ub{j/ve<]swWgO~x$j?qxm,ypZyq}vŔr^Q.sL`tbuteMiug\vj@O wll? xEoQ,xqUyIr}wVoe䉇pg2qiIt,rk$hism[to N+uq>vs,zwtCxu|yfmXm no }op>rpqgYrrZsGtHMct|u=uw,0v}xV=vx|{ik%v@lrv|mwqo x@fHplyZqyLsz=_tX{c+uD|4u|={W}Skh -izVkQolӂdn]}Xo߂TKYq[0x@!<|V|Yxr|\m|_b|bV}eIh}Wh9}k'}n[~~pCv{XZVj{w]w{_mY{b7b|dV&|XgI|j9} m/'}pom~)pvz]ـzB`AwDzblzda{/gZU{iH{li9J|on'||p|}rw(wd4wfuxdhkwxjz`py{lsTznuGzp8{)rj'D{t |ptygul~umtvKo@jqvp_wrSxis}Gyt8Iyva'z_w{6w~P{Vrt|sust\v:ihu4w^vwSvxFpwy7xz&y#{Hy{i}e}8p>z~q q`rg{s\tQuE3v6v&Iwxq|n-xGoKo`pV3eqsV[Tr~P2sDt5u%vBTlwjR{ʁmvn*,moGdQpnZ%qNO+rʒC(s58tl$u\! v{7`~blv*mmJncpYq4NreBs!4tyK$tvMz}l umQlncnoYgp˝tNq/Bs 4s$tPZ uz_zSWFr/>Z3if]__UbJe>h0Vkvuhm Fo(w\yZbq]h__z)bUWdJZgm>j10^lfn pxwxk]q `hzbK_?dTfJ-il>PRk0.,nCWOp Pqx!wd2o˂f g,z}6zKf M{s<}+hcX a/ZYw]qQ[`H$b>ue3h&kM;m72pxgœ4Z`ב]GY._Pɏwb(G͎_d>95g]3#j&tgloGnSq>[xga]`s_Xُb5PudgG}f=zi23Vuk&XmNoRy r: xf@,ct_mesWg5OiFьk=am22܊oJ&vqWr<tQzPei^\kJVlNˋ,nDF Vo[.6m_. & h`YmPAJID=V6fC.ъ%(n>؁P+JJ~ADI!=w61x?.bB%ߌ':K>'ORJ-D i=J!k6ב.R%|2SO0ÃwN~/YHs[pA㢧]:`83b*eo!h Ijh rm@~szN\[lHV]Aɡ_:b3d^*f!i?Rk_ @mtYyN-]H+_Aa:{d2۝f.*Лhy!CjW]l }o{tz5MscGdA f`:5h72fj)*xl*!n Oo qPv{9LhF!i@zPkE9l2no*=p!S,qa4s& *uyc7|LHn7FAroR?盪pe9q1Dr)ft:!!umv Mx{V}KQw,Ew?Qpxe8y1O.y)jz!{| I}N^~*Jt΀D.>8 0rK)\ҁ ͑E )II.D4}>#070)| 6n1 )<I_p5C=ەhS7N0QX(݌ tug x؅EIC==Ñ 7.60D(ސ1` ~ЏN|M| '\قXG۬JYoAƩ[;7]4B` ,գb}$edgli6mt>yz3G4[A];+_44a,Ǣd$fZhjYan(}t!zG^!A_;a4c,e$g j(kyo0u1zGF%b@ d:f3g,k3i$-knmy?ooqw5{DFhl@2i: Qj3Yql^,m$oךqTru$ucty|Em?n9o2q +ӝ rT$R sę;tgv!zyF{}EMv?8w9Okw2x^+y5$:zϗ8z{E}~sD?382o+za$'Aԕ_v逅C*=>r 8zއ21+#ޕԈN8Ւ#EWCb>918C 1Ԙs+Nj#Ǖ$4ڒ'ryC|`> 8dr1H*\#Ɣe6R<gA]O5^R`V(~gbYqsd]scfaTi'dCkh0mZl3nn>xCrZSG@\Vs^Y}a\pcR`'c0ec|TGh!fCyjjS0plmbmXo;wsfXW_ZZQ]]|_x_p7acbdzfSgiCil*0knlpowtg\T_˒WKbbYdA{\fn_Phab#kYRdmBhgp /ir2[k1s:vv/QFhSj>Vkz2Ymm\o>`_pRbrAetb.hu'ivbuyFMq"PrXSsxWtlZXu_]vQ?awA d&x.fYyhgyu${[I?L3PXvSՀjWi^ [P ^@,a. d.fJt<~uBFzJxJ# MtQki_j-ea~e^G-la@%dts=pCG%g}JѤrN g*RH[VpMZx>^'-` sdhr„_OBoa5Rć;bV*{YdYnfl]vaQhcaRjqdB,lh/rnlWnn_xps#"]S=_JVb}aYszc\n"e``g@O]~RhsVmhHY[[\N` >c,eog@tc~ILԍ |P7 r/SfW+ZrZM^B=an,cw#bf:sȁ>GreJL{cNqpQekUYbYQL?\=Y`@,ZbY3esbi!F֢JIzMϞTp7Q`dUYXdK\f=7_,aae s'4FlLcIҥzoMM`oP٠dT+XX8yK[ߘ=>_?,a_e4r̄bOScR˃e5V+xfYkh`]p^j'aPcld@.mh-o0l onHxs`mS2aVPcYZwpe9\k/g`^-icYOkf?lj1-wnjmKoUo:xt3^XW6` Y5a\vc_jeb]geOjh?zlk-/mnnphxSu Z_,\av^cuh`f imc8hz\ejNgmc>j?o,krqmSs/wwJPW gY_idt^+lhF`n[cZpaMer3>5hmt,DjuSkvQvy^SpVhq}Yrr[sg%^dtZaAvM dwC=fxh+hhycAjyu{ O~:R~{U~pXy~er[~Y>^Ka%7fH'g/kvP헉zYS9q1VgFY>\\Q_"E;b7Ye 'MfuNk*IuPXySpURfXm\P[Q,_Da7:d'Me!pjƊuTOy>R5pKUX)f}XQ\[[Q^nDahX7Bc!'te_j6~t/n:O}nRsoVMivpY^dq]:Rr`Esdd6tg%ukNwm|ts#lTS|m3V-sAn.Y3ho<\P]p__R#qbEErf$6Ssid$tl9=w3m|ut`jpV|IkYrl\ChGm_]Lo.bQpe Dqh6s#k$tm/vo|%ug$]zhm`OqibfkPd\(lgRPnjiDolM5iqln$`r_p'uiq{kw cey=eLgofiUehk#[jmOlNnCBnp4or$ptK6t!tzyȀ`n4wbvo\ndYpvdfQqZhUrNjQtKBl;u4`nv#oxMrxZy{~L\zu^{la{\bcP{Xe|)Mg|Ai}3k}q#l}dqh}x~n|Ys\3Yj^aT`?W3cnLhe܅H@h03 j,b#9k#xpSx/zJWǔwr.ZFVio\͒ `_NV!aڏpKdi&?fތ2h"iowyW#zqYh\%`_^Ua7K4cϒy?fL2khej"ico6wo:yYVqEYh[_Y^.U`BKc:`?e2ogҒ# hȐMnw|tOt!uTS3juVla,vFYVv].Kwq`?xd81xgyjs @{l~t| sNS'sjsV-jHtnY#`u \0VIu_]KKvzb?-wBe0x"hxk ;{Nmg~uI{qVrrOYSis\ `s^UtaJuzd>v_g0wTjx l ;zn~Nuyno]qoP_h$pNa^qSdQTrhfIs}i?=tk/univco MyqC}wwkdolGfgmht]njLSp'l9Hqqn,=6rp"/}sq>tsz rxUtw|yvglni}neko=\lpRnqH.osTlzP\dS\VT'qYJd]<@f`5~c(Cg)ۀWi luk~iSVd}~CVO\h~AY7S~E\'Jf~X_6@s~xbT5`~ez(  hdjmvkn|V}c|Y5[|[S;}^I}Dah@ }dL5 }g4'~{h4_|k'k|m<} n+qDvxym(3z&o&'zp{TrEY}tzgctj`Vtl4XuYmP[vnG\vpy=wq3=xZsv&ytyu|x~{epu^qfvvWrRw&Ns?wFtMx]f5A`+cfPhsn,6w2[R{STʼnVMYeEm\9=_.4Tb/*e/gZinw@Z̉VTUcY-MO[EC^n=oa04\c* fiBc?kowY%\SSx^L{\`Dc <Äe4jg*>9jdINlttmsrSxXbgRj3deKfJD)h6<j73ll@)тUnA{pqC,|ewH|})~yVSxMx臁Gy34@y9Fy׆14zI( z9z{8}~ÁSKwՌMx!Ghxx@~xNj-9y1 ys(zB*zM{f@}D~}Sw"M_wp8G'wЏ@Px8xav1xߎ(yT9yszg|Ä~JQE.T?Wr8I=ZX0]_(`mΑ cn|f!>i9pbAxJ{*T DV>VYt8#\%0Œ^(ڐaÐ,d| gYj@pxgJKVD[Y%>[7^,0`(c>f" hbr%kO5qxI[D^>`?7ib0-d(hg@|Yit=kmpn tyHېVaCgbcj=qmeE6Սg1/i1( k>@jm6rDnʈqbv{MG%goBi<ˌ0j6Cyl/8m'onq mSrtyg|Fp}Aq< 4r5s.#t'>uȇw%vx> Pzd|~Ey@ۇHz;c{ 54{.I|7&W|}x~V +3gEX@0B}:4G-&)@j oD?لa:|釮4Vq-ʃ&o- ہ@2+LD)?:O#d4BL-%Q&ߋv:΁:L KkǁCQ=МT8 Wf1Z5*@]""`(b~pe?#j qv)xBޝ:T!=V7YW1o[*m<^"ܖ ap=df^bjaqxBV=z3Y7[1Kd]*N.`r"ɕc EegykrwkyB/[=]7JU_0,b*dc" fMhjnntzAma 94D.;(p͂!/KF$CjW3>D9 4j_.(Uc!Nۆg݅QN+=Ԋ%9YI4H.(E!hL˅\'<}Q7lT1ϠW5+xY$8\2_zqWb Geqj~Lr"y,TD$ ˈFY8:4 /.B)WIS#@XŠ"9 ӆoNׄЂ8wYK*[Oe,]pRwi_VjaZ]d^OSfvb>hf,wjj:ZllFwqWoOSYhSa[|V^v]YjZ`G]d]2b`NeOd>ghU,ikl2m?wTrxUSqW=VYYu\]i^`A\aecNedf>3fj&+hmknnvs{P\S>^U`tXch[f)[^hMakx=dn+-fhpIj5q9v-uВLdObf~zRYhsGUjpgeXllZ\ncL_bp_kqKvWv-Oad[RfQ{QTh9piWj#dZl#Xa]n$J`p*;Zcr%)esitpu}xGKmhNnyQpo(TqBcWrWu[DsJ^u?:av)!cwhtwtzՊF{J{}wM{mMQ|%bT|~VXD|H[} :^}o(`}f}Ms}鈟CFvJa;kN,`R TU'HY…p9u](^ e s6@p)~DJtH:YjcL)_P2+T TCGwX69#[](]{Eer܂p?}CtGtiKn_.O SS GTW 9[.(]xdr?}+BsFמiJҜ_NMSSGZW 93ZU)\1d*t;r`_8Kކ`O|ibSq5cVe]eqZXgR^K iAb;Vk(f)hlFj" ol9xKr\O^{S{`1V@pbYdc]OXKf `Jh$d;j,h+)#k^kanrm-xsBZSG\hVz^RYo`T\d@b|` WdcRJ3ff:i!i(jblmn\wt2 Vf[X^ysZ`n]cc _eVb'hjIfdk:gm(khWplq'vvl,RacTewWfgmXYib\kU_mHbdo9~dq'f@sk,tMv xȉ^Nl؀;Qkn4v|TFolWp`Z r-T],sG`5t8bvG'd@wfyiwuAz Iz)~MztP5{.jNSi{_]V{SZ4|IF]|8E`e}'ta}ZhW}#tP}F']|7IrMOhP]T?RWzE[{7^~G'1_gNs;CzG)qcJ5gtN\R\6QV2E[Y;7b]'I^=fsYBz FZwpJgM̖\QQaU%E6YDG7X\u'k^(yfgs&cDByEpI|fM+d\XQ6QMT E8X7t\']erԃ݈dL$eOufS>juh0V_.iZS.k?^F lbk7nEfI%oirkys%byO~`cSt}eVMifY^hM]!Rj`Ekd?6mVg%;n;jrlys`eS}aVscOYiWe\^*f_RJhbEJjfG6alXi%mHlMqxmyXt\M[&|^]ru_` h!ab]$de@QhfDgDhjj5jPmB$kIoup8pxvtXXcFzZVe6q\xgf^i\aOk!Pqcm0Cf*oF56hAqR$Ni;s znswxTkyVm&oYSnue[oZ^qHOahrBdt&4fEu$g?vmw2v{OP!xvRy#mUycX|z'Ys[zNF^{1Baf{4c|5#d|l%|v}}7Lo-tOslR^bgUńX!YM0\HA3_b3a߃@#bkuqO{IߓsMjPTa6SWWLL]Z~ @]39`f#a@jOOuD{I)rL>j;O|1`RᔑVVKLYő\@r]30_ˎ:#a3sit΃zHVrKiNњ`R2VULY3@q\q[3M_0$`dietxmjLgvkyOmlaS^cmcVX`nZMo^c@pb32re ri vkK|s~hOuiSlkjV?bqkYWm.\Ln`i@)oc1qgT qj< v l&{t~fSuu6gVjkiYMaji\PWKk_sLmMb?ne1cp'h rpk umG{ut|7bZsd&]%jPe_`gtbV0i/dKjg[>lj0n.l nn tbp zvz^bOr3`d[ibfC_ldhBU&fjZJ:hlq>5jsn0Jl(plrC ssByyy%[Sjp]Zkg_mN^PanT&cpDI^f9q=hYsF/j4tju qvy%{vVvnYIwzf [x\^:xR`yHH:cRyh{ Opk|!x;}tSgmUdyX0[R[uQ^AG:at;c=.e{fÁ oXwszP5kSCc3V-Z7YdP\MF|_<;Nb#.d;Kde n w$rOk-R̔bUYYXPY[FB^J;)aQ.ucze n7vゖrOcjR$buUYW=P6ZԔF0]Փ ;*`.b5d @mvuq@LmgqPEdRrtS[s W QsZFjty^Q:uAb,v*evhlUzk~*t)uodPlpS2cpV@ZoqYwPr|\Esc`5:?tKc,FuMfuiWzk}tt2mSmknaVNc"o\YYp@\Oq7_.Eir?bP9sIe~+t]htutjRyl}u8rviZYjUj\al:_XzmoaNnd+Dbof8q;i+]rmk6smdxeo|wq"f!aigc`yieoWsjgvMl#iCmk8@om*pqoqqZw*r|y&obi_gd_j_nf'l"VvgmLio=Bklp7m rb*nsoAu uvv{N{m^uf `^u]bZvUUddw KfwwAhzx6j[yg*!kz;lz tx{z]}kZցSdi]+\`_JSaJcۀ@f6%h3)ikK Isj)yjsX4c$ZЌ][2](nR_Ia@;dR5fk)ohaj.L qry>qiWbYZ\TbRo^DIa @ c'5e܌6)dgi rQox(iVᗐb`Y:5Z|[R>^fIo`d ?b5e6)xgiM qԆxf,|M^|PV|SNv|$WGEx|=Z;|p^91|a#}0e}goku:ezSPw^OziSuVazVdMzYzE{ \;k{`_0{c8#|Rf;}hcKlEurdxS]xVOUy'YMyq[Dy^;zDa0]zd#{cg|Di~mNuctY\u|\:Tv+^{Lwv`Cw^c:8xf&/xh#yokz{l}pwbq`[lrfb{Ss6diKtfsBth9uj/*vl"wfnrxp |xsGyxa)nwgZPoaiRpejJqql1B rm8so.tq$"ugrtvsK{Kv~<{J_yjur6Xks/Qdlt I|nt@oiu8pv-qw"-rxjtyHy{b};}]g}WZhb}PDi}Hk:~ @!l~E7[n9~-qo~!p5wsOx9|f\dV@f4O9gGi(.?sj†6lim- m&!onrxk| \/cUe-NfÌKGNhQ??i6kY,m'!nHqvkw{΁[b3Uds[Nf mG#g?*i6jЍ-lca!mp҉nYw'{lVNJOCQ^HThA0W9 Z0}N^6&aLd 'g0 m)vUIPO#SH-[V@̄Y8ل\0C?_&'bee 3VhmAvTSN8V@G̓X@x[8J^/#a&Pdskkf :i/nwTSYMMӁ[G ]?`p7܀b/se%h-\j. MlqJ*xrR~`_BL~;aeFK~5cp? ~Me7>~sg.~i%~k9m hoqgt[z Q{OeK{YgE{~i!>c{j6|l.{|lnI%3|o}q ~Ksw{PvwToJwpD[x q=dx}r5y t-yu-$zvOz`wo |x~!{}O&tzItwzxCuzwD֑'S|?V.9 X3Q8[},Bl^@$atcXf):.ipx.D#0X?0[ 9IZ]m2ڋ_+ߋbJ$TdDg3-i4dwlksy\CA^I>9]`_8bp2N d}+kf#Bha jlo7u=zB^td&=}e8 g1dži`*Tk&#l:np]Є sxp|dAQcm|ԇ:|5|a0|4-)| b#|/:|U|Z}~Qi=ZN8Q3NT-3W&qZ!]?` cth5pxx=P8`S3VV-Y=&_\ ^Ia "d[icpxa=RNS^8uV3"X,[0&D]`Lc ejd$qx<ݔ6X_8Z2ё],_c&8aΏdCQUf hmsy<<]7Z:_2.Ja,&c%fJh#/ j ͋nlfptvp {4;Xc[6e'1f+h%T}jfRl8m 'p Ndtx6|:mk6mM1>n+@o$>qE/r-s ;=uф *⃠=VS/W HXL|gZPSq\TNd_ X]X/a[\JHc`:Ufd(Zg3hl-jmvpڐk lvMr!LY pOv[y,R-^>n.U`bX1cV[NfHy^ais8a"l.' bTniohuuTHaKcwNelQiga}TjU%XJl(G[nU8T^pe&_rhdrtwCjEFl-vlJWmkMo `QpTKUsqFYss7\?t&C]ugvsy@>#xY~Axt_EyiIy^MzlSQzF U{S74YM{&Z{e{s }09|=ӆ:rBhjFl=]J؄QOI+EBS6W %Y(tWdrn6Cz; qY?gED^\HQ^fC9\DGꓼQ LDQ6US&W/co~r/5y9Wp=fB\'GFQ KDP6T{&W4bqy|YH}[#Ly.\Pgn^TYbO`XfUb\HEd`8gd&gh~ljwqYW LXP>xjZSmg\WKa_[UWaV^Gcb8Yefw&fillk}vrTvPƁVhSwXWlZZha']Q]T_aCGebSd8dh<&[ek0klvwsOXQ[vT{^kcW,``YcS\fnF_iO7`b)l%cnlLjo{uuoJaE~tMcetPe~j&Sg^ViRYkE]n 6_p=%e`q9i7rtwFj2|IksQLm6hP'n]Sp)RWqEZus*6E]Pt% ^huRgv tz7A0wzDxKyU\xOyPSzeD2WUz5Zc{n%[{fk{s0}SXXd#r`8:w \;XioP{[kC^m4a p#aqjruhx"Iiy\Lk>pOleRn2[śijBa=FW7K"LO_ASi3VؐK$|Ypt%erqLGaIlx-bMncPd>eATzYXfXeMha\dA i`p2gkWdf!Jkg eqjy@rQ_:M w`zPomaSccW;Xe;ZMUf^@hbB2jBe!ji ZqKkxr\Pv^WSmT_Vc-aZ'Xfc]uLe`@VgzdH1ig iyjj Gpl,xs~X0X_u^ZZl\]ub^/`$Ws`vbLbe?dh1?fkV bg@m 1onwu|S`\sUbjjX,dv`ZfV`]LhK+_j>bXm<0dZoa eq$ ?n-_r0@bsc"t VmuvYz}x{JGuIpVM*vgP.v^S`wpSVx)I.Yx=Y\y/_z5`z k{"uv}[v~F1nIeyfL@\P3RSH=W:oTKpXrANq\75rN`()s0cxsfgy~i}xsznkMf|lP]mSTnmW"K oOZ@pD^5tq6a'r2dMsgryj}8smiPejS]kVT lYJsm\@En`/5pct'qf}%r#hxk|tleWdfZ*[hS\Ri_*IBka?mii3xkx?ez3gp}lBul]pWqVqKYO!r!\G#r^>saP5gtjd+"u3fuixxj|vn,w\l^Um`N>naFNod=pfV4qh*rjslvn{MrQ~gx[i dTjpvT{V\gzP^+I` +Baވ:c2ne)ghpiWdopFv{d)V[^P];I_-/B{`o:bċ2sdNj>)f0hAUnov{PńHKJgNCuwR}`+}Ecd!}e4~ h mdjpxxMyo\Gyu^A\ya:Pyc62zEeo*zg![{i"{k }m`syLucHGve@vf9wh~2+wjP*8x"l$! xmy2o {q~Hv{kKqKlEqn?r^oN8sp{1sq)tr uPt4v/ub yw9|z~}oJRmBw,Dnw>nx-8Pox1py_)qqz rz-s{w gx%|{}~CIGjAD k. >8l67m%0n5)&o9p p34r w6z}ŀ`Ii6Cj=k#R7l+0l)n*% o.;HqJ% vxaz}HhbCi>u=j57kk0k)"m, n1wpy uUz1}A{@vL.;POF5ю]RU/uhUo(X [^ b%"gnw@LNy;AQg5TK/DW3(dFZ9 Ԋ]@` c+bgow?P: S5HRV{/Y6(;\ ^牎a Ĉd^?ho8w?VV:lX4[.]'*` |ḃe ʆXgpjkrdxy>5![9]4L`.(b:'uldk 1Xfth ڃj#nu8z=Va{8؂dcm3eR-g'hjl n߁wr{w|(<~4j18}k3}mE-1}n&}o~qc~Ur=~tY]Ow{M};zHs7zFt2zbux,z}vI&zw)zx7{!y!`|Yzs}|~N;ww|6wm}G2%w},}w~-&rw~x9Sxz|~*unt:vR6vIo1ve,`v &[vw wzqyT{j}āv::!u]6WuG1uWZ,Wuf&Iu{uVvnmHxJ{1}O\~O9jqL4O/YR=)LUA"XbZ?[x.^[ 3bPqgoxH8NK4DQ0/'WT")/VV"~Y[\_ &c5hg*p2sx8 P3S|.VB)X" [U^mĎ` -do'ifpxH8U3X+.Z(͌]"X]_@bҌdo EgtR'l#qs1Kyn7#I[2ۋa]J._(Ra!cgf‰uh ^ejio>uwz6^`29Gb-d'7f]!h/چjk $ñs-xo|z5i 1j-lK'=m!yoقpq 9tCXxa{~%54}r1D+s(,t7'\u"!gvw9*x* ?zG|P~!k4}Wz0}!{,p}|A'||!\|}|~DW};~ }6~\M6b4|#0{,P{р+'{!R{G{| f }P~#I4h{0zՃ-,=z' zm'!Iz4"z^Tz, 2|2\}m`~1S39K.qON)Q#͗TXW1UZcdvglsy1Zw-\(^#azc4Ee?~g}#kÇHp }vM{%0Wr_,va(c"*egeiDk=nsLxI|/h ,@i'ňkk"z!ln36oqG teux{*~9/p+څ`q's""lta}#I/a}W+~'O~"=3uԀ2ـk ;䁩聕R /+'DnD"A~~~~U~Ʉ! ~@Iނ gU  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~mft2   )2 9"=$?&@(?*>,=.:08253456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~RNK􆞇H񈛉EB쌗A뎖@됖B풙E򔟕MZ mjik¡nţqɥu!ͧy%ѩ})ի-ح0ۯ2ݱ3ݳ:鵘FNRXdq|)ă/Ƅ-}#m˳T'Α_"Հ6ٍ5݇#oIZ{24"o ]  $, 2"6$8&8(7*5,3.202223456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~G삒8߄.׆)ӈ}(ӊ*׌0ݎ7吓AJT_mfb b dgj§nƩrʫv"ϭ{'ӯ+ر0ܳ2ܵ0ڷ0ܹ5㻒B򽢾Rds",Ƅ/ȁ(r˶V+ΡсQճlgݴGPb(AA(r \  !#%')+. 02*3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~G삒8ބ-Ն}&ψy#͊x#Όz&Ҏ~+ؐ3<ꔙHWl]TRTY_ elƫs ͭz&ӯ,ر1ݳ6㵏;緑<湐;滒?콛JWdnsqg ˰S'ΎVҶp"x r`DJY a a[V   !"#$%&'()*+,-./01 2)334,5&6#7!8!9!:#;%<(=+>/?2@6A9B;C>D@EAFCGDHFIGJIKJLLMJNEO@P;Q7R3S/T,U)V'W%X$Y$Z$[$\&](^+_/`4a:b@cHdQeZffg]hViOjJkFlCmBnAoApAqCrEsHtKuOvSwXx]ybzg{l|q}u~z~}xrmga\VQMIFDCCDFINT[do{|tojfdbbbdfilqu{ÜěŜƞǡȦɬʴ˿ζϮЩѥңӢԢգ֥רث٭ڰ۲ܵݸ޻߾܀U{"0.9=K{]cЀoI:,+ztWhw ̰_fN!{'Ác2؁HV,U8y~%~WBUXz|bHZĂR,⁎M~O~Ac۪~F2~P~aM~xL~Ly~ۇ2aGUl+}Fg}K}T|a}f~3}}َ}ڍx~+3`~"G ~̆+&x|_GG|l|zƒ| ~|p|a}"Lw}`}匘F~>5+~{{l{-{R:{֢||Ov |_}F}+}뇹{¬l{{{ǰ{9{q{t|"F]|E}6+}Xzlizͅzǂzdz{$G{rsa{ ]5|! E|F+}>z z(zΆ zݭVzz҇{I7r{\{D|z+}z"znόz԰oz͏zzžg<{ "rc{A\{E |)5+|ȌE~ꌂ~~X ."'~끖āUydaɀ3Hk,/8^|馂"kٚт!b߶gZfG:x׀ՁapgGo;+5 q辁 سK_Ǔ'ك|xS6`qG;J0+͍]ݴ*7ačMwm`녣FބI+l^wG(Ó0ry՟X2AvgI_9UFV+NK0ABDSh~~͖Q~ ~uf~ ^y~:E~lj+K~Ņh~~d[~0}}١mO}ǝȈ}řs}ڕ]u}E+~+R~~d@uЁ^ہ|mE +̈́ᩃW&*@݁߈3E[tC`^JES*݀>*iN^Ёʮ~Z4sF]SfpDހ_*Ä#ڟs@ ΂ҚrMԀ镞ۀTFr~\͋Dz*;괁Į;۫Yci٪N΅u&qx)~۔{f%{{Ȱ{F|Gy|~T}nk]~V7~?p'ʀBڒ~\E~̿C~8N~e~ߋ~~*;j刦V-?4'ˀQs̍Ƃ mFįqČ9L:+}ց0jro"Ŭ?nc'z ./;:Pꆄ0|:Ui焗U? 'XJߑɚWT欿x*I{hWGT>tC'ӃkHPŘ< V&AŊ"qBz-XgɆɌ6S_>~䇍'ĂE;^ˍԌ'6щ툦ygf S>-'遑ڏ8Áѵؗ^)w{eRR?$4='ԏd|ó)Ԗ!Ӭ(vmdV.'Qnșg==T2(sxaыTrHȇuhb`c؅ZQ=!(Yҥl:!ǓVN:t'cPM=Ԕ(L7T5zQzд>{{W{|Hw|d}Qnh~E9ۄP&يklypzHߡzz؇{Hy{j|Zђ}KI~j~6"*$ |_}DK}y}+}VyU}j~Z~I;V 6"(c ᠤ3dxj5Z3|H1%6rr"̷؆ģȠjD"؄,x*i~YH6BW@"msfƋ=(@&wSuFhҔkYweH43S6%ʄA#+㤮Z.n yvx h XfGٍ+6.N#e~Zq7>zvu++fN*W,hGIߌz5kg#1𣶭{ڪG0ޛE t.sЕeԒ̙*V2F[5ߌ#х!ϯ Ӷ'o'/u3rr_[do,UHEY5Pzt#ꄵ3XWɻ嗥tk~q6dB|UREƗ]54D#|}ǚ ~&q3=c]U՟E51$,/zs#z4{zǃ̩{w{k|^|Pb}A~1o t } }̭8|vu}!w}ekj}^q~/PuA~A{S1Y rƣHʙA_Y!/TwY^fk,~^gn~\姓Qg'F*ύ 9͖݊-TUYz4pf˚\/ݗNQXzEĜ`9(-!Ɋ WIٯ^Tyƨ2o԰e;[rFtP¡E[~9qK--: pD"y=4ohۨQeZW[RPƜE/29cJ-8 ƈ-iYu쳗x޴bo xe(ZإPPn+E֙)9h-R_ Ԉ w|à|v|l|c#v} X}NUS~HB7*fJ~vip~lӹi~c0~X׮~N>jBܢ7g*I#āv7lpb߀bX{N"̀B̢60`*t;%uSlcYnb-X~̓MBj6ᛚ?*ḢU~ul.bsXU'QMӧvB_6ܚU*ܓӄFчŢ~ϐ;utkhbAߌ.X(~M͉B6ݚ|+ ,|͆ħ[~טgtͺlkVnaؒWuM]B^6+? phv$D}b2t2|j6a W%jL퓰BӐ6˘Q+^C}ˉA\|sefi`c\~VL;oA۞$6Ɨ+X "+C|KIsf iQF` qV_Lf暞AƝ̖K6ΗY+Y IۈY޴{r̹)ib_׮V0?LIsAc6ߖ+ ~]BU~yR[}p\O}g}]n}T ~BJ~?R4[k)!!+KyFp?gP] TSbI=?Fd4K) ǁfx̣yCǘp˜fN%]ȀSܲ Iݫ)?=If4@S)ŎIɇWxˆuof]^{SG#I&?147))6􍶂 x[o3fɉ/]URTSIt?-54<酄)M6:끐gx^}oDf]IQS݋I?,&4F)yb= GMxYBEoO&@f!8\ߴwLSIeIz?4^z)Nb 9wi=w]n9e\`kRI2>74b9)tAt;6&Ǥwi¥inT e&@[첹>RH֗d>O4}e*&ʋĉ\wPn4]d夢[WRb.HtX>ޟ44 *FxXvmګ#d·'[!RI&H>nj4*r (e~o}Y~}U~}~}~}`~'~yN2b |G+!}ҁ`}r~~>~Eh~ŏt~րy a~QGv+܀}QV}qً}4L}C}!~D~yx~ł`$Gj+T|yn|B׬|ŋƐ|۴}7ǡ}q}w~*`~Fu~+^e{vL {i{⒈||ZK|yJ|v}j2_I}TE~f&*~zzޝF{{L9Q{Ξ{ݓ||:u|^}ME}׈*~]B:y)y竔тzzRszߜzI7{_t&{ꕐ]w|FD} *}oyyD-'yl^yͬyzR{zȢr{W5\l{2Dl|*}K{x|yy!HyZAy3yzvGqnz[{+D|@*|x"x&%yoy6Ţ'y|7yոFzHpz[d{9C|g+ |͌Axtxؓ˂xy@yGyzp{zg~[Gzܢ C{+H|~kf|D*|֊|ś}4퀟}4~=w~vwI\~`:DMF2+`-€ĀoIղk%րR//۠v2)TvՀq_FL+Ѐ=n䂀s݂l̂ς]vR҂(_8ځE职*+Um2a 4a % (lum-^cREet*r/~ ~]UE~G~BtVd]?DҀp*}F-؃iCfفSRsׁ]C⁘Df*Zb)#Suj슅0hHo؆ׇǀ+r`\wcD;*+ kiۿ8E{ڐ`hO3q݉N[͇C*&蘀、ŀ9G1qaQq =[2Ca$7*3 7}JMVY~ ~:~i~{o~nMZ'~kZB~j*X~\Ys~I~Sڥ~PL}񥲁}nP}ޛY+} Bz}*f}vn6˻~ƚ~^V}֣}}9}m}tXe}pB}*n}MH*~ͺ~=K}}}i9}YCl}? (x>4Mކ&pw{AԌ!a3Rl[W]@쁲P(Ȁ(DwFn{+fф\:\u# k͂ijW Ɗ+@$(݀mֆYDŽ[ƙ}~w* Sn❊§60W{݁iAUEy?)"97EȚ€oڽgeĂiЭvzqFh%Tw7?g)-~X΋ĄbX>g聠EyہgpT'֜?FLW)E~pԪoꃒɷÜw.g_yoȯPgT"ST?F~|)x~WvyGMyzIza&z܍銸{|ye|Ti }U#}>3~'RÑ|ɏ|ݻĎ-| }}DXn}{)~ i ~T>&'#H z`ʏ5ƀWݜXˌĊ{wh5TU]S>_'nS?ZʅszB7gƇS>' -ӎʏэW$KؚWvԉyxf񆇇aST=DŽ '(SG.66Y(~~Y@?xn%>fQRL=z'LSxϴ^ K v}qv]d1QɄ?=0Š'x_̢.)dأ(ubӜcɄXP{y<Ƃ>4'ۈʃyW((KԄsw"bKP<\ݑ'|MɪɸƋgJ١0ʺ`f3sXG b$FOł<:+'BӼMʧ@`|Ԇ-3rra̓ϤO<6SG'xmxݕyOJyĔY{zL-{ t퍽{c.|P};I~%XwO{ݖ{ |P|I0|d}ts}b‹~O~;V%Eu%z8"\\o~~(~!%sPeb[zO[:݇-%3X$w ˄n ePsŃTaNЉ~:"%3ŲьՓ p6TY珠Qor72`ӊfNUk:nG%ghC畬'al RqT_ϊJM~:O%ӄo3eep3nsoߓ?_OM)85:=%Ճ_qȓ`4m 3}ҌnY^ L]'9K%悌,D ܺnИ39Mkq|3m&\5KX95V%$#ı-G^%Yi{| l֦K\gK6$.9&JXrlŮސP.zbl{\ Jۚ9 ˓&1vwxgǟx̒H|gH8}c5䋇~" ܵ zB{(8{Nʜ{J{w/|qh}X}G~U5x,"t~a~E}I~E~(vǗV~hhY~XGŽs5V"怂mȄg+\ fouƂgnX&0G_5|"~/4O?r[e·u./fy^WG 5f["܆[]Rʎw5tWfE튋VF5[#"M)&:N;:МԀss ړ\e,,V%@F)15.#`$wrϨ<~qUjdUADEC4k #퇖bo\qX } pFbˑCTJTDًA4#|TS=|\1oӧ`bD?SߎD 4Đ<#=!5ΟV{ڹ{ǖUo$aאϥSמDc<4~r3#¡xP9x_y B!yutz#iz\{O;|@ }0c sVez{l{I{u[3{it||}\}O e}@]~0P g~: }/}}}u}i49~I\Y~Nߘ0"@<0-5 .E֟QڃH nЀ ytQ=h \(łN@I0 bҁ֞ޔ6;͈:KIws㢵h! 8[6N4?Ւ_0 6 pNa6~s5gv[(MٖwD? 0p *THݙ&{)}Z&r$Q)fCZb؏PMF?V=0[!XΚ߮\bާ|!hq.e[YDgL#>鐦0!:h%4pzʤ oƠVQduXț}K䔕>q)l0a!džkƮ 5Ƨ]z+Vo4#*cjX)XKVl>B'0W>!Cژ8s~췊4bbyn誇c%W٘=KXd>"0_v" Vyy{{{z qzpgc`{\ਜ਼{QϤ|E}9La~, 7A{| {F(|ql|Ig>|\[})QW}E˞~99>j, BR$|}~"~h{ ~Gq9b~Ig~u\~Q6E9+ E, jnkҾӄeYzpرf"O\Of=QZgDE"F9zJ,eOSIz(hppfiyz\QŅEku 9 ̄,;;dLMypYfҊ[PݢEAÇ9,^M|iz˂8(x׳ݕ/o6qXeRᑑ[&Pc+DЌ.8#), ω5ϢȁPMw5nY.d(Z^kOҠo`D}8kp,OY .ȇ*RW qwEmr=cYԜO?՘D){8Ґ,f s= ܶխv8mcGSYRNC/8,rG \+׵pcv/JlbY6gNҟ/Cј<8&-,͎ Çq6z}ztzj {a{W|LLȨ<}&A5~&6@* Nf||4|s⼌|j|`벷}.W}L~IA 6*အootN|s0~jYc~`̲c~V1/LA6*)ӍfīM|fsgo2j`Vz}LvA!6傐*7ւ鉴|Osiֵ4`cVȆLYGAxi)5+*Z]&_P.r{2ri3`2FVdL7Aa5i*U=4{(lr)h1_:UbK㤅A76 `*đ?M}zv$q}2hKd^eU^0Kx@ҏ5𗗌*̊IުyJpͷ{gġ:^RTըKN@"5+ +y_p{YgPy^ fT/DJ좫{@ʕb5+܌ #Uqyp/ )g$T]ҭiTp̟uJբFQ@c54+Az? JK-3{vK{mr{dżX{[|LR-g|Htl}>~3d(~bʲ~v}m}d}[}R~0Hi&~>r3]:(eme~U`v}ŋmds[yPsRH[΀> iw3X(,b2~Ʌv5ļomgdl׃L[TQHD>3W(~ȷIvQm?adS [KɆQ8IH@I>T3a5(˔ÄEу~Gu um>,d5B[>QܮtH:>.3qc(Z}ؘu{ Al&kcʸ!ZҲݐQJ!H[r= 3G)B߇ۊ}Ut+pl4>cJ2bZQQVuGh=Ԡ V3e)uC|6Uto@kû"b׶qY߱PȫGF=W3%)Y||=t<k0lbY\P*Go>9=Q3ƘLJ)ˑ)3|hĘ}sk\ѩ.bugYLPGhә=Þz3+)ч|z}{.2}/{j2}j{\}{i}|o~9}twp~~`<~~F?+|>~+|w~X|~f|~}2~}#}ev~D_~F+ *{9{y|7Ď|v|Ɂs3}!q4}{vd}c_ ~n7E~*|z 3zӈ{7 {N{2|W |u}H^Z}ڄ3E;~u#*jyz%Ѽzaz{k[{~ {t~|"]}9tD}䅄*U~ix4ߍyAy\y[zC(z{{:s{~\|D^}Q*S~ gwƬ/x',rx׼xࢂyH%yśÆzX9r{ y[{͐jC|*\}ew+ږwfw>x$x"yeydpzqZ{?LCL|*Y|R}vsrw)wf›wѽ xA:xı3kyaobzZzÜ6B{*]|GvהvKw=`wTxNxPy11nyȧDYzzB{s*t|nvۯvխUwSwuڦ4wxVƂOxnyvBYzB{*|zztz{ {{)|j}u+}^W~aEj* ~~Ѩ~~ %~~2}~ ~tLP]gD*P'~~~{wU~!~~_~Ɂt0]s/Dp*&}8}1};}\}ц}6~gsC~\\~D ~)KX|ܕ|=| |Վ! |􌷚2}p}U$rX}[~ /C~lb*~˃u|{ԛڴ{ˇ|[|mn|+S|_O|9qb}[H}lCh}ۇ*~EzʪHz৸z{XA{3{qބ${—zp|+ZW|B}*2}`z*սz)ơz:zZK"zڔzʥHt{%tn{QYU|B||*>}!D&y'ӞyĆyǴz^/z*zmzɩmd{%X{B|2*C|΋'Tyҵy,ëyyybyaUz=CzlzQXK{R&A{H*V|yJ(yoy\3yyUyOzOlzBX*zAA{x*|@ys܍TyzzOwz|{ ?{· |r}f\d~&D|~*U3%}<ۺ}dρy}x@}́ } ~~r9~[nZC])8(E7AѻӀw`d~s`Yq̀K[8CV/)S LU"jW솫օuLpVZB)q‚`jxȄIP1~΄,~o~HZ"B/4)Ap~嚡դ~~&~e|~Cyg~=~Eo~\Y~|;BM~)~V}L+}ĦB_}4}l){}Q t}UK}im}@X}A}j)~޾}O&Э}||¬բ^||!|ΟzlX|W}$.Ai}^Z)}%ܜ} Γ||찍|d{|K5|Q~|mk!|V|A|')}:|Q||oň|9Ɵ|| y}|9j|JQV|j^@|)}C|*|8|CY|${価{ݷ}{qjN{6Vn| o@|_*/|fxѴ xñy^yy7z#N)z#|{mo|X6}Ai~(~ޥ]{쇑{|4|^tz|}!2}l~@W~@Sh(@ ݇zDEn慤MυI xz lQW^ @z*^(()Y΋2`5ϐf'kV@@r(*ځD3*&~}~jȂ\V?Ձ-(8?سN8Zsuq]Ԑ͎V`|ցLi䁀kU`?(N!H 䃪` )uМo{5 hTC?A܊(bӼصg\*uک󚀁Q芋y~g\ S>Y(~њēaпEzO$퀁x(!'f:RMQ>b~'(~Хq˰z,P0|@Mw>ewJR>@~(~L"O¼̬3_q3w(eq$R~>@~`(}W;w WYw܌x3jx›(yjAz>z+c{-g3|!S}=Ą~H&hҏ1zţz'{$}{mc{ˊD|Vy#|g!}S ~>=x &aێ~Bh~?k;~< ~5~O ~y.~f9R̅=E&xBbCcnVvOLAGx86 e8R00Q<&~ΡP狣K8W(g&އFw-ɉ)ˈ$ބ'Dt'gv<ՕYv`wԑx*(xグsyrŒzaV{N#|:7<~ $ą6j2yy쬭*zN3z{!+1{rM|i`}N2}:*~$.bC}S}}>.}R^}}q׌?~A`~M 9ۇ$5g޶ʃO}9tNJp򋭂-_ΊMfd9$cbڒꊈnL"#2ph_xLτ9 $#=vQ-( ~Eo?}^_݉\L79bo%6Sb٥ۛGA<};mቸ ]cVKԆuu9V%gĆӯ܏i~ɤ6n/{bTlv/\PK \8)%y;߹’d-{MyϊW9k̡N[14J8 8N%ΉoJ_.KSXy*rjZITN8'[%:.ǣ|оW-8Rx(jQZ^[I80i%2=̣zu v4w hw%xtuKyXgz[W{uFߎ|4 ~"~Fx禃byEy`y%ztS{(f{WF|F}4Ҋ~"||1|Dr|m|t }fdp}V~%Fu~4e"(`4Ёyf~ЗTstIe݁OVw0BFZ44_>"?0GXpt~KPrՅe J@UEˌǃ4ЃE"zcnL[}yrl.`RC+_3Ґ#yxv'DvqwC~}wsJxgey[3zM{?ES}/ێ~}10xדby/"y~X,yszgc&{1Zm{Mo|?(4}/Ύ~ %{z{ʈ˩0{~|1rh|g"|Zɛ*}Ma5~1? ~/ǎԉCV~ }^,r'Àf=Z]M>ݒ%/Ǝ*Y LhGۆk|qXf YL ">⃥/Ӎ5 \䂷譽2ixEjD#{ڣ$pՠ+epEY`^cLckY>>:l/ !YJ}٩+y}_ncWŗ:K.,=яҍ/֋g!Q6{۬{x((>mtboVޗuAJv=UP/!|/ +XĨށ˥o8wmlo{ahVx7J(=a=$ V/!舐t߸ċ3=&w­lx.aRV*蟉IT=/D!Ɗwfwxx9oIxe"y~Z=zbP`{pD{$|81}+FTjSyʁaz x7zHnze{$Zɧ{P!|Dj}8$v~+LQ̊$|Q|jxc|}nǰ|d٬:|Z}rO~DW~8=+S_H$qd@`w! nadZ_O("D;V8+pс`4suwbam0td0ԅZGOyzkDd8+"vrҍ,߶4vβ0m{EcӪ"gYĥO9ʇC8=+f& ," v ƓUlc+Y$NҌCB7Jr+r37o~0.1u6 kݬיtbEkXoݔN/4CB7Ŕ,wЅ}4.t>DjNatٝWHM{XBܙ^7, -(x|ttsȰa(ja#WjMb0B7=, Ќ N4|N2sc}j5ç`ȧ?W.M8ԛB97>c,%ne }:0xxQzoxqYxh#Oyi^ıuzU&fzK{@M:})4!~w)K}Bm Z}zz zqBzh{0^&{U|IK }@I~ 4)ZUZR4}y㾦}q$2} g}^}_U}Jq~m@B4)fzXRyu遼pùw]g$^dTի (Jޥ[@>5)Č9gmfy p^gl^7QTLJ@.U:5T)ǃ΃ExDoՋgL'^KT}J9@5)ے9lDwx#UaohݒfH ]nT{JJ*?򝇊5A*&Cwmn˶e۱w]\Ŭ SyIݢRh?5<*K2];~޾KvXn}~e0ԟm\&\RJI|?s5 *mb~hvWHmŵ5dఆ[ޫGRISSF?Zf5T*}<'ψU~/ v Zmvd.z[GR>I;?RC5 JX*֌ {ȿyus,yk0~ybOz%YXqzP7{{FǪ8|t<}2UR~' H={Z{s{k'{bH>{YR|3P1|F©}<~p2U l'ɕހO(@{~s^}k}b;}YH}P)Y~CF~\}6}C~~w)T~xu}x} yn}y}zg~z~c{v~r|-[|LC9}|)/~w>9wƅCxa:xxbyjyzMq{sZ|YTB}B)~Cu؇vywGww֋AxpPyy҈ pzZ{B\|") }t4ouYǫvIqv㒒?wxCRy&o(y#vwD|zwlh|xTy9!?z=)p{D8sgtӘCue%v[ w  wԔ~x߂a~ynv{&X+|LA[}(~~xq~xę~yx;~y~zi~{$~{m~|X}A*<~[(\3߬~6|R~(|{~'|m~|2~+}\~5}~^~mm~~}X~~@X(#hݭ|τc||󃎲}}<}a}al}؂(W\~4@|~(%~܎{ٌκ{j|Ʊr|!|F|y#|k}!V}@?}(S~nZz{5{Q{PԠ({n+{~|j|nQV;|w?}bR(j}yqy𡌼wz, z;Rzjz|a{i{Ug| ?|(}*Bqy y&1y^cy}yՋzzzphKz*Tw{vR?7|(|_Hxxx Xy CyF>yy;zrgzwaSz>{(|L5IxuFxpxkxڻ0:ygydxy̩fz7SazR>{`Z(| тxLЃ>xe`dx}xcxҺey x+y~-fTyܥSAzPX>z){ UUs9tuvAv@>w"x5|'`y_iAzTف{>ÁG}A'-~lw;g4wۻ#xrx>xyyz]{{:h|%Tmz} >\2~&!z2zǍ{II,{{ќFz|%>|{}=hR}TC~b>&πqӂ((VՁٛ+Bz~gցfSnJ=ʀ'&ހdѴR55ʀbzyKfÀ)ARn=t'ׂnϜ_рˀZzꍑ2xeTRpj=t['GG+߳Z]4# ^~~ӕvy~zd~Q~=+~'~tX~x~wI~GO~,t~ct~P~&<~ <'~Z~ϼ~ȯ~J~ }֭Kz}gsx}/b\}O}cKI9?WL&g~ɓ;r;s~Btt΍su}w"nOxk]yK|{8|#|~uSVvُvȘwm?x-}ymފGz]+{K-z|+7ن}q#~q~мU^xԐy -yC!zz|`{)mj{\Ԉ|JG}f7~O#zQJBW}sNdzj{l\\,J7][#䀓KQ0vҌzNw9zak[qJ(7nɂ#փRbڬ(ס?Ĕj}~yGj {ZЇ_I*7s($xbYySߎzCc{2}`!~$EwxN;xy,|qyo֓`z|bX{PS|2C1}2׉~+!Ui~@~( .~( ~6{~Zo~al~S%MCv2ɉ!^Z~Ŝ͜o|`Hz+UnAaǂRwC*`2ˆ!̥́!ΌZJ뗤oyœymvY`V%R%Ն/Bq:2Ň0!%XKו(̔axMl)9_@TzQWBa2G"5UdVܗBxc:V!cv˒jϏ ^6Pmp(Alj2g}Q"aƕީϓ9uaiy]LOA2""sA(ԵlŲS!t‘chA\*O%C@͈Y1Ža"w)ZtB"h\3N܊ly@1cT"ȖR?q܍:rƒ=syI!tn|ucavW:xHJy<{u. }K҉l2t˨u6Iuxvn4wc ^xWQyJqYz<|D.}SZXwPqZwтnx[xexmFybۜ zYW{@JI|<w)ꂘlӂcaNV(OGÎ/b'TH*;zL.?X ˆ@8̤.j|Ir͞8uhN^ASHG:k.z EkA82{rAh1ۢ]җYRdrG7ږ1:č!./ ք܍bٰS{|qϝ̩&g̚C]|e4RF:.ˍ!qr{wsrtiub`vqVإ\wLyAz5ٖ|Z)h~SuJ{Y3ur^viͭkw?`gRxV yLzHA{5ؖ})>~z/+w{2x^rxi yE`ByVzLu7{Ax|5Ֆm})ƑRJM}Rzͳ}Wr:.}Zi=E}_6}VY~=LA|~Ac\5ԕ)k<Ղz4qɯOYhثm_eV,L AB '5Օ7*K]y:;qMyhh_:hUbKɝAG5ӔX*AcFvϴۑxڱ1pexg>^}U^KO@טIH5ʓ\*vv߈Lʴ0w԰ronf̕]ÓTjJ%@z5*@8~Գvꢬn-eبAc\.SŸJ:@쑝5q,*z ~K]vvn(ex\SxJJ-?𖝓5^*Rd}γ vpmҫCe*\MSCYIޚ5?ݖ85^7*7ˆ?{\ss?tekucuZ08vQ!yx%Gny=} {2W|',~~lE{xv svkwbdwZ#xQ&yG!z=}{2ę }h'ʒ~K{iuxsfxk~NyBbyZzCQ {G|=z|}2ɘ~7'ZE{;}sm}k3w}b0}Y鬸}P~kG~=w2ט..( pzsjeboG(Yׂ Pç& Gi,"=l=2i\(@Wr_邊z^rjkb*dYCPJGHSc=`"2򖠄(q"yVr"i貅aJXތzP1RF>+=43(eCx׹zq/i`íeX?OOF`<82(⎹lUx4EphvV`"›WKO cF"<2 l(4wǸħwp,Ϥh(_جqWfNգREm<:tc~3mR4~eO~]d~T~IL7~Cc.:b0;D`&uttUHm!,e5ꂭ]gaT: \0Srz&⋫t^m 0:eꇄ\nTMLC\?^:0k&ݑ%BڃtTlvd\b:UT(,KtC9}0w'1؆SʼnɅ sM#kŹc䵴[̱8SKQx8B͢P9圇X0B'k3&rSk`9cr[]xdS7J>Bi9țҏ0̌'HyUr k c<8[)VSSrJۦWB 9ěsP0ȕh'}r^j丐cC[R㜫Jȥ虀B{19ך0'ߎ|ׇDvppwqzwrصx:s㥪xuyjvNzwn,zyX{zA|{(~}XutUvOu]vvNwvޥx=wxxyymzzXL{{@||'}}btxҀuy vAyvyQwzQxl{) y@{m;z3|W{B}C@|l}'}~ޮsUгt48uuЀivSwp\x[ilQykfWzO@#{׀.'}<qr}sχZt-ouvvp~wu-k\xtV}y؃?{4'|}ڨpqer௷sqt mu}vGjewڈUy$'?|z+'| ot~kpu>qk]r[2sZtl%|uxi)v叞TxI?yЉ '{jS6nnTopxqۛr(sVzxtޛHgv7Sw8>yI'z`m%no0 pﳄqsxt]+fuES$w)>xڐ'zsmynäo2p|qr֮jxCtUf3ulRvܚk>x(z]2mEnɪ5oIphVqdψrwseuRvq|> x.(Gyry)p/dyvq\:yrzz"s3ztzv{[w|l{xW3|z@+}U|'~1},x[sΠxty!ulyvyzwYzsxa~zywk{zV|X{?}.|'~~%Owwxx{x{yxyytzFyz~uz{k&{?|`VI| })?|}'r}~يv% v w< wџyx]ʏx}uyjBz{!U{`1?+|a)'i}vדtшBuqOv/v|wSٍxN|mxɄiey$Tzt>{'|ꁙժsitU\uuv`Qw"{VwhxcTny>{ '|U"rVsҸ$sětu'u5Ċv \yvgIwSy/>CzU'{_^q^[r!\rߩwsut\u?Xxv:sf wNHRx{=yŌ'{![pſGq׳rG~oss̭Ptivu+dvɝuQwF=uyU'zÉnsp@lqQr դrݕs^tm)uuqAdevQw=Zy/(zp_sqLj'q_rw4s8PtJuudvQvwC=ax[(LzU׆|or{p| r|3s<|htrE|u||wBi}%xUV}z]>~ | 'M~}{2sjɬ{NtX{tu:[{v{v|(x |4|yy.iL|z_T}T{>}|'~~4 zgwLzw:zx|{y *{by {zp{|{Ch||T}}|>`}}&~h~4xN!yCLyBy1zR;zgz{2h{S|l>}( &},wȇcx xfxȚyR8ȳyzXHg6zSL{1=|l'}fzvæwQ;wwΙxirxqxy?f[zCR{={'C|у#ͻu^uٜvLUvwBԇwޓwx!e*yQQz%=A{'y|WStutıuduܤ>vkՅwOuwԙcxQyA~%~ځx|׎V|Z|M|4|ڊ#|$r})a}PO};}r&~AÌ{޷{&{z{{{qi|+`|dlN^|:}&s}n z7zzzztK{'5o{r_Q{M|#:|&|$yCz Yz uz-czY#}zn~z^<{9L{@:|?&|%yƲgyٻyзyyDz2} zP5mz]zLm{U9{ΐ&|=eyŏyMsy]y؊yò|y%mzA5]xzLCzy9{k&{׉inGLopƜqGsL tpjvD_@cwLy9D{$}qq1rsgtu>vox^#yYLz8ӂ)|L$} u4vuUxv:wKi`xxoOy^zLKc|8}6$~}|i|ׄk||2x})}}n}](~gKρ~8xzO$%̼CeӃq_` |%m‚Z]7t(K\.8Y$S<YT҂p:ӁÈ{ҁilӁ\~ˆJ86A$:—eΕ" z$k=kg'K[m3JD7rf$045<줕ߓD "xt)ixZM@I 7~؊%~dAsC:`pJv//h~Y;~ŘH~7N~h<%$~(_xb_F䳶2vL~th~uX~He~S7+~#%3}s#D.~Xu~4g~[X{~$}H4}7%}%Y}zAi?m|np:F;8B{}/u,.igJ\ҍsO#@O)0̇ ǀ5^azmt=ah*\ QNt @|0Ԇ_ 68ZaL}ؐrԎgU9H[jM͉a?0!GGѐh\φ|>qbAFf ZL?0!uȎ7zpȞdΌXJ%LuB>Z03o!, ;YA;IzNofdFӟJX AK2>C0'!ᇦ&gxƃЮLynHc܋|X+Km٘=㓃0 Í!y觨mNn}A>ps}qEiWr^ԙt6SuG4Qw9y,E{؈v~J pApqC|rCsCsOiGt^YuSmIwLGx9z,*|oV~rs s|CtruhЛv^^wS:xFz(9ԏh{,6} #~̍8x҄£yI{Рqyr=z*h5z]ؘ){tR͕'|CF}9},OM~i܌O~ʃ ~zqyg25]C\zRRdFcU9X,f^ɅYBzp9f֙f\QΓF =9m,CVx/xϝwoaeӘbP[ÕKQ>E 9G, k\uޚN7iwnTNdėZӔّ'PS$D8Ԋ,_k i~ܞv2imȝrcYؔTOvDNs8nF ,YҊg'i~;5u0elrc*ў]Yk O(3D+84,Bs؇ ݠҮ'}mBu-lCbǖYNݚ Cэ˕8 ,>G (kR|nu#ompdq[/sMR:tH v>}x3Xz'|,|8ptqlrdcs[tR~vLH靷w>wSy3\{B'}"Ov|Istt't7ltd;u[lfvRa|wHԝYy:>mz3ad|( q}t9d{ȲxtH2ylFyycרUA};3i~0(;<ʉU{1~BsF~SkĪ~ecdQ~Z~Q˟%Hd>7v3p|(l/偁zф:s!]k7ꃌboIZNσQpH߂>3r2(Q'y|rC,Ij[ˋmbRYPޝ݈G҈ =ؕ%3r**(֌P+ *!xq3F&is蓣aGn XߠϐuP:G/'=͋U3W\^(`eiHwΟpOhB`přkX#O]Fd"=#-Y3Z(WԆ:w9Ɲ`ZRޘ]JBw7 9–q0\'y=;ipfifacSZOR~oJ6꘨.&)3چ5k?do9]2CfU!N5ƙF>X>n6.a. &Dnfrk?ɍslʻtjnM#u)oȝuqUYvr{wtixvTzxh>6{vz[&}|bEqo!rpT-sq|tpr uKsvGuM{XwTvhxyx/T?yy={<{?&h||ps4 qsurtsuϜVtvÌ4uwzvxh x ySyh{$=z|H&T|}yloD{h'pM{qi{ت0rl|"s~|t|yu}\gDw/}S7x~7=azO~&Q|~Ѽmęnǃo1qܙrGs\xt؂"fGvPRw܁=y&b{oϱl 'mTLn oߊ,q#r}-ws8e8u Qwpr`1t0lN3uє:w&yg§k8PwlI4mW_nswpoppcr]|_sޝMuJ:w]g&yN jekmGnmoAIipoq_}sv]MuA:v'xZ-xjx[l xmyoEypjzrvzted{vSQu{xO<4|zq%}|]w8nCwo}wpɤxtrq8qTrГsӁ tqqu `rvۋNJx:ys&Iz\o:pcp6eq˟rrPsƙotG_OvMuwf:pxڊ&tz^un\oVЦbp#!qTrT}sntC^6uLv@:xfp&yEn/BoRoе2p3qg} rmsq]u2Lzv9x&y m]zKw{8|T$}*Ove-3vƔ͢w'Xwx'zxolyR\yJz8{k%H|F%uVuUvvw*Vy/wȗjxyZy.hJ y8'z׊+%i{(tt^XuOuڨrvrwwi=wԜYxIOys7zb%}{Ut92 ttՑ:uԄ_vvvåhwJYnxIIy(57z %{RsgtMt쐡u,uvxv_hGwYw㝏Hx—7y(%zѷiyڃskll邚nFEoyqksZIuIZw6z "Հ|/lƬ:n/ԂJoYpه+rCynvsj6uLZcwIx6bz"ʀ|\pp6qi2rgs5txuj=w0ZxH΀z6@{"ʀ}tāwQw㞫rxlhIyu*ywzdid{1Yk|H<|5}"΀#~~Y[~?- %T -v ]h~XG;5\#3~O~O~E2~?~A"u~Ag~KS^69wӁwkЁ_LQ]B% 2"s2Fӂ~W%)Kǥq[svVPj|]0ePgAh2C-"~؟X͋߁!HuiP]lOAJ"2"~"~lrf8DzW8S૆u;niL]OqBv@~2~}"}ə8hڐjo3k|ǑnmqϏoKfe7q Z'sLsu:=wq.yυ%|kAm#ɒ`nx|ioqxhqRfrY&tLHqvu=Έx^.z|}n͏RoTq"{cr9qsxeÍ^tYvSLw=qy|.{`}ift񎩒0uKvl{gw'pKwe{xY yKaz=|.}@SF~{Wp#{ք|0zd|yo_|dA}LX^}KE~K=A ~.sTl1y鎿IxqniciWCJ·߁= j.ƄpJ6!kczdʼnr6'ivx`4Wև-MYB*ㅿ6H*V@xѓp`hj_PzV3L[^A6D*ރ?4w)\oag09^lU?dK @,5ň*~Svoßf]`T֐K;@;S5*t $}XvansDf4F]TvJ\@ؓ05p1*ML7u{inlDkSfܩ l^nOV{p N(qE-t;Ev]0’x&J{a4~uHlBn0mfn^ť\p!V qNys^EuB;BwJ0̒yl&3{a\~!tKnm+ofp^rVdssM tE Jv;<^xk0Ԓ:zG&L|K~httmtful^9vIVwLMxsDڙhy;&{0}|Y&}t_syDlyezA]zU{gMX)|!Dz|; }0~&POs@l4KdO]/qU.L9DJF:ր0&싋[rY0kid \uTu5Lr CnY:0˄V'+4;pWjXPcF:[SvKC{k:u؉0'O5jpJriɘb[חZݞӕBS)`K>_gBO:-0Ap'Q̄^oBieawZqRכJfJB9ӎ0'VGo0ޢEh(aZ5RܙJɗeB%9ڐeb0n'lBԆm jgk_5m2X=nPpcHrM@ftk7av-ە(y1$<{~gmlf۱.m_o!X).pmPzoqHms@c&u7ew-y$a| Q~bmq o;fp9_~>q;XrSPdsHu+@Zv7fCx-z$yg|ߊ ~m@t*fqt_=/uxWШvSP,wXHgx@Ay7`m{#.|$~;YlyJepy^֫z,WvzO{ZH,|@|7[}.-~$leto ^eW8O }G?'7O.C %/o Ɂky\hdԬ]ΨoVSOGمF?7Ap.h&%Jj 2d ]/Uٗ76͓c.a׊%RWi+8b[ћTޢlTMܗF[(>Ɨ?6Ȓ#.i^%gkQ`JlxYmR~oK4pCצRr<-t3w+M=yz"x{h~glYm`nY0oRwZpK3Ir3C٥s<25u3:w+az"Ƒ/|3*~vgOo`TpYqRjrK*sCեru]<4w4x+sz"ݐ|J~g&t`p,uYguRIvwK wuCx<-y4{F+|#~!!fƶby` yY&~zERzJߧ{gCx|!<|4}+~#\:Pfse_ѱXޮk QҪ-JsCxu<؀4 +֓#`wk~eݴ_VX|Q{NJbgDCC#;霖4'̈́T,#8iMeo}^COXǍQJ@C ;4#ʇ,6$=QQQd|^e-WP@I?B#;~Í!46,@#$g(d^)Wa>P{I B";g\ 4,LB$;kdd ]-W6PXIi6KB~,;_㐰4  ,f4$Ĉz-ylcgmemngYapiqq?krmrsp`urMwYu&9}y_w$ {ztkg&#lh:mjo/lSp{n7qpqsfr `Cut5Mvvk9Nyx#{b{ {jkFklx}lmҞ nQoXopq1rqrt-_tuMFvw9xy#{{‘hVs%yiskAtlunCvowp8qx_syLuz8w{#z}"fO{g{4i~{՛k|)Ol|~n|oLpx}W^Pr}L)t~*8w2~r$y~dMf:ꦕgyi)kx}m\n^obQ]qKs恛8ivs$Fy,S:bGepd"&e􏢗hgՎRiˍ{kԋln)\tpWJrӆ8u$xxkr`ˡKbdMf|1hzGj5klx[_op~Jr>7t눴$we_aczPengqxiמj2l8ZdnTIwqw7~trf$w䶗_27a FcCetg7px/it)ikڞZnizICq%7pt+$wT^a`~biUd9Sfͫwii\knYm I3p7sɏ)%vo>cpe-;qg%qiFrk|smou3p^vrLxu28yw${zn"fǵohp j>fqlr1n s[oojtq^vt*LKwvp8yx#{{Nm j.nlom}p>oqop~{rrQntt^!uuLwHw8ly6y#{R{#jrvl#sd3mdt5Inu+ov6}iqawGnrxg]etyKpvlz8!x~| #z}PKizjZ{k{Gm{en||Op|m.q} \s}Ju~7w~x$ z~MgQnh$j'ʖ^kmHV{3n(lEp[rJytr7v$Iyee)tfh?4i-kFymijoXZqj2Is7v$x͵-c۪ e-^fЛa*h$XjWwlDfi~nSYpIrڋ7Guh$xb|Jd#zeʧdgilPvjklh:mXoՕHjrC7t$wٲRbMcgedƏg)i:uk gm3Xo~H6qg6t$wuacSΛ2eُ2fĮ̂h~urjgjlǠXOoWH%qN7t;%4wqbƱyrdQs5f~sitkW|ummqvo\wr~K yu*7zx#|6{ pflqh=rJiskފYtm|7uomvq\wQt Jxv`7zEx#| {p1oj2pkqbm$5rLnsRpg{tarlus\&vuJ}xHw7zyy#{|mqۮ?nrآ\osptquzsvktlx[ruydIwsz7=yC|#{;}jkyxlzWnz]o?{ Ep{yq|$jsP|Zt}PIv}7x~k#z~j'k[ZlmoDxwpirGZ skIu;6w€$y瀑 gDiYjŐl$mvo5ZhpYrHftE6v؄$MyаfdgNiM,j˜ZlZu>ng;oXq׎bGsv6v-I$xꮖejfөhI}ịdkt$sm=fo'Wq+GsT6?u$x'e/fqg?\ihkszoXzrCG{pt5h|tw"}{vWeЧvgviwkhx'mVv:xoPgyqjX*z>sG@{v5:|2{I" |cX rŘsntX=tߓu&uiyv]wpOuxj@yv{1z"P{L^q~rs:~tstHh4u˘a[vŕCNwΑ@[x 1z"p{Fqq7r+]r}ss!tgutS[vrNEw}@ x"1yɋ{"z q4q٭ru}sU&rt+gVu[=vNw@x21y`"zڄaٔcكf.htlj[i-l\o)NZq?GWt/Awa?{HefߊRThjtlhȁn\4XpN-s?"u/ xSl{ShNik+bmt5nh\p[ـrMt?v/y1{ћzn Jpl"q~rBssg|t[vgMdw>yw/"{8X}~v~v/~wL|~wr ~xf~yZi~zL~{t>|1|g/k}o ~:}}b}f}Å}X}{}t~ q}~Ee}~Y}~Lw~,>R~]s/~ s~ {\E{{y{soo| de|\5X|K|=}^/} ~*z)z:Pznx4zWm{c{V/Wn{wJ|!=3|k/N}3 }yyQiyyvy(lzGazV~{MI{$<| / |,!}u6xFyyOwv&ylyaqzXVzǖI{?<[{.|:}!+|{x#)x>yuyKkya y UzjTIrzݕ<9{X^.{ы!L|Bya󄻍c{茰erqghxj>^lRfo%F`?q9t+_w={Fi$dĄAf{hYrj1h l2]ʼnnPRpF5s8u+^xhX{}ҍ6g5i\{=jqalg{nM]q}p5RfyrEF ytg8߅uv+ely0zg{imǂnypp&qafSr\kt?QՆ}uEwr8y(+}{҂}BQtAarux׈uovfw[KxQ5qyEIz8{+ }'#%~| {N<"{wćL{n|+e4|[D:}Po}D~18p~+GKp{#~fcv,m8 cꄊxZ$O6DC^8+XUۀʄI,|/t|kփbnYߋwN͂R!C̈7B+=pŜ1{3/s(j%aX.NUB37)+Q /ZցlQzӟerJGjЛaQWۖMbBB6Z+=A `P!yr>it`Wd| MxB}}#6"+AOZ~{ub;t3d@5rI2u0':xvn{{ =dsݘ`fl2hdcj>[\lERpnxHp>,sT2u'QAx|zgs|i#kjc.l`[mn4R9p4Hyr_>ht2?v'dy|Xymr[nJkocpZFrTQisH]uu=܋}wb2vy4'>{D}mx䖭rqǔsj:Ntb=uYݐvQ,FwGdy'=zl2{'y}3i?~wuyDpēyiQzaizY({>P-{Gk[|=\}2~T'ׅ(Ѓ~ v"o`h!q`_XCspOϋтeF֊Z]7jV%lzNǛpnZG@pm?Eor6w;u -!w#Ez+9|iXplc?m\do-UpNnIr&Fls?eu6dFw-8y#~{}hqb r\$sU5uNv3FIw>דOx6QJze-L{$&}{ hYwbRx_[xTwyMzfFE/{E>@|466R}=-\8~E$bIbAUfraZśE:6t-s$8f t`;2 ZwSVLl:bE^=45 -v $نփ>oe8:n_jMY6ِRKՔ;Dڑi="5Lk-SL$)ՄdƜ–v_Xݘ`RP 0KĐDS=_E5;-H$φqdbR^[X斶RKjD>DّT=KN@5q-Mm*%YcJWpC0r6=}s6ߚu/jw(L y V{1k~\r2VFrPNZsJDtCv=jQwg6љgx/˕Zzv(q | i}>[«wTVwOx`IУ yCz=D+z6K{/ӔO}(~F!3]솻r[SUETOaPIbCP2= 6x/(ڎM!Nl[PUTOGTI8C؅`sb-t_d[uXfvdspi wieUkxyUnfyEyqiz3t{!x/}8[{G]{q`.{ȋb|#ke|rrg|djT}TUVmE}E po~'3s~k"w{~Xˠ[?0']`1}bUpecFhTbkȄ*Dro)C3r̂8"Vv Vd-Y [^^Q{a7okd,bgQSxjCn<3r"v e\T !WbZG/]zm` n!c`f`Riޑ_Ccm3Zq"uɅ^T>rVY¥\y_mb#`elRii~_CBm?3ZqQ*"u SVVYQ2\";y_mXb2`]eRIiCBlؑ3vpT#!uFh[i]#j`<lEbhmetoFheqkoVbrnEu,q4wuy!zmy"g!^hr`sic>zk:elh>sn}jenp[mPV rlp5Ets63wMvL!z+yeb\g#dchfgj/hFkjs"mmeoo`UqqESt:t3vwM!yz3c j"Cdk+f}muh?njpIr$lqd:nIsUpuDs6w3vy!y){`rbwsdbsfSt}hav q)jwcnlx@Txory|Djr1z3u0{!xh}I4^Vz`W{'bp{Hd{|f|p/i|bk} SnT}Dq9~3gt[~w"w~ꦺ[ל]Z_цb;A{ dng8%aniRlۃC~o3TsM0"v؁A=Y3`[n]`plyhcmCe*`Gh\RkaBo 53#r"vLqgWZF9\a_-lwaۙld _=g QUjB{na23ra"uYWY>\!r^wqa`xkdB.^gRQjB\n r3qƊ#uVSbY.][7`^|ww2vy!y{ϦcWqzer$fshvt{jSunlPvaJnuwRpyHC#syiy kGz smzhPn{G[p{NRr|?t}s0w4~,!(y~degA]}5i r]kfleZo MqJ?`s30v-!xځ4aޒced{g]pio?ekYmڊfLp9>r0ur)!xD9b`l zb@d!zf0ohYdj=XlL ox>lr0st`" wՄ_auc@yeog dj.(XmlKo>Hq0rt".w_zCaR| c:y#eInguciX;l.Kn>BqX}0t7"fw0v)Z{v]0v_|wyboqxe6f xh Yyk L ztnE=p{vq.p|us~y0t]Ɛu[`)^ub~{vdqwSgbexiY^xlKyo=E{r.k|fv'}y٘sa6^t5cIteW{fugupvie(wflXxanKypq5=%zs.j| w1}zcq5hri@sk%zsloptndxCwp~xmqybs)z]Vt{+J!v*|SpAGrA>:t]3.(v!*x셣k|Zy|]"~ |_te|bIj8}e _}WgT(}jGC~ n,9~q+1uny{~]M{_}{b@s| di|Yg_-|iS}lG }ox9q~(r+~vyՎ zb`zb|zds`{1giI{iZ^|kSz|nNF}p9[}s+~vzSx'g*Cxh{yj~r5yl6hKzn]zpR{JrFe|t495|vs+}x~{8un4(vorzwpqIwqg~xs3]7y:tR7zv)F zw9{y`+|{2g~5}%sutvayuZwp[v(wfwx\wyxQxzjEy{b8z|a, |!}v}`~qzrLws-nt$e1u+[@v2PwIDx8ỳ,{ |of}ptuqt.lrUcswZt∉Ov!D;w{f8x%+z^[{ꂉ1n)n|o4tlpLbkqubrY&s!NuYCvƋ7xDP+yφ{im{nsoؚ&kSq$bXr@XsNt3CtvmW7w<+y { m{n9sorKkpbqҙXs)NtC[v7ww+yz6ŇZxR}]=pY_gӅyb_^؅eUxhKpk @ n`33q'uy)]qwԅ~_o b&gsd{^RfU,iK1l?&o3r( vC]z~`rwJ~bo$dgf^iTكCkJn*?Âp3is(2wz}}\fnv'hnhifSk]S+mqT+o~Jaq?ns3ЁvH(4mxVk{{ ltn mvo[e=p\r3SsI|uy?'w63y(O{} z6sstlluAd`v*[2w+R8x?IaByf>tz3{(k})~yu|}r[|~!j|~Xb}3~Z}}{~Q}Hq}^>B~-3p~z(~/l {wzpzgi_{Ea{GY{߆P|9&G|=} 3-}(} ~T#uycaolyRhUz`zlXz׎P>{OGF{͋~=X|N2|ԇ(f}Q}ʃujxјHny*gys`IyXZzd+OzSG{oP=,{2|zj(_}}|'txV~nxgy!;`y`X"yaOzu1F{={2| (j|}o6[8iZ]b(`:ZώbSeK9h{Bk8ʼnZn.jr$PVvm"zn鑦]he` a,bWZdRߌg8J拜iBdCl8o.q}s9$hv?zn`\h[pbaDdZJFfR4iJkB:nd8cq6.vt.$w}iIzmegsg`ˌiAYkR m$J8oQA݈|q8wUt.~$vw$y2|lkyf l_n`X݊5oQjMqIEs>As0u8F4w.-y$݄{*}ikBqeWr_!nsXtPÇvI%wQA x8z".={% (}$s~jQ{$dh{^U| W*|O} H6}@d~17σ~.Z%O hc@9]VHjO>r7Gу@&7^.q%d#naCggb@\ \U܌NX*G`ڊ(?T 77LJ.:08%f|/gpђaՄQ[ÃؐU5ZN^ێoG0i?7e.'Ї%^&jg Oaҕ[\7UےN:[MG?7.&[%s_s[Zx^@TS_`N ^cDGEf@ѓi9~lR1ʎo)lXs{!wY{b_O^Y`ZT1bM闙dGbg@Qje9mn1̎{p)t !*?w憁{\_ {`YŚbTdMfG:iL@k9Tn1̍q)xt!Hx*,{^neYg9Sk!hMDVjFޓtm @VVoU9fq1tJ){v!yle|]ޘjX8l-RؕmLœoCFuq @r91t1w)̉tyE!ć {τ}]@pXWוqpRBrLBQsFu-?v8x31y)s{"%}W1\ xWfyiQz KVzE{?\|V8ŋ}/1~*"]["V?Pߐ+KE 4>8͂1'*0%"*فZ}9UPN-J]D>k~8OԆ1m*($"Af>Z(ÎUItiP #JgŋDY>8;a 1d*)Y"‚lFYːjU֐O؍JC0D|č>]W891k_*89"qX\HS^N0`H/cB&]fT< i[5l.p/'usw"{X^tSo`MVbHe-Bg;j5m.p'4towJ_{Xm`SLbMҝxdGIgAio;Il5vn.r'Hu3ΊRxss{WϠ)e|Rg0MUhGjAm;od5q2q.ltr'lw( hyˆ3|WmNjRN[kLVmlG:BoAep;r5St.?w"'nyf On{,T}VoQpLjrFՖskAt;L(:!eT_Op}JĒˊE~,@7:U4ɋS.(F}!$uӁT_:OǎvJ*Eek@:4ъΉ.ˆsl([!;BS}\M^HVa3Cc=tf7Қi1sl+pm%Ut03rwr{R^M`H|bCeU=j:g7К%j1m+tq+%tQx"{RڦaMѤcHdB%g =X}i7ƙtl417o+r,%6\u`kxۇ{RKeMWfg1ВXw+Ny%{.}u؅)7PwL-x!GxA蘜y3&}> ;=%HOu|J܌F-A1:<Ԋ|7 1N,Py&~M _ƄEX_MS`VbY}d\wf`?k&ic]kgOXnnj@qn/u rmxw>]WD_[ZaC\ʂc]_w ebjh e]tjiOml?pp/ts`xw![[]]U_`,bbvwdze^j/gh&] ikNln;?pgqy/t&t`xCx\uXfb_Zd]f_huVb>ji:em\Hh oeN/kq?!o1tk/s'w~wqyfUbjWlfZam]otH`phRcr=[fmsMju>mw/ir$yv{Rrs&UtWt~gZus@]vgraEwZdxMhy>klz/dq.|u}I$NOQCT+|W#q[V&f^YbπL[f>kf/`p  _tK(NȍR%zUp'YJd](XaDKee=j\/]oI ttKIq-1LϙpPPySnWʓc[ґX#`2KBd=i/tnÇ t, HġQL!,Oy SPnW3c[G~W_K'dT=i?!/n!,t˕8HIBK OoxRĞnKVcnZŘW_#K'c=hٍ/n2!msŅ3aS)c3VdYfy\uh^`?hjnc\lg)Moak ?r@o /xu{s-lywM_W3faYcP\e3_tg@bhixe[kiMnl>qp4/Zusfxxn^9Z_]gDa_icbtfeChhzh[MkkM^mn;>q#q/6ots/sw5wz'Wj'RZ.k^\m-|_nqapWfAdrYgsLJku=nw/ ryv{јUrWsZtd{\uRp_vXecbw_Y"f$xuKiy=mz/q| v }wQ;bT }VyYoI] d`X$d'Kh =?l4/p u(ߔ`N=QH6Tl'xIW8m[Jb^FW?b1J}fąmq=pth.ntiwAqx!zNZi\kJ&^lya2n\oEcpcf]qWiAsJlhu|eIIiek<-mI.wq\ zuQ ˈ7S /VAuYck]]#``Uad=Ih;l>_.p9 uN[Q}U_t7XKjFAlmx6@pԉ*tItwڃ~YȜwx\"o^;gNa?^cΕ!UfKiƐyAom6Vpj+ sꇨws!u"Sz uVqv(Yhv\_ws` Vx=clKy&g @XzQj4&{n(}sg~x1QsVyftUYqu\Bhcu__>vbUwre4Kxzh@yl4{3o(|t~xvvraYxs\ups^gta^udDUIvg-K0wjK?y(m3zq (|ct~Exo`[wJpbdo)qdcfrf]shTntkaJ~vWn?|wp3ys(({va}zL}lgvmhn%o,j]epul\qmSsIoItr?*vt;3xv(Gzy|{|mj neukwom-lpdnnq\psuew3wy(ry{^,{}gztgy7s4hxykjz+cqkzZm{:Ro{Hq|p>ds}$3v(}(x~{xdqe܄ojgb)iYkyQ/mڃ)Gp=r3Yu(wzi_w+b pHd hf a-hY jBPlZG|o=q30t])(w yvaaocThedB`guXiKPPlGWn=q63*t(vͅAyvc`școbh5dԖ%`fjXiP9kGMn =p239s(visySslT5lS6W'e)Z]d)]UR:`CLYcCgI9k,/>LoN$sByxr}Vk}Yd~ \V]~,_T~ZbL~eDC~h9QlJ/6p!$ĀvtP\9xr%|Yke|\Od4|^\})a[T}sd$LJ}gCr~/jD9~m/.^q/$πuzy3qy_jMzFac*zc[{-fS{h|K|/k B|m9J}~p/"~Ss$2vĀ2zoowzei8wgbMxi`Zy&k4Sym-KzoCB{Lq9|9s/}=v`%~Hy l{nu#l{h/umalvho(Zw4pRjxrJxsByuX8zw7/|0y)%J}Z{5~}glrvfrwL`swXtxQ`uyzIvzOAmx*{38{yp|0/z}3%x|1~9}`kboNFe^pK4^qaWrPsI u,@v8UPGXbK[?FT^B@Wa:u@e45h-ql&>"q5\u؅zQU WP|&ZhKp]Ed_@_.Eɏna@d:Lg4!;k3-tn&cr3vq)z|TM^Oa.JˎclEge?ˌ^hm:k;4n8-ngqQ&trw+{}SĎdGO-f&JBhDj$?olc9Ӊn3݈Qq\-pt&vy߃M|SiNkOIlDn?p{9r{3t-rv&҄y[{3~~!Qr MĈs(I%tBCbum>v9Gx3y~-${'(| _@~7ȁ]Q*zMq{5H{C>|M>h|9}3yv~Y-&'C Is2P4LHC7>(83o5-'d 怇vsP4RLGGEOC ʆ> X83r3-'" ,TyÕLGł5B>P8Q3"-ǀO'U!)"YNۛ UJ;XEZ[@ە^;a6Fef0i2*_m`$KqĈvi~zNXJtZEҖW]Y@Ɣ`/;cG6Bf0j7*n!$ar,݈dvL+zNZUJN\EO_Z@a;xd66+h0=k{**o$$vrvzN0K_Ia8EWFcm@de;E>h6k^0nh*q$t~KL*q;HJrjDs?Nt:zv<5Mw0uy>+z%M|4~BHKxyiG݋vzCz?{f:W|)5|}0|`}++~%~UBKgzAG{lCK>Ƈ:,ׁ5r0} C+Jb%ǂÂiЁ0KG@C#ӄ>:5nE0pW+^d%M^ h_JÇG ᇶC x>1::P5x0φ2+΅&Ą 'IV5ExkXAD[ZiD7j?\lQ;zn6-p2[_r;-rty(Sw#Ey{5~\GVmpCq?s;KPtp6ԍu2]:wp-y (ʈz#~|~EG&xzCwy2?zsy;.(z6ȋ{2gh|v-͈}d) ~$3t7"؀GC=7?0r:L6 2XҁX-Շ)2$K7΁fJFÍmCt?t:u6=2Z-߃)KV$oOނmy,Ft[B9?Ն :ۉ6r2gY-2k)m$5qB\!P`(]S_V}aZ/qd#]efa[Yi[e*K@liW

jWem^J-iep!;ms-Wquvy8QgHSh΃Vtj|yAYsl4ns\n c`oVcqIgs;l7v"-FpxruzMoPp=SqxVrmZStb8^uQV6bvI!fWw;Cjyg-Eozt|I||]L|>P|vZS}kWr}]`[}}UN_}Hd{~;ix~W-[n~t#"?EއI.~M=wtQjU-_Y.T^PH c]:h_Q-mm[soC:G>6}1K$sO3iS}b_!XS\׌Gbz:g0-m[ >slwJBFm|J\ssMߚiRD`^V䕵S[ҒGaC;8fҌ%.lՈz s^Pe` SaVzKcZ4o>e]cgabWcje7Imig;pm-FtXrsxZvԗ\S^JV)`$Yyb4\nd{`'c:fcWig Ilj;p+n-.sroxwIZWJ\ZJo^\y `_n-c9bbeeVhi I`kl;dop)-siszwwW^Y@a,[cXw^em`hacjUfmCHjhp;nFs ,r`v$vyz SfVhvXj-v[hkl#^^ma aoU;e qHahs:lv",qTxu{*TP^nSp,PUqVuXr}k2\s`>_uTcPvaGgfw:lkyX,pY{0u8|֏L({O,{}pRW|EsU|iY=|_]})Sa'}G]e}:DjF~5-o(~tSGHքKA{O}r\S"huVP^ [R_|Fd/:i--+n^B sсzF_6IzzMfq/Q9gpUI]EYRs^2Fc':+hY-gmυ ysoEG4HۙzLpP~gT] XRV]EFb:Jg-m sn)D]H9)yK pOfT ^\XrR\](FbN:yg-mD5 s5aPm^baScVwYeZ4lg}]aaiaaUnke:HYnig:qm,ur:9xw _Sފ`VҀb]Yvd6\lfI``hcvUkgHnj:iq*n,vts;xwy]9Ws^Z!J`\v1b_keb`geTj1hGm=l}::pp&,at*sJx)xY^A[`~]c u`'epjbg_ejjmT hem G]ko9oPs,Us(v0wZy\VOfQXh}[is]ki`Lm^cRorS^fqFj+s9nv,Mr"xv{JS#nv/Uo|3X[pr[rh^ s^^ aVtRdvFphw9bly=,Nq){u|Nz6Q{1zqT{qW{gC[@|R\^|Qb}Ef}9?k]~,}o~tTK}Ɂ{NOxQoU^-fY [\Q,aKE{e9jC,o)tQzHLNwOّnSseWP[/[oP_ЋNE?dm9-iph,nb `tXmH ~KxBw On!RdVZZѐP_EE8d9Hi},nX sGh*JӜvNjmRdVRZZ?P^JEOcW9yh1-7n s{ePfSzh=Vq6iZ8gkj]\ym;aUQ3oDe/DqiT7t0m*wrOczJwDd&SuefVz[fYpht\fjE_\l=cPPnofDpj7sn*vs kzwCbpWXcZye]\p4g#_qf+ibc[k8eyPmhDhp+lL7sp*v6s|yx;_^sb7 pu*tBxxx{ZXsm|~Znv\ol_q.cUbVrYQeEsNhjuCkw+6ox*sMzbwT|VTy1|Wyt>YzAkX\zb_{8XBb{Mfa|HBj|6n }*r~ZvqIhQ-zTfrViZ$`]~&WUa M4d̃4Bm|(3q}(t~xh"zWsYTk\c_j["b^yRBe|4Hhʁ>lH3oo)s΁7,wˁywTrVWjZb]ZW`QcHgg]>kk> 3o0)4s7.weyT-2qVjOYʑ|bb\ǐ Z_QtcZHJfX>djȉ3nӇ)Trw.txSqV@"jY lb3\Y_MQfbHLfj>xjM4%ni-)rv{qQsr'T#krW#c^sZZZt]QuaaGve>}z\~r^RGk`r db]eU gLj8DJm:p1s+'vbyqC\'j^`Fc`ŠW\3c?yTte䈖LohCk†:n1rb'2uƃeyJrp[;jE]c3_[b}T>xo6z r^,{u.#}}x(V~{]hp ibpk \?rl|UBs7nMtxoFxuq>w$s5xu,znw#|&zV~|gelsdaomtSZnu-T1pcvM qwEsx+> u4yQ5wz,x{#z}^D}~ei}`7j~ZlU~S`m~LL]o~EEq~=s5u{,w$0z,|Pdg;_AhÇfY#jhRl%IKnDp@=`rH5ct$,v$Zya{ڂ d~f[r^g틯XiR_keKmJ!DonG=RqV5dtT-v4$my{d$eH^g<\XhOR;j:KlDnʊ=Yq5vs@-#v)$x+{D2_`RLYcU4S[X&M&[NF^?2bY8ŃfB0ビj(^o `5sC6x^ TYW`SUZ+L]%Fo`]?[c8#gz0҂kx(o pt1cx^WXXYR\bL_0F.b:?{ev8]h0‚Rl(Yp ftyA]G\zW耼^RaLcEf??&i87l30oo(4r ovفzc\;~]aW~MdQ~YfKz~h>E.~j>~m79o0rx(Ԁur nx:{[b{hVY{iP|k^J|kmD|o>p}=q7}s*0~>u(~x!:yz2}FZ7xKqUQxrAtvyP7U L. WGkZBH]<a 7td1thX+i4lz$؇pu3yPMWbKZG6\B#_<ϊb7_f1i+lm$sq9duńkz OV\)Kz\^F׊|aAʉcgWFik9Nm=n+qrvw/Q \_$Sy^~VatXcvj \$f ^_hS:c8kFig[n8kq+]puuxMd*Of }[RgswUiiYhk^-]"mRa,pEero8j}t+UowuzbIPly5L[m| Oo-riSpvh@Vq]pZsHQ_=tEd vi8^i0x+bnzWt2|#Dy 2Hyz@KzpOz~fSz\WX{jQ1\{E b |r8Bg|+mK}sZ~t@5JDuxHzko`LeQ'a[lUP[ 5D`85fm+lfAr&=ǒ AՑwF`nYJdOEZTIP7YUD_y8pe,kr<˘h@ޖwE5\nIdNZSP.YD_>8e9,HkɆMr<%<@!vDmIpdgM5ZSPIXUD^u8d܊,kk !r0[M/];Q ~^TWtv`Wic4[^e_rS hkcF1kg8o:l|+Rs*q)wv1YQ0p[MTE}]7WSs_hZiVa]^TdaRgseTEjin8nm+:rqwAvWsTԆY^W}8[{Zvs;]]ph`z`]cPcRNfsgREjk8jmn+r4rvwKSz\6U^{X.`r ZcLg]e] `hQdvkE)h]n8 lq+q#u0vx܌OcكR1ezTgpWif[k\?^mPbsoDfr[7kDt*p wu4zwKlNmfyGQnoUpfXq[\irPc`tDNevE7ix+nzutd|RVGWxP*JxwMynOQydUtz|ZwY{O^6{Cc"|47hb|+?m}s~ɆcCHO~vFvJmN'cRYW|iBCtEFhkK ^bVO;XTNY㏜C_8;e,Kk( Qr^MDŽ_wQ%{aTbqbWgCd[\gB_sQ icDlg7p5l*sqIkxvd\ Q*]T?z_oWDqaiZfc]\f aPheNDilif7{om*ssrkwvԌZTV[Wz(]ZMps_]UfNbL`z[dcPrggBD&kMk7Hnn*jrssw_wtWV \ X%^`xZr`oT\ce[_eZbhyOekeCin}6mq*\qu;vxxR`cm>^sSف=ByEٔqIՓ'iM쑁` RPVWL\6B8ap$6g+sms:AVyeEqeI:hM:F` Q=VVg$M [xBa`Q7Af+l 1s "bN}d QQtehTkgWah[Wj_pLmcAFog4rl(uq_zyvo`QW}bPT[tZcWTkeZasg]W`iajLl7e5A oiB4r"m(uryDv_T|z`WsbEZLjd,]2afN`KWhcLikEg@n>j4qn(usxw0[4[{&]]r_`>iaMb`3ce^VTfuh!Kick@gln=4epFq(t u*x*yWbyYdqs[fh^vh_CaAjUd?lK=g|oW?kq49nt(rw wWzƀ TjxgVYlpLXmjg[n^`^pkTb!rJes?iu4mw(ryv|s~Ov5vREvnU6wfXpx:],[xS_yJcyzo?Kg{I4"l+|A) p}n u~|5K낉tNYmAR8"dU\)YS]qIva7?f=43j)Mou {@Hَ0sL9?l.OɌAcS*[jWR[ĈI$`J?es4hj$)o_t؂zGړsyK=iWMdjpZq[l]R5maSHFoe=Tr\i-1um&wrT{7w |dTu eWclgZdi\[j`QlccGo f=qj1tn&wrjzw{S`[Dsb]kmdv_cf`bJZ3h{dQ jgGWmCjh2wOaxTRsZxUjSy-XL~y\;DzL_<{d3|#hp*}`m#!~rNwtgvwR(a9vTZdw|WSjwZL"x^ Dy`ag:]lhWmjEPoflIpmBro;7t~q3%vtd*xv"-zy}m|wbkgQp\hqVujlrOl/sInuB2pvW:rLw3tyK*w(z"}y|k|q~|ad{C[e{vUgv{O7i{{Hk|`An|:p}Z3s$~+ u~"xp{ǀ0`&a}ZcCTe? NgfHimAzlO(:so3q+0uj# x&{Z_`XoZfb@Td[ NefbGh况Agk:mnoq3 q|+Kt#*wڃ)?{#M__f6Z6ah]TZcnNKeNJGh&Alj到:|m҇3*p+stE#Yw,kzۂZePTꂆSO ZV I#YhB\<`5́d.viV&ʁn(sxnYQRmTU@N XH[94t6.vO,(.x!;zs|邬LPHSDW?"ZX: ]4ֈa/Of)j#iou+te'yLx3RH[BUCՊpX>[9_o4ɇc/Mg)1kq#{mo2tJ5yzL+UڈZ]9هa4d/GAhf)l#pLJu$iyKYG0\CT_*>a9zd4h /9kp)o#rEvzJDž_)GaxB݄c>/f49Qeh4c%k/'n)q#Hu:ɂ2x?|%JudFufBoh=ׁ{j9]l4=NoE/Fq)t$w(zeh}Ii~lE~^n@A~Yo=}~iq]8~s4&~t/* v))x$yV{}N>H{8u'Ek{Nv&A{w=@{x8|y4|zI//}{*}|$~~I~bTH!pKtWz FXSBא V >Y*:\Z5O_1X'c,ig'Xk"Ipbu&yF.UUBX1>[&:wh^(5Faq1U9e,r%h'el"qC|uDzEލYBb\>_D:Ib 5҉0e 1JWhN,{zk'zsol"Bcs@w~{EE^AƉ_aS>)c9f35h1(vk,tn'r#"q uzxƃ|sDd!ALf==Æ(hf9j~5dl1oC,u&q'pt"wBfz&$}D k@Mm=poX9mp5D?r1t,v'tx# {=}KiC|t @7u'=;vI9Nw[5;x1y,{2(>|#z~>0j=C3}{@?}|\<}}9#}}57}~E1)}~,~B(}~w[#~v2R]C|5@-|<|#9|{53|Հ17}P-}(}؂$~?W~ۂ6wC{2@{Ԃ<{ڂ9{Ճ5/{@1R|Qj-+|˃(}8$T}~q BB Q)>jgTH:铨W6ϒ[2}^.Xb)ލCf%XFk zpuS zLAS/>?V3:͒~Yi6đ\2~`.ec)|g%ol^ ňpJuqzA*^Uh> XS:L[^6^`2ua.meL*i&%mU ؈DqƆuXzFAeY= \:_[6b(2ae2.w6h*l %yo!swYO{:@e^=nsa>:1Zc6ZXf62ePh.^Sk*Kn%†ra!1u3y/|?cIl.NzoF*#q%ru!jAx+w|{݂}?QkVx.y}*gz&|"S~<fmÀ倕?zx?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~mft2   )2 9"=$?&@(?*>,=.:08253456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~RNK􆞇H񈛉EB쌗A뎖@됖B풙E򔟕MZ mjik¡nţqɥu!ͧy%ѩ})ի-ح0ۯ2ݱ3ݳ:鵘FNRXdq|)ă/Ƅ-}#m˳T'Α_"Հ6ٍ5݇#oIZ{24"o ]  $, 2"6$8&8(7*5,3.202223456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~G삒8߄.׆)ӈ}(ӊ*׌0ݎ7吓AJT_mfb b dgj§nƩrʫv"ϭ{'ӯ+ر0ܳ2ܵ0ڷ0ܹ5㻒B򽢾Rds",Ƅ/ȁ(r˶V+ΡсQճlgݴGPb(AA(r \  !#%')+. 02*3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~G삒8ބ-Ն}&ψy#͊x#Όz&Ҏ~+ؐ3<ꔙHWl]TRTY_ elƫs ͭz&ӯ,ر1ݳ6㵏;緑<湐;滒?콛JWdnsqg ˰S'ΎVҶp"x r`DJY a a[V   !"#$%&'()*+,-./01 2)334,5&6#7!8!9!:#;%<(=+>/?2@6A9B;C>D@EAFCGDHFIGJIKJLLMJNEO@P;Q7R3S/T,U)V'W%X$Y$Z$[$\&](^+_/`4a:b@cHdQeZffg]hViOjJkFlCmBnAoApAqCrEsHtKuOvSwXx]ybzg{l|q}u~z~}xrmga\VQMIFDCCDFINT[do{|tojfdbbbdfilqu{ÜěŜƞǡȦɬʴ˿ζϮЩѥңӢԢգ֥רث٭ڰ۲ܵݸ޻߾ r]XcqVހ6P01 I/Lj´crBqƀVڀ6Yۀrؑ5$"|NSbpiUs-5Ɓ{/~wB~g}Q~ib~oذ~}~̇A~o(~T;'4];l}} }~+}!}S1}}ʍ ~}}m~BS%~42~چ8|ߏ|lE||ȗz|}ԃ}G|l}R#}e3~Re~}{̭{Ԫ.˱{ئ{?| Ŗ"|8I||j|ؔP}?2} ~&!{T؎{Nkȷ{K{VFd{q{{i|N@O|p2}0#}Ĉ{q/{R{ A{@{){[u}{g{N|LF1y|ؔ}{>zpzzͣj{C{5}'{}]f{CN?|1[|}ZL{zuz.zVzGz|{C*fd{u+N#{ 1i|S}r~~Ӯx~b~^J~,.~2~`.@˄ż#gC~|~wd~tb~+eJ`}m.}ߔ}Q׻eAŚu~ɾ~s v~6a}CJ5}w.}#}<|h$P|XƖB|N{|C|^L|}}Xfe}M#~/f S֊Csű+ڈ~=]~~|i'e΄oL/I. UˊH ӊe)f::Ć;{te- Lʁ. ҂߇LСMD_N5EpzDͅ/cvJX.; 0;'-2rA܌+xQjbJӆ-2 >2ʷݼNͬǛ>͊WwڎaIGD-Gxv zՖ秀Ǜ邏 5AuØu_SSH,Ɋ `uK7񄲪σvă;Fs&^cә*F , Q ?ϾNp3L˴QڄpՃ1q8q] s.Ep+~: [Z̭]Eǚ,8ZS1p܂ \CwE+Tȓ +m>b:͛a,oOpdʮ \$?Eg4+X~ @~ኛ{5ɦ0{A{Gύ{^[{c|u|_V}aGQ~ +AL ;~fЏ~5i~;}ܙU }݇~t~l_/~F҆4*܅A MɀҐǁX;өy@6sFH^rTFTj*# %҄"pĻӇ_Y떡-3ir7]LEe2 ) `ϚOKċӋ{siq%?\\"+D)@ DᕿۢTފ"9oÇŌZvC݅)5 <]ɩp:㮝9󊦜vdlL%%?~ܖX%ΓE )@_(yIҍDgSԊ'>6B$bʼ,xBRj啱XݗwM#Lf.aR{=$oj_̯z˓ , n7;Ԏjudd=Qx< #އ= הG%$̎EsubъPD;_#UeޔA["X68r\bVOx*:#!h|*T0-33:5ar 4aLqOV:WNN#"iekyyRyMz6~ܚ;zo{5_{L[|7O}+]~Ov|P|QA|G|^~]|o9e|^}jK}6~Re6C?S9~Č.L~}~nɗ-~^=vKT6xċ5$08FZЄiǛ|Wm]MJґ$K6|ӁJ6{^Bl{L݇lC\PXJ5NN5wѠL˚Kzdk_ˋ[X\I-!5a.؆[1fZx%ĕi#YkH%q14PL5ƅ M [IU_wRΚ-vYYhXwG3wϏc:/)vB]厁"t!fjjWAEЎ\2s!"ԇΤlqwWxHׯs :eH~VavwEKn2?BE̢ȟMY̀cseUޑ@#(}'-f~*OyȝG|T;|F|@zV|fmR|_}P-}? ~8-/~tð~­~">~y~mS~_V~Pc?u,w̎iY;to6̃cy_l/ނ^DpO͛|U?.e-, #*j3Yw釴k9d]ވXO>,Pzn ]3SҬDrvޥtjE싕\ONbq>#V6,N@z3ڌ!Ř buPܔhݡD[MXӏ=Y+$' ÔȊWvn^sˤugwКZj$L@Q|pAdȠWLJ$^+:ה)&)K{ c{t){,iJ{n]z^{P|tCx}54]|~!#'|Ȁ[D}Y~}:t7}i}0]K }pPϨ}CO~_4GU #˜mvi~xs,hѱM]+PC]C$d4 !!#byqf}9s;h*ǃ\SP!Bӂ3❢#t eavg :|ָɉ>r{@gDu[φO/;B^K35#ThpZqȼ| NqôHfŒ[iIO-A3Xi#;hz﷢p\ eʮGZlz{NUˏASގ2盈#ۊ׏݇yϷ* okԝzdhYeژRMm%@2(2]ܐK"ʕ/E0S 3xϩnZuczX\VL?؟31ӚKc"]{ʁx$3mٲLc ŦWGLV?Y1D"{]h:&LwrmnbWХKʤ?I1t"Y(%{U|vw| l.h{ad|'V|J$}=L\}.n~<1~=vO~ k}a7s}U~J~`=0$~.EFhv M1k`%U̱HI٬>=ހ&.סjvjw`T#I)d<.݅`>Γ 2}DtHj2E_pTHުv─Ѫ`ŀKE(+a.C)mlkR3I , 6 {^߁ρgҁqUāWkԁ;Qu܁ 2ӀdC( fj8P_Z2zC~~| ~eu~YU~X{~\2!~li9~O]~χ1q~UcH/}ӛ[֕}<}}}ɒ}~}Ďg}Ns~8R0~sf~ʄ|究|צÈ|ɣV|^D|7)|Ϛ|c|fC}2GM7}v0L}c~~'=|^Ϟ|=j|'I|||38z_|cd|K|$/}DO{}ۦ|*j8{^2{ؿ3O{Źݝ{ĴV{ޮx|c$|=K|/|~}uK| N{dK{ͭm{{!{w{b|FJ|>Z.|}E#{]ˣ{і{˚{tW'{hÊ{xwy{b{$J{i.|^|{ݱ{<{һ܂{Щv|V2|},j-}PD4~i2)nT˃  j~ɻZ~~s~恌~iP@Oˁ 1F(D2ιˁHӁ́zހ=iO-ր1- |טˆ߁r߷.0 ߀ʅ~gɀ|QN!^0w8 +wuԄ!ʎ+`J̑}afވM0φ/& !/՚yx%b5A3(x{mY,eXFLQ;/s.z ;q,*8VB#~=k~œ2~+yS~c~K!~}.~| ~oػyʡ~b~g~%ip~ wX}b}I}.9~ ~)f<>)E~Y~ph.~=}Ҳ݈}Su}o`}H}-} }ފa%΅f~I~PP}=}P}-t}v`}ZTH}M-}mA } ~`$~(ɗ}Äc}v1}Imt}}3<_})Hp|-}cX}\zNوzv¢z;zzj{zv&|Cd7`} KD}.<~ R)"}\}҇|}tO}[?}\}yۄ~c'~Jg-͂ #ćnZuތ:y-c#J/>G-l ݀vIԈ1̼bG-! Tw҃a݃dI4σ,Ђ$ Vv@_yhvFgY`Շ6HU@,\ نʗׅA b4gntł_KG%+ π1/0î:zΦcΝU0/Zer ~]oFr +ua υoаVDٓ pρ\a;EKsi*, smb<|hࡓMLo-7/[(DK*H %"yW To.~#Ѻő$+bn3ZzkhCכ*T< E~+L>W˃iũ }΀O|n Z %C*X ~\Әy0őyefyjMyؖ4z> zr{]q||EO}j)ۄ~ j؏w|hǎ;|_3 |V|]I||r"}_\v}E 9~)uI py^8~h|q_=\l=uD )+À& R4ΰGh( ϋdÉقψzp=σ*[FCa(a @9T˷yqNn.d -X7n܇LRZ,4RBJ(R܄7 b IڮI*}؎ҍXmʋYB?Z'Rm 3ŇRu;Tkډ2׍?}ck W.A0э"'| ˆ[֋)橺W)C{KiͅV2u@S0&$V ҁ ~DK<3BYyoEh&9Tτ=?*&\Ȑ ܀-0|]/Of*l9x|gu T

Ùn&.! {vmƊnCć Nx]f̩SՃ>jyL&'E3 E*Zx;0Axxٜ y7\y|4zfjS{:V(6|?\X}%*\~]ԅoAă{R[{X={s\{ܐ{{y|Niˌ|U}o>B~$݇Nnâ(~z~J ~2a~B~ {;~RiG~U3~>D$,Tִ4G"rڐ5Uyh]T-o=Ј$5Ɂt!xƥ ɗOψ x=/#f⋓gS(䅇=&$#BՄsGXXf:Îw2hvیeRX-<#Dž'惉ڔ~eTMÛ 7_tۋd]P爨;.#X.b1Tí'͐fÐ`yrzbOO.*:h"΄|qͺ}'EFPةq5``N5؛J9"='|RkЧ;:'`zy7®pE_gM)9ߗ" 'Vqԙ']~o$_g+/M/i8΅"ҁlj'âw-wxIShx|*yMmD|z\zJ|{5} ~Y-!Jzjzzz̛%z{{HlזH{\|hJ }5k}ӊ~e~}_ӟ}Li4}0B}:{8~}alc }\{~IƐ~~5$~ŬQ]Aeaz kZ[:IPx4qcvjnɇw˪[ߓQ?t8x|~j4 ZBZHAʄ4 P^(kˎfwriyYM]GyA3Z:O^tV(,#ՏlDϕuWggᑻWVFi[2鋥󈖈SŅӣڝaQU|ls眄ekVtܖ^EG2%8G N6QNjaǭ.8]qhd`UDl11ÓmџۺD>?~@! q5chTaRIC0ԊYvG/d 6j.К}'p[]bҒaS C1<70E(2rLnwՏUEx) x}wxkyy]zRNX{9>g\|N+}~~]~zYzxzwzj΢<{X]a{N)|>-8}a+~G뎡LR(|}|1|w?L}j}E]}Nk~=~+;֎2Dn .NvhŁ|isR\kIMۚhW=]+CfiŀS[uGK;hȠ[3M&=Q*ӃQ1Э~Jǧdt+ӋMgҠtGZ[Lrhv<pi*:Mڌ߅4 ~3r3\fjƑY}MKk;*Aynj.'g|-qěeHLX9ʖJT%:ٔ%)ꍽDۏqsywfPy[zyNʧ{;A|(2ŝ}C"7~zg d,{L{${]q^%{mfg{Zث| NO|Ai}S2~~."tN~.}{}q}f.}Zv}Nw~)ADS~2EE"`#W`6z8Lpw?e$Z'yNo|@롺2W">^ف7!yǶYod쮘YSvMޅ@(2e"# 08_=x3n۲-d? Y ÉMJ@)1ӛw'" >Qw."w̵mwoc)DX "L9n?1cH!){ mhevlbSWHKPÔ>0ۙs!{3х~Ƹu|k{`6UĝJj7r>q0SS!h鏴75wJ~2CtojR`SUJ4=+r0 `!QAJ}Ftx8j`XU'8~I={ԛo/!UF1c|BysUziPz:^-zSw{G{;dT|-i}B~ &S||#s4|!i)Y|^|CS0|GӫL}!;J}-]n~9| ƔPv\|{~ys L~Mh~!^f~Sc~BG~;.-P10CZ ͓ՀJ{2rXhne<]S?Gb[:$-6&/ I#jzsTq$g׷ˆ]zR!RG5:-7 azMqgE/]RM F˩:z,B+HME"JLyc+?p5˔fg\\-ӑLQF!$/:匫,G MUNJxjSoJ(e[LYPW Eh_9qf,F󖏌81;wnl6d.KZz>Oܫ.D8z+ݒlwɬ-medPZEnOkDpo8)+ӛ:5 Ovm;cYί'OAuD<8ə+Ǜ'>/74Xu{l+’{&bY{!WR{[MFM{A |p6c}H(̣-~Kn CZuƟ}Al#9}bD|W|M9};A}5!~Q(Ţ AueLl3b*_~Wи~M"~Aۮk5(S Ltŷkz?faWqoLز6A%56(!Qs DztgkaI:W+LLRA{ v5h(ig ޓus~-j`UV綜LAYY5(>-d 3,gs.Ù2j `PaVHK鰖@ޫF58x({ʙ 9 v14愨r\ߞiMFh_UK>j@Qc#4Ѥ(GkΘK dq-h_ U J?ߩ 4("ꗙT =q誧hmMp^ظ:TʳJl?IK4i(<An (n":nq}{`N~!|%~U|m~}DR~}3H~=|~|~j|~0!}3}m}i}왙}~H`~~l{~]~Q~$3!*|j|T|X|z`$|€լ}~}St}k~Q`~|2~tr-{!}{e̓{E{*|DΖ|5|턨j}m P[}X1~?h#z"eڅzuszȎr{{e{k]|.2iX|OY}[y1B} ~łyg4:y0ǟz zGz z~{x%h|Ni|0}pF ~G ix>{xx뢱y?yqzy|z=fQ{DM{/| }.wtwѴxxq x)!ymzzdz&K{u/C|E }BAw^͔wõwɾx.[xy+ 恟 ȶj[̈́]wr>uC_GU$A+Z ʁG<ӴtGӌE;,IHԈsƂh8^EFA*% ʂЗU<;+?erSۋ]em,Ef*n Ņ Ϳw_ TIH֘o pLR[؀yD@b*&݉ \zp V]çρAB]ܟvnYrZA5C)` h$he%1hҀ'~{(lXBT(~" o~͉KIR+«սԝN@V}ɀIlXZ}BB$.(~ƒ ~%)^dfnB l}<kBW-A~Ĝ(~r ~> Vw ^w"wۣxKȊx܂Ljyp%z[9{C|(e}҃nZz`zdz7zГ {,{o|EZ|C%}(p~jT͚F}ٌD}E}zO}pu[}c}o f~#Z?j~Bd~'JuMʾgUTwЯiFАnł6mɆтY-A'Rかuǵ‹z֊ 쉏q~}-luD8XViA7`'Zn;F[/RӉcirk%~k|񆜌k'W ʉ3@Շ&ԅсC݉ӧ'z۔^iAU=?|g&:N >fW-4<1VK5xT]ghxT#>kk% (.Ί+导N6vĄeDŽR**=]8V%= 1`݉ʮݾvćιµ*z!vϪeTR25<$ O%A ȥ]ՅzukdQ˂|=!}Z#z~p߅-Lh|z|rq|a|F|x[}fߋ}S ~<Ɉn~#JIՅðXhÐׁlώkw Oe HR$1<""L/ل4z꡵8·B7eudRQ0;q"ÅăG*4-F!ŏ+8RϋtOhcpP7qL:"0p8:I{D ;_rGaDN9*y"({`2M1%7pP4E_ŗM?-8셪7!тhƑڤZ51kh#h}@Y7n^ahHL;>7M! ܂ܑz&z(PNF|i7m]7K7s փbaƈ!aæ¿a)މ{m~],K=g7+Θ Ńc,l٫u[v"::v>w4y_wjRxZyH|:{4q|HŊ}~Ϋ$sxx̞$xÓyygxmyjNzZC{XH#|93W}2v~c{w4{{6d{xl&|iܔ|qYޒm|GΏ}3-~HzQ&kTV۝ ֚рwXfhLkY倆Ge3 U4/uܞއ✧$|UJv !gÄXbLF_܃20U3` ;{aNjFt4f=@W!ވMEP72!Ʉ ڢCٌ4u)3rܔؒKd󒋐vU*Dv1d;@MK`2}󖿝q`cJ*TC4Ch0anҝa7۫#||oIaRڏKB8k/ PG!F${L](n`R>A}/L”܆厴pvz37m꓾`pQQŎԡAV3(/tӆ䃷 Lwģ5laJT=G&n8-'_M$Jtwʪl ``MTzF3k7ʒf'^cKO?aʲvXeksI_ޜݦStMFJ7'3YNKkvxwUnwcɮxKXky LvAy?}{ 1|W!}}{WǺy4xp*yvndVycfzXCJzLQ{W?_r|:0윋}D!X~bp>Ł{x4{n-{cm|X|^L)|?>*}0ԜN~\ !8 w~(rmX,bo WYKʥ?>񠈀0 ▖-h^vglĄb6؄mWKPu>냱0j ЕXАt~ӷۋukа2aA V%JJԣև>/J0+nI ?Ä+}Lt_/jw`ku,UKIS=e/h Y:ކ:P|R)sRɜi_Q֗TyqI F@<ŝ/7Ԏ bO,n{6[r'^hyy ^=\SyH+< &.B POzq(gC]"S؟9Gǡw;ߘG.xl ǐTWWzq"xg} ]XۥRG}$0;B.X hov\QyB wpx!fRxu[߳xQyEAz9N/{+|֙~? qRy!zoz,f8zY[ȳ:zP뮷{"E{9>|+{}ԙ~ 8xY|UoֻE|[f&|b[|~PЮd|Ej}P9+}+7~ԙ} ΓOBExF"o=e[J7ZPZE*8+јx vw'ndՅ Z˱P*5DިQ8ãE+n郫͘2 *vym˹Yd4*ZGOˬf=D8+K3Kʗy [Vuǔ%lcdY/YqNrKC䦪8 + 8(ږ} `\tۼlbXDNC'n7xϐF*o 握Bdskea$fWMXB~B60*Xˑ=k&sNj$aSWZhM{B5E6Ÿߖ*6wKv##dcs$[j`ڪ `W cLeBa6**`oMXurxhy_[@yNU%yJzD?{3?|' F}T@~ j-&(rs{h6{_Q{#UX{QJ{?p|Y3})'~"@4 ,r}Rh}3_D}U}J-}R?}3ܧ~c' @ Ȁoрbqrshi/r^ӺՁTɶEJ[u?d]3¦' "cB選 ,7.p¶gp^PtTwJ-~?A23*'aO' q1v*p1 gI]ιL T"ȉ&I_?ᇣ3h&[d` (!o. f̼Ғ]D]SƏI_>͌3CRa&R dnwf_\8RܲH > 2أn&y' ؐ؈vօUn?Ĥexf\&RI9NH!Q=.2&ƍ 2>m{Re2V[ضPR/G=jҘ2m^&RŖl =ߊ0m+dF[8QұG5=Lh2eV& vy w͜T_x˗Gy,Q{ *|knҙsc%szt7u&u˰vs[wn2^x\!Fyd+z &|<ђs?wsń|tkh>t (un+v]rw<^]x!Fy+za" C{ssˎt@tHyu`/vrv]wȥaFrx&+zK {|uԞ}v}vm}2w }Swr}ux|A}ye}zLc~F{.~}A [H~O|Zyӱ|xyV?|y|yڢ|z2}z{}W{e}|XK~!}..]~~ A~{|һ{|`|||=|I|s|Վ|},z|}d}b~ KO}~.~_ Gz>zȃ{.{E{|vIv|,}Uy ~9xqxxƒ\y-JypYyz1vza{UH|,|ą }MwL< wx zxY㘀xH[y7tyʒ_izyG{6,| }_jvrvή=w:Хw%wcxry"}]yڗ-FJz+_{r |d͹v[vqev̸8w)w xpx0\\yjPEPz-*{& |@#̪u6\vKwgvvcwf8wWpxG[y.Dys*zڒ 1|unv_vpvþ1w&w}ox?[jxפoDys*z} }{GwucUuL8v:#vwa xSy  yec zJ&5{-Jb}2 ~WC~xt~x̿k~y!8~yf#~yҌ~z{xe~{Cbg~|I|,E~ 3~<~d|́~T|~O|,W~?|9A~L|bf~R|w~v}Aa~}H~~T,Y~ D/}c}2}2y\}L}l}vz}f`}JH+~J0+~ 0G|/ |>^|[!|b,|L7|u"|_}?Gm}I+~T ~Ғ{Xb{j{f{ݗ{_x{s|8^|F}+} )~*E΄zMzFpz|UzהzWzq{TF\{E|8*|Z }yR7yby{y`y̥ՒGy4zLoz[R{%yDY{*|; }Gȥxﻫyî y7wy\#yDy^mzI$YzvC]{4V){Ӑ |Ǐxxayy*Hya~ym0z(YVzwBz)^{I |mƻxx'VxxȎy"~jyhlyƪXz vBz)a{7g |bslOtfuNau𖉃vCwrV}&E} }񈄽7~ű*~b~5={}񴂇}x}ef}S}s>}nJ&}~O }~ư~gG~}k!}yw}QVf};=S}{>J}-&}%d U}TMssmtwЈTu1Iv|醺wjxPVy?D{ %qk|~dv2"vw)w3xC|Okyj_zV{?>"|!% Y}w~k(yCy؈^yZz?z{{/iӅW{U|>}`$Ճ-~X~z݀[,1{љӆzjghT̄>2jX3rƕۅPrwrjf9w]R҂߇C;#$uvC`ꠟ,Ds cbq"Oځ:#\nb&ڄ񳆛;Nƀ:ڧqDB``ށN9ր"hzĹztYnsp `8v~M9oN"P:I~A#,溇΃"~тfo«f_&M`9,"EbF~鈼Tr%&grǏsԒt_usMvbwOyM9ɇ M7ނs r6kN 4؋}މo͈5^XL;}k7tEn MՂn퍱ό>z.O{)l \Jⅻ6>\AڍCʌ!JYʆ͞yI.j쇃T[(dOIm75 y ʯ=5jwh6i4YH+ʘ 4xT.Y׌Թs\z>Kˬ%ժvݣiϠs\ʜN]N>aN,r:ьAUxMkUëu?Vi\M͌|f=H,nn3ʘeҖ@tOhrb&[~PMa0=+҇_r7TчRzqȦr~z{ s_o+0tIc@u\Vc3vHrw8y'{B]}&l~UAt0pXtz%unנvAbǝw!Vx#H0yM8~Tz&搸| _}T~{ԨvڄwRyǣGwnwxcbnyUȚyGqz8L{&͐}c~F)~ь|{7|xʢ|m|a}&U}G[~7|~&8lMʋ@/wl)`ٜJT]PF/7tށ&k_Ц^vkaXk_ۛSF56D&1$zn .X~g$tiю^r?REDƊ6?mV%ȍA|ƢҚsh]>] IQJC(~5a׎%?Ur`U{Jq{Ϣ3fY[gO͖ޙfB䓺84Z$Տ+)n1ozqpџfHT[OGBr4&$J#n zGpD}KeŜZm%Nߖ`TB3?3ۏɖ$19zQMrqsZh(t.]kuSv!Gw^;3Cx-Xzr|3 *}~z0tqsIuhv+]ƫvR@wG>x;y-Gx{]| @~<wzdwrqHwgٮ]xK]xR¦yyG[zS:{T-51|wg} XW~DJy|pر|gN|]"|Ra3}5G6}:~N-~ ǀ:,x؁p\f܁p\F|Q:hE{,ژׁ! O4hDM8推+zȊoAχsMkiˠ{b?:X}{N=CvR7`+.됗F0_@=sL-j߭aŪX >MѢSCP7*j*EDžrji^a`ƥWMBםV7m×*ݕ@'{b)aqs\it_ʹDtU6uKv@|w4yJ( zҘ1| fn~8=quhv-_ųvUwXKx*@-y14oze( `{ח}A A~pCqx hPxi_xUӯ~y5K|Fy@z4{(|ݗ~ 8qR2|h||_^|U}KH}o@u}4֡_~(c4 ّicXp~ih^၇U1\JS@5Lg4'監\ boָtgi^]TT"J^?ᤅ4j΄'̚ބn$ V&cnͷߏf[]Q/S+fIZ?-T3͉R'F' (me@L\EhRū^>H٧%~>k3N'/ ԉ @gl̶ dk[qߜpQʙH=ɢ2ϝfp&٘u. 8υ{lLGc[QGŦ7N=Ǘ{2&"Q׍ Qkڶ c:ZŮQQY-Gڜ=Ui2L&l D銀jtthaмtXuOWvAEw4:x_/y#_:{W3J} ~nq[j-vaFvXHwcOwE$lx:y/vz#d|!>}2~FZjxay XyfO yE% zH:A{/|#n}#J~TP~+je}xaǻ}hX}^O }yEC}:x~3/T~#qc$րvfhiԽOaeX_Nڱ߁Dc:/x#~{Q)ђqi9aIXAxNDs:k /x#Az. Vقohb`+VkW15GMͯ$Dr9ত/#X/Ös }LЃgX_})vtQw.uN wu߮x8vwxw.y_xxzybzz2IK{{\,K|| ~ }uxs`vx8w y wyfx?yً xzuw|y{)az{H{~|+|} [}~$t<]u<8uNvuw<[whvVx`~yGz+l| -}ls5!ȼsRt7unʙv1$+wtw_Rx}Gz%*{m ?|KrlrHs秼tYu1ZSv(suw^ xAF3ywB*zՄ O|vFpqouqZB]r:vsΔtu8qWv\twR8Dxp)zR h{τeoRpTNqLZr3s't6o\uaMZvJCx)*y8 i{bwn opGqIr~smtYov'dBwk(y* i{DǾn-opx[q]rU~sjltXu㟮B]wB({x葉 zӈna\oR~+p9kq'Yr~slOtJkXusB;vۚ(x zt ypiz qzJrrzsXzt_K{8uu{v`P|!x{G|z+z}u{ f~j}Pyt-Ɋy`tϺyurzv:zjvۈzwuO{Cx_{z GZ|{J*}O| ~V}x_wȘxx1yxyxxxyyxzVz$tzz_5{{F|<|*}} ~+~DvnƤwr>w gxb~x~܆yk~sz*^.z\F{*.| }uć2vHv΅̦_wQ%:wᄒx|rOy/]z4EHzꂶ){ }:Stu"SuvPڔ vPwpxdy[yIFDz>)k{Y{ |[s$sϛMtqu֑PuÔv|nwf1ZWx\CXyh (z, @|oerrѩLst.̎t韅~ulvXwB,xѐ(Pz X{pqy0r7!rsڌtXw|u4k v,W`w1A.xR'y d{4.qA=qr8sVt{t{jWuVv@x B'y^) z+qqx.rgs(sǴ+{At|iuXVzv@w6'xI zG|dp#c|Yq"|\r|{s 2|t|uar}v]}LxRE}y)~#{ ~}_n{să{tR!{u{up|1v5|ewrI|x]"}yE7}v{&)v}| D~}ьzw8ê{wG{Ix{px{y{yq|Jz\|{D}5|~)*}} ~~Hwy~y~qz~nKz^~RAz~W@{ ~p{w~[{C|^(}J ~=YxwCx#ySy\~yz)o\z;Z{>C?{z(d| G}&wIww]{x2xmxي yUmyY|zB{?(|V }&Xu̸Fvlvԗw7܍w}xEkxꏌWy>Amzi'{Q |ud{u ux u褛"vXFvޞh{kw~'ix3Vhx@LyҐ 'zŋ {Ytb'tڵWuHiu­d(vN(yxvhCwUxy?OyV=&zZ7 {t#ﱡtˤu uuPv >xv>gwlTtx2>y&dz {]%sаtYhtżHu7ㇲux!v\/gwx&jy lzoE(Apeqy倲rx7s~Ytlk4vVX w@y&i{7}CUresxUtE)2uu~vkxWxyf@yz%|cK&}Ɖ_v'vw5\wxH}ZykHyW{{@|%}J! ~ĭ}J~}:P~}(~}4e~}Z|~}j4~}V)~~m?V~~%K/_5b}ꄑH}ք}Ń}ȃ1}Ԃz}れh}QU+~!>~U%~sj}qa|񋌥||}y }$g}NT#}q> }$}~({ҙ;{T{̔{Г6{ v|-e|Z7R|0=|Չ$}0uE}Ą&xz꧃z夼qzz{(.t{`/c{Q{;|C#|\}JzDxrzN(zRPzpzrzբcb,{!O{o):{Γ#g|AVg|燚z+z&Bz' z0zWoqzazۡO={(3:{5#;||+:zy򽑛yֹHby鴧Yz qgz;a zNz͞:`{* #8{|Ck(nno(p5~q̆Ysv\t^eEuQ6w;yL"?){a}sqɯE,ruso tCu9v vRdƃxwQ)x;!zd!|܁}k:uuvJۄv؅wuxxjdC yfPÂz|:ɂE{!|ځ}{۬ꄔ{%|k|(˃@|ktJ|c=g}GO }:0~]!uhV 𪷃<$APcrb!wOaf9M!EՁ3@πw$ǛYSqxU`݁N+8}Z! >o:ΰ3ˤЁCɁQހ~SoHe_%L8 y|$<׀ЖPS@{m6F]9~_KFHB7  H~3>~녉ma΀ :çyvkm8[I~җl6~~[G~ ΞBH尚`y;j~&[~vIj~O5~e~>Z~A€m1Ѐv7zL/xd~j&~Z~mI~85_~ }{}}݇ݯmݣop%ҊqK|ʉrms]uJwC5yք{;]|{pIoqڗSirŠ>qs|A{tmn|u]vw*JSyx5irzl{H}7ƌt'tӖuv%{vl w\%xI4z5<{K?|W|}Xzlz;zN.{5z~O{kԇe|[v|II}@4}M~~٪oUX.*؈Ty4jZĀH4#82󂅀( ? cawÆiM~YGM`3w8~kA3ۈTybuKێgD)WՄDFd}2Ȃ2ށІ3ˁ&WTތМDs%0eUkXV(E1΂cD^74e~{qMcT3Ct0ȁ,рڌQ.s)곃 R8}-_RpbxT$C<5[0\p1J6=S|$oFcbotSBځ0<LCQmuAn&oz7pmorA_DsP\ub?Ό\w.,Ey?{W4|Ř|pJq4ryÓpsmt3_urPv?"xb,}z/{}8-sBPs$tyC ucl\vG_wQP?x?Iy,O{|}xmylyx/Nj=|,S})J~}u~wVp^]wjRj]]M N_>-+5u+|LO\!tM=zV+Jt2K1~tsZݍdgU?/ZwLW/~B+e=3j"x;bqȄ)yesoeP;Z1!N\}An2ق"ʌт-f<`w|m<cŋ Xŗ;M#8@qԈQ26J"l.,~VH:ulAb%NWdK?h(1jP!NC!|颁sEj%`V Jnf>[0А!=ȍ;~\8{蟦Ns@Si*`jgUnМJ(/~=rC0' ~Rw{Karria_z(TI3= /1 Ċ<rnjoan*pWqM]Ls"B|t6vx)Ulxqz L|~rXpjY qaLrWksM=tB`ix`|xV2yLz0A{6<4{)!"|~ C~NqG}h}`}VgS}L>~1AC~5f(`7 [ipNB-h _3ςUoK"XA}S5\(kau ނ1>n-f]݊-TdJ#@*4B(`i ڋzIm[oeTLy\SgɐIV1?K4-2'኎{ 4l6ĝd5[RW0H>p]3yR'bJp' ^kck[>QH:h>Ж3.B'-= O.4kO?cF!5ZQG .=њrI24'N AijLmo:bp"Yu;vr0zw$y{},o~bjlsaòt\XiuOuEʧv;w0qUyH$z|X/}0~isxaqxXy$OSyEzW;U{+0U|#$}( ~=s wi1}a}X>}N}EE&~:; ~0*-$!۔I\#Ԁh `]AWwNw KDץR=:9:/܂J#Uh񓄂 f8gS _F?ZVMD A0:)/p҆#K?}| ag[f1^3\eUِL2C?^C9iK.y#ssJ ;Pe& ]F;TŪ-;KӦ5B|*8ž.eZ#͍֑ dǠ \ԭ]TbڛKu(AB)L8|F}.0"v,ѐ ͋bd=\\v T}nK/ƛA@8Pߖ." 5 >21cpZǸZpQqHذr?a`t 5u*wGry>S{Q} |~tc r8ZķrQ-sHڰ5td?fuz5v+ xRz{B}ME~mbtyZxtQuHկv/?cw5x>+My2{ |֑}~bʺyZy8Q˲ywHy?Kz5~E{`+{|Qj}[9~j#GVb }ZXx}Q}H~?)~a5eX~+G_6vqb';Y봖Q3炊H6W>G5/t>*㟬Jnxh/Ɓa6Y PmʼnJGሌ>LÇ4NA*KR(:`h*XJ~O̐9FԪ=ȍ47Z>*9rΓgxׅ_tHWO F;=!3¡)Hג?0rS^W+ZXNĭE<祌337)=APLҌ׈^Vܰ&N2E̩?#<3y)&nL  ܌/ htktmuOn[.uovqwbrqxAtF\y9u%Vw>y7i}]jWl'mG҇WnEwpwfqKSzs`=u4%4wJyؼti2^!jrkn`lnBw6odf'qNS(rQ=tΘq%wj%y~zmmJBnQ{&oT╚pZ,'qpcwrufsWSguJ=vȎ^% xrbzhkjlkm+n_opuqdsHR trVQtfz%{,z|rLst>tuoy vxgwU0TxK>yN$z|Vqjcrˢ r“,s2t]vu@\evu :vH#?xzlz=nS_oqRp8*qOqqmratNuG:yv%#Dxyׇ߽x~ j}kQ}mjF}nȇS}pOw~}qf~sR~u67y 5z+*{#ui|0k]klʍ"nn;~!oo؁q|_6sRL68uW7"w|y|@m ӂnI˂ op~]roT`sk^%tKـv6Ȁxz |d>pLqWr_߁Os@}"tMnȀuz^FvK{x@6s|yz{ |ҀvuwxjwuDxt|'ymy]NzJ{5|&}f~ ~%g~-;~4 3~A{@~Yl~\T~I &5>Ds dVl}~lc~kV~`~Yly~Zk ~X[~aNI ~u4~~9Ϧ}}}fI}w}+h}=YR}Z1G}3}"}YF~p^|֘|ʍ|!|/t|GFf|hW|zFY|ˎ2} D}V}ۄ{{x{}{r{e4{֜jV| E|K1|u|}o{D{;{;q~{Nr,{iTd|{lUw{œ8D|1|O|}'-{ zz$~:{Wq{Mc{:U{h7D0{1\{F|A|i=ȉ4jZlG$muoeg*q&WŅes Edu!1fwH/yk{Xld1mn삣p-uHuqgsW` tE'\vo1$xMq zx|4fop}9qrtsfWuVvfD w0py|T{Z|uvv"7wՅwsxbe~y5Vz%DMb{0^|.^}K͂$}NV|̅|,}z}>rq+}vd}U(~0C~06~.iG0Lsǃĉ;s}ÃpNc*ӂTYkB܁0//ˀހ~*!XWɃy{&Zn7=a5ˊ'Rd`A.€T߀F&\뎋<6=xӁ̖Ele>_[&P̀@NXw-׀ qX #`ڌ {"vށK)jπ=]!Og-?,Ev<<ܪG~vj]LN >,wR~ІNJ󁲯Gmus֨iiB\:Nm>@J,6~~~ge4iFj~fksnmf֏o/YߍxpKr::u(w_:yM|%k؈'m!}Fnbrofuq"YrKCtl:ʼnvI(rMxD6z.|_y:z(n{@}x}[hz!{'{{o{dR|1Wg|Ir}G97}'~rC>~ӐWqyLn'byVV+H΁%8*'o.KBF܏\5w#ڊlvasT. G1݇97ᆗD&х?<%ȃy zi<~:t_j|_AS"m[Eڇ%6ԅ&Mڃ-܃M|àfs h[]'=QD5m%<&fP\n{Rr;Qg \QDo5G)$ރҎ:mqcZ?{:qgYݣ\lPdC@4ϕ$rg1 Y~i7ujjl;Xl2a'mW onKrnqJ>sY/%uJw Uzy||}Ơ,kulknUaoVȗqq*K8 r>t/ڏvx %zl|}fnetgoyk;pac*qV}sJt>Nv'/w~y{3||yssۜitjWua`?v.UՖwJax4=ӑDyj/jzh${0S}-}{fzyirkyi[zU_JzU&{QI{=9m|/ }|Nq~BX~,z+uqxho^dT)EH<8.@}(R[x$ioϙj|fЗk]^XR=GD;-M؉҄j"kxv“Fm՘e"[Qfn-F|6:a-5i~xƃDt0 l,WcyZP ؕmED99`,T8O3=bsbkmşbYfGOtDb@9"v+NkA rjʗb*v%XbO;D_ 8ӌƕ+^<ێ=kickaZhlQ,nOG_p=hq1t %%v\x{1}-j]lCb©mm}Z:vnQ~pG>`q<sK1љsu2%!wA+yn6{p}4jJnbsoZpPԣqGsP<؜t1v%VxX6gzBXK|.}xjsbtjYuP_uF wІVN D\:Ҙׅ0,i$U\Y( t;&e.]ƣUL0C-:/# _9Y y&h#d;\$TUKtBȚs9,KA.ؓT#kl-⌸ Մc\ɚT Kp B_8Җ.#5` fb(,[l2SKB8.^Cu#{ pbl]jZ*kR#m`i3~NXɫ!~]P~nG~>͡\~5SR+)镖FE逵_SPWĩO΅~G$>-4Ĝ|*ŘXǔzȐ-^ VNҌF>=a34,*T͏P{Iڄ$ ]0UȨwMФEhÒB<`3ʎ)͖<6Md:~\UWMmE [<z~mJ '~rA~8Ƥ~/O&yIAOXQmBIa$@d8I/ZM%MȌX9P!PH̋@cQ 7Ǣ).<%VMɇV!WsHP 7Hؒ?ʥ_7<}.~č%CPf IeUe$Wՙ[O{GǨb?7:.PW%"5*Zsv Є VծaFOv29G?[f6⠷ .9ڑ%d1CݏpWLbFn9c%o0e̥p#gqiyr#kxxsNmftoSvr\n1io5jpCl9qjnwrofEtqRushOUrI89tx!Pv‟y/d0I?eُ g}ˎi2}jqqql;`nMq8sp vMZxGbtd,\e+g~ioIkr_mLp2<7rI _umx˭a@\cUdΦf_|hmwj؜]nmKVo6rntx/`4wb͕dcDfO{ChSlju\lJo06q̒twA`@b!dBCeSzg&lOj \mlVJnǚt6qftY-wcpcqGergXriR_skjutmdcuoQwra;xpu !zw|&zx^og)pOh"q!jIfr lsmu!tocu?qPvs:xv@!yx{zQn|jʭ(oYlUp=m_q=nׄrLpdtsnqc_tsPvu:Sww!Yyy{{pUl{r9Fmsns‘otpusRr0vb[swO8u'y(9vz~ x{{0|Qjy`kz?mz{nBzفHo{Mrp{aSr|[NXt1|9v} x~5z~5*i6qjK klkn[Fpo܁,`GqMtsK8iu: wc!yfƏ$>h5i?k}lnn_^p,yL$r67|t2 'v9ky2լe+f#hi{"kdlm4\o&PJq96{sruxөlcuej.g0ih/y)jt jlZ[9ndIp55rߏVuoExTcgdAf܅hHmxjjj=kZnIp925hr9Cu)x4c dPf:ȅ gwiikZ>mHoЙ5Pr)YtǍTwrc_seMtg%qti+uqkJrv=mqaw(oNx-rQ9+y\u zwZ|zqfѩrhs(s6jsḱetmruoa7vqN:ws8yv4 Nzx:|l{pjaqk_r[m s(ntp"quq`vsMwKuv8wxw z;y*|3{nqorpsSyqt?\ruKpNsvi_uwLv[x7wzdy{.{|԰mSy6n@yo=ypDz]}qjzor{l^s| Lul|7>w}{x~+n{ ~Eke,l5mրUn|p8mq]rKBt6v;gx#zi«ij 7km5zMnkp [qIs_5u6wEy֧gכ͜i NjskϖwmNinZTt8Cul0vꉮxz5lfV'm?Ň`n3{o`yoMpaqS&sWBt˒0v^U|x'?z ?kC?lשqmϦyznmnp?3at{/vsbw֋hyօk^lx[mt$z`nn oۤ$`q,?R1rAtQ/u1lwryp}bƗ}tdr}af*}phs}jd}lUn}oJC~q/~\t~wRz|eܗ|g|i8|jrm|ldm}#nT}dpCN}s>/~uq~x~Nzס {i^{j{l~|mxq|Soc|pT|rB}Gt/C}vY~^y_ac{Uyoz$plzoq}lzrp{sb{|u"S{vBC|gx .}y-}{a~|[xPvxwMyw|ylxeoZyyaz^yRzzA{{._|>|}}~C}w~JGw[~_]w~dzx>~ynx~`y^~Qy@zU-{|gD}T!u8u/vx'vkwS+^x|Px?y-:z"{|܁KssbMttnuuiiv,D\vNwՌ>fxŠ,_y~zJ'|=!r׉s%~sОJstguc^[7v@M*w.=Ix++y8#zhCS{Ȅr4~V~vs"~wg~xCZ#~y&K~z;{)R:|&dz}< }}|LJ}||}}q}}Je}}Y}}K}~J:~-~)~p|~fN{={Ƈ8z={ot|@c|RWK|8Iq|9}(W}}gA}ႄ~Vz]czpwzm"za{?YU~{G{[8|N'|}<}>yS7Wybuy\kQz68`+zTz攎F{L7{ &|9||ĈTQ}v38y~tyRty1jy睰_z: S~z+FK{7.{t&_{V|}v}%Ȇexߩc}ytVyRjjy_y䞜Sz@XEz6{&/{kU||&%bg{dIrf-gh,\@j[P/ lBo03gq".tw zDe{3=fqGhg ijH[ۉlԃ0 uفc퀸z3nzrώi%D_UDzI삽`=62/ N5fBDx$p*glu](SOHR<0.с%F1wD8o_Gf$O]68S6H"Ɣ<'R.s؏EZ$qvWnZZf!ԟ&\XRݚBGǁk;߀.9wB%~kqobid`̖fxW^VhzMCjBm6Ďo)reЊkuR ;xm@{qei$ f`XhWj_L l]BO3n6@p(:sfъv ׇx {7pgh\iD`(jV$lmLn:B p66_ra(ыtЉv yw{oilg#n8_BolUpLhr4Aws5u(wQʉy2  {.|nhrf͓s^CIt^UuSK9Cvh@ƍw5Sx(8z@;{ LQ|~}msCxey)]9yTz"Jb+z@ {4|b'މ-}@s~! }~ k|c [Rǎ:I:ˁ?@3쉟'W vf OڀiiGb܋Y@QD!G苼=C3A&/T8 ン!gg3`XOMCFŊ<ꉊC22&j qTg-왦_厕-WBOgFL<3-1އ%9V XMfyx_Z0WwN E-<:Г1cN%Ԏt9>* a^>cCWleOCgG$i>)1k?4\m*[pUϒ5s5v<?y#8{^pe{W6g.OmhF6j>l4n*pqpϑt vgHyn{^#gVwihO0jFɜl=ؚnt4]p*Wpr͑fu# 5wy|]AlV<0mNo(FE|p=kr4(Qs*ru|w v#yl!|VeO 8gGtqi.?ej6잾m. oB$q{tcYw%،y|ULh2NᦑiGMk,?Cl6Ξ0n. p$r ugqPwz$|;UVlN`EnF٢o.>p6r,-ҙs$uuwxyS{}TzqMrFTas>mt6u-wS$FxzbŽ{} ~8T)HwM9wEŠ3x9=x5y-4z$}{*}⍳~~:]S4 Lq%%E;=Vu5;\,Ԗb#ғ6qu5R'-OKvΆD<_^<؅4.,TEN#x2ώ3"jQPJzOCYPD;ϙόO3+Y+Ĕ_U# h?*цQxdׂPJ =BҐu;|R>3+팮"DqƉnz'P&. IÔBR2;D͑3*L+pp"ѐ rO-}/IO &dOH 7e@0g8i0k(E9nDXYpCs ݒvy|OwfhGg@~i{8k=0Mm;(Foybqt wW>y|NhGj@ekm8l0n(=.pc`s#au 1x z0V|;N_m!Gn0@oL8]p0UrD(tWTugw KNy {}N[rG98r?xs8 t0qu'wWD?x͔dzr x_{`-}~4MxwFw?QFx7ex/ΞRy'z+/{֓f| u~U~VM/~FC]~>㢣~7X /d'p܀) 340ڀLsmE>HP6Ӟ4/'?F˄_ 32pM{ KnHD =$6;_.n@&՗JZfА?< >i҈toK͑Df:?=T5ҍ.j䌵&̋EɏԈ N?%JUbD = 5؜@/.OQ&Cl{֏W p)ʅP Tg[ҡi)^CUjZ`WkcAzom*eknh[)pkjHrn4 uqWwu^RzxvSf_Pgaui8cjeylIh^knjZomHHXr#p"3ts!wCv1=zxFe*bfd̓hfihy&kfjjrm@m Z3oIoNGqq3Uttwvw-5z1y|6bjzd?k֒emagnwip>iNkqYAmsGpeul2sw^v y`Byzݧ `7r1b sZcsЃetvhuh,j;vXNlwFVo=y92"rz^u4{}x|b']z`1_zbzid/{Iu=fv{g h|0W_kf|En)}61q'}9te~)x}I[YA]n:_'(as7di#eGfUiÄDmld0osj/wR&ut~#xwczyߠgi2hkj.lm~kmqmocnq`Tps2C5ru3/twQw?yz.{8e)qnfrMhm2,p2syEw [ߥ~>^,`:tb`i#dY\RgDjN8i>>OlՑ,oݍs3vԄ'[z]^_ɥAtzaMhdE-[fқMi&>(ll,oz'rԊvvz qc[y_q]r`dzsSbmt=e`'u;h]QvZkH?wn,xq~zu|yp^p`څqycyrhe`m9sqg_tjLPul?wo,uxs bzKvg|ycnaoc5poeyNqglrj*_slePu$n?2vqz,0xtKIywP|EylhmjM\nkwomPkLqo ]rkpO*sr>zuwt+w4wy6ya{{<\jOp&dk|q$lrv]n s.jo}tb\quNArv=t^xy+ivEz xj{z|=hRwixa@jxtlhy\hnz[ozMVqt{i=#sW|A+u`}"w}wzK~ e&g)q~h4rjQglZ-m~L o?<)q*bt6vEyt7c=e"0{fŎ"phe+jXlJnW;p)sW\umxa󜫃cyerngeciqWkI{mېV:0p6)rluvxwad_c$FxdnIf뛬bh/Vk)I my9oސc(rcdu/x6a`Dbxfdgmf|bzhV;jHmo9ot(q~tЉDwׄv[/*w]}ws`9r|wbfVxepYSy+h2Jyk+:izna'{q|u~x6u^R}v`}+vbqweewgXxj JyXl:z@o'{Mr|vB~TyMrtUatc|ueq9v@ge(wiXHwlJxn9yq7'zt |@w"~y͎r!h.rizskotlcun}W2vpUI'wrS9'xty'Izv{y$}w{oopp&ykqq5nrr_btsV;utHXvTvn8ww'yyz{T||~=n v{owx p.wmHqTxParyUCsyGu(z7v{&x7|y}|}ꈮklցunkAoT|_piSr'UF=sD7uR.&wy {J is}j،s}l?i,m^o>!RpՉDr6tP%ov7vxTz̓gzi|qk\gml\n1PoCq5sV$u8wφz3]gp"z4h?pj4fl[mPCotChqZ4sC$uI(wyBg yhpVj"jfUk[mTlOoC0p4rܑ$tO3w(uy^|[3}|]tN|`'i} b^0}?eUQ}zh"D[}k(4~"nU"~q/Budxi{^}F{`as|bi|Bd]|gQQn|iC};l4:}o"~>r"~v+.y6za'|zc2s{(e9hy{tgA]{ixP|5kC|n\3}4q"}s.~vyxgQzxhqyfjg!yl.[zemOzoB{r3b|4t("i|vvD}xQzcvn ywo=pYwp_fx;qZxrOytaAzKu2{%w"9|yIP}1{ ~|V$tuxTuYuo*uvdvwHZwzx N>xEy AHy z 2yz {" {5|/t|`}/}}~rQvslsbtÀXIuLv-@wI1xۀ]!zmY{oC}{pGGsqBjr1`s7VztMYK#ui>v0w߅ y:!z0|mmyovqohq_Ur!)UsKIt=uԍ /w/ @xz6{ynipoxh4pF^qkTrߔsIt"S=huz/yvٍc xOty{x_m6phoVgp0H^SqH%T>rtIBsZ=:u/Uvtwy{:vO[Mn]e2`L[.bQetEOhJ9k^+Yono r ̂uy6u^m.`Ndb[ldPgDEi9lXl+(ocr ₂vSyft넚`l{)4{Q't| ,| }S}i`xb@y ZnypQy͕Hz4>z3{='W{s|F |솻}Pcꑮ[\ݐp^QT=`LYc7Ce9Jh.Mk!ۈoC釄r|3vjyc^F\`TdbLZe B֌Fg9 j?.Ym-!ƈzp;'sxvyc/`\bTWdKgBi]8̊qk.#9n!qjtd…wkz8b ,e[gSb%inJ:kCA׊FmC875ok-q!ttvy{@`KkZglRjn^J oAqw7s6-%u!3wy1z"|_ŠqiXrQpsI#:t@Fwu6w%,x z {h6|j}^]\zW{P%{HV|?S|6}6,/} ~!Ict\ VNDžRF̄ʃ>J<56X+U* &oɁw΁R[&,TM(E=_124tM*‚9Rl8ZopT MEG3z= Z4/M*ۊXJ߇fԂeYSL5D<Ղ@4 ڎ*}quBpB|nQН\K^DaA<2c4ҕ7f,Uiy#lJp# sdw2zQ ^KU(`DoDc<Ȗwem4g,8jj#'mp mtG̉wuzQn aK?cD8oe$Ҍu70ɊI)1 NӅሪ 2odIԢ2\C_9<띲a5|d. "f&'imp t#wzI<_Ca+<ќcU5e-yh6&k*nXqM čutwzICaRCZ-cI<eL5jgl-Ηi&BlpoErK ތu\(x+9zHǞeBg<i94k,-wgmP%oo]rt w7Kyn{HRjB&l$;m4yo8-q%xrG"u (w> 3yV`{'|GaRoAk|p:r3sg,t%.evhx 1y c {}|~*Fw@cx]:Zy3y,Qz${q|K} ~JP~E<@9 3ē82KW+5$pՇ^M cTHCi>y]8rZ1+" $0+uW$*' >܃:C]o<ݤ#_65a/?4dK'g jcm>pta=wzB_t<ʣDau6)c/2ue'hd k1n7$qAtwQzBФa9a+3`[,gZ&NS%Z„VUKʂ,K>49w 3,&)uIFwibg>`l8ˋ2 ,h& nK!Šd^B aPTVbWMdcZ3yf%]Llh`^j/cOlgM>YoAk%+Lr5ous,yvޚ_WؐaCZb]$xd_lfb^Vi=eO+ki >nl*qpAu"sy7wM][2_] a`;x)cbkreev]hFh5Njk=mnI*qqttxwZbo\db^fvaKhj+cj\fflMiEoJe(j)o55tߋKo߂NxQmUfbPY^V\HuaP9em(jCL'o\xt˃_JN x(QS*mxT՜aXU\r:HY`$9e)(1i cotcT[odWOgfIZ1vg]Lji`\xkcMmg[|ƉVf}lX}v[f}k]~ ``~=T1c~oFf~7@jo~&5n!,rJvaIS;~`VBtXjA[͈^^ևbRbEe6i7%m"qSu؁>QfK|sT>rW.hZ%]]XQQ`ĎDdh5hL%lg^pͅnuĄ|P{SzVr8VuhYx]\vQX`4EDhc5gE%lhpuUnP{7RqUߜgXt\\+Q"_tDMcm5gt%k5p7 uыjTukxWawlzZAlm]J`o `~TpcF r.gV6=t k3$v(oCxs{wiWjZNvk7\kl_`'n bSoeEqth5srl$\upOrx4tD{FwۉgZKh]`vCi_kPknbM_m eSngE8pj5rn$!u.qawu(zxcda~fctgeii;g^nkiR m l4Dfo/n4qqg#tt@Ivw0zAy ah}cmj\s5ekhgmm]?io)QkJpCmr4Ppm]c`MXc MYeS@i2Ml<"o.bs]&wU}VuAYZl2[ZbL^WaLHd?gW1kO|"Kn{|rӅv|V24tyXk[1a]ZW-`Kd?~ggo1j "Hnmrev'| UsXkZ5a[]e&V`_Kc?bf1jr9"hn4 r6kvnMoTy'pWioOqYZGdr>]EYsD`sMtfc@XugQ1w2k$ Dxo8Vzs}WwnnWxso>Z4np,\dFq2_YrWbMse?uh0vln xxp4Hzt1}wրlZwm](no_cp!b X}qddLrg?}tJj0v mwqa=z'u|xV~jEavkhcltle bBn g+WMoiqKqk>rna/tqvt7yRw|#y}`gsgthikjXka klV6mnJoqpd=qbrh/bstfuvCxry-{b{-{doXsfypcih#qg_irU&ksImu(=ov.rYx(/tyWw{^z|ytaz@pcwzge[{^gc{xSi{H_k|l<n5|.3p}s~*lv~y~w)^n`f c \ueTR.gRG,j/;lуt-orq}uy$u@\ݑm9_dad[cQfBF4h4:\k-nF]q߆u7gx)t\Pl^Zc`~ZcPeSEh^:kA,nLSqatxt[l]Ù0ct`nZ7bPNeEgב:jďY,mیjq"tx)9xuTov,WfavZf\w;]YQw`Fxc:1yygg+zk4{oC }Ns@wwtsWntZ1eu\[v@_QiwbuF%we9xh+cz lh{kp/ |t+wvs%Zn4s\e!t_Z[Su@aPvdEwgs9Sx)j+ yymrzqN |t~xUup`lqZbcrGdYsDfOt\iDuk8svn#*xJpBys {v~ ysgmg%k9nhb]p j\Xq=lNrmCso7u\q*!wt?xv zx}J{rk9n*iloJa=mpcWoOqMpsBrXtt7su)uwwyw Rz{|}|ohVxgiy_Tk,yV"lzL6nzApU{_6#r<|)t[|v} px~'{~mefg0g]hTjJl@nu5Cpڂ:(s(u x7zMl cFdwehf\AgLoSdi:tIkGo?mzd4o͈C(rH:t셉 wzkeb֓?cd[fRhtIj?il4coU'q߉[*tM wazIkjb;cmd[@fRhIHj^9zb4V {d/MJ{kf_C{h9|^kD/|m"}p~hsU`vydiwf\xggUxiLVy}kOC z#mD94zoX.p{q":|es}`vz~x{bulm[vCmSvoKdwpuB.xvq8yHs-z)uK!{9w3|by#}z|arvZ swvRCtWwIuAxL@vUy"7lwcz- xz!ny|{||}~~c_@p)lXiqgPrTHs5_?t}r6u,^w!xx{z {L}o]n >WoMOp~Gq>>s5t+v ( wMFyV&({$|P] mVqnO)o֌G/q>rl5s+u w/o@x,z׃|\|mhUnmNoDSFp~#>qэ5}s\+t vNRx.Wzs|V\WJUPz!XI[XA4^A8‡0aj/͆d&hją@lVzpyAtxV̈X'P ZHLJ~]R@` 8{^c/f/%؅@imFI~o6(q2-wr$uot!wyz(|Q}sKr}tD}u=~4v5~vw,~x#~z {iR|}y$~yO{}7J{}vC{}9\ 3^+b3$eȌ iV^mdfqRuyEX?8[29]2܏`+Ic#fejbn/Sr uyzE_jZ?"]U9|_2bl+iseE#ތhZk=o7rv;EyDA_? a8c2,fK+h#kVnqtwzzCd>Uf8C hm1jx*!l#?oQqtU酩vyb{Ci=k7,m1 \n*p"뇒r!tw y<{ }'A&q>كb::ވ4rb.(!B+ɀ Jpvʁ=V8Y2ז@\^,b_U%bfimquy=͘X8[y2J^,G`%c0g7Gj8n=rmZv ky=[#8R]2N` ,$b%g)evnhkʋoɉs#v} y=8_7ٓa2]c+ɐf`%%~hMk3nϊgqtw=z<~d}7Bfb1zhP+P2jg$ɍl\o͊q߉!t{,|w$ya|;ёi6k+0l*ڍxn$p;px֊rjtwebyR{7}J:>q@5΍/rl0p$s*ct$ v>wy@FzЄ|[}mq~:y#5Ey/zw*{-# {$|}D~"$]94/v;)`G#fہ^ہc݁ 9 %4cA&/2w)x#΄W Hp[vnҁ#8\4/)`醔#qa\a΄΁>y7[W02B Y,\&_ %bfQwj 5n-nrDvoy76Y62&[, ^U&1a $9d&gtj JUn'rv-y7[l1],v`;&iHb aePhl0 \oCasVvy6ҙd_1a,+!d&/xfuikƎn uzrt@u"$wz60Cdd1/fR+EhD%˒j` lm8o*Eq .tw; y|5KiZ0j+/ul%jngjopZIrxt wy[({9-}W4p/;q*ڏs.%,tH0uKwيy Czgn|K}e ~4`$xH/y*y%z8B{iX|W}8 =~*6~;84O#/l*: $ǀZ:o>= eȁ]7JI3΍C/(*$ ҃-17 @cׂlt3[w.ً`)Wq$8eY: ) DVyGip^;Q,_TD~AatWUrcfZfe^XgaJ joeq9Omi'pmtwr*xv%\[T7^Ww}p_ZJqb]Pe~dX`xX/fcIig?8lk&p3nt rxvdZX7N\[Z|^^]cq`` dccWefIhi68kl&copDssx,w+nV_^Y,az[co^ec`hAVcjH-fm]7jpp'%n[sJrvw^xfSfV hyqXjHnW[ekbq^cmUaoGTeq7Bhs%`mv)qxdvzCPqoSp)wUqJmXrmaY\)sT_tFcfvJ6gw%kyEpzu{ L[{dOV{uRz|kU|F_Yf|S$]7|EpaL}G5e}$j{~ /o}~Mt~;Ha}LIsO‡(i`Sa^W9Q[Q7D{_}5Edg$lio:nātrgF|JYrrMh Q~\UPYNC^u4co$xhnCt7QhET{gI7qLgPЕ\sTPYLC]4c J$hPn otE"zHBqLB-g>P/\5T[PX2C]s4b$gKmćsn`tQ4aTN{ cZW^oeZcrg ^#V[i+aHke7ndi%qpm@tr]xvl^T`(WmzCaZ=nc]Ibe`vUh&cGjgF7Smk%spotts#xv׍\X^{Zys`V]@n9bf` b8dbUKgeGii56ll%1pKpXstxUwnYV_&"[WaNw]~cnl_eabXhT?ejF6hmG6]kfp"$os"sv3wxމV faXIhPvWZjk]?k_`mSAc oEcfOq5is$Dmvrx~vzxRn~UiotX pj7Zr^]s[RNatDdv5hw#ly9qzu|Nz|zQ{ rT{ohSW{][|$P^|Cb|4of}o#k@}o~Ku~_=K~iztNqQcfUP[XDO\ׄB`3erZ#lj4o9tyIuxLfEoOe^SzZxWO!N[eB_3dv"#ij&'ntXKH)x_g`oS9icEAlgB5fnk#qou*s:y"w_/WT`Zvbv]kdS__[fgbRheDk9i'5nl#qBpYtt&xw[^~]at_c=iae^1d gQfjvCim*4xlp#@pssv>xxXf=|Zgs\ihs_@ku] amXPdoOC)gqi3k+s"nvrxw8z$Un{,WoIqZ^p~g)\q[_sObt^Bbf!u3Jiwr"my"qzvv|'QyyT,z\oVzeWY{2ZS]{Nk`p|A\d|2h}*"]lJ}p~7u~kN.mwQmT5cWoXZ儙M?^@yb2f""k>Po|ur}KAu|N,l]R4bqUWY=IL_]#?aKO1e":jubovt˂5}BJɗtNVkQdaT֓W_XKL\n?`Ím1eS2"Zjo8t|JytdMRkQPaT0W&WK[?`>1d]"i^ntjHgXQozZhXTp`iWejZZIlq^+N n$a@pe1kr:i tnwrzvƂeTyfWnoh"Z8e!i]0YkY`TMm2c@8oCg,1qj St8o ws@zw!dWxeWZof]dhf_Y:j:bMl8e?nnh0pl spA~vt!zHw`^w9bT`mcbcIeeX*h gL4jMj?ll0.oorrfuv(yy~w]eu__g]l8aIi ac{jWelK5h_n>@kq/ns^}q_uktxbxz|Zm,t\nbj^o`a(pUcrJJ?fs=yiuL/lwDpCxwszx|1z?VxpqYyh[y^^HzT[a0zHdE{6ri3ctdla&vKofxgrYzu}fxlpisdBd^je[l%qm2rp4%trwMucyx|zjfkch/ldZimQmko"Gmgp=YoPrb2q]t+%`sv$nv=x5xz#{|i c,uqaHdvXfvOhw|F^jxSOxf4yi]*UzlM{oh|r~Uu y$]sscVVt.e(NufFouh=zvk#4wml)yoAzRr{uR(}lwBz\opiUwqkMrlEsnorHsx3PɄUJ;eX:CZ;]3zra*2dc!h3k Ղpt*Hx?P<}WIă,ZB];2_3(b*]f!Aim7m ۂGptxOC]!H倲_[A퀮a:vd 2f*/i!lo 2s v\5yN~rbsG~bdyA&~mfy9~h1~j)~mJ Bor uex;dzL{hNF| i@d|@k9|mH1G|o*)>}^q+ }sN~Su &~xz|\KxpExr ?!yvs:8 yt`0pzu({Dw {xp|z B}{~|~J(uz/Dv^z>Ev{7Pw{/x|0(yl|zZ}{X~C n|o~z} ~uI"sCtW=yu6u/]v'wymvzO@ {}'~]Hr؆C8s=tY46Su0/)v'wB){x{y҄! {BJ| ~"!Hlr'>Zt"]adh mqjuoy1V~-EY>(J8\#"_b_+eOi )8mFquuy1rX-[K("*^"`cWgBj =nrvu'y1b],ޓc_p'a"^dv,gJj]/m _LptEwNVz0pa,?)c'kf!haF\jom*pt vsw&Gvcxy{0,f+hx&jp!lg nQ]p)sU ufxz}/Um+-oj&p!cr{t `uYw y{~|~/Euv*pvs&oww!Jxxy}2zh{ PQ}}<~"~.և|*7}(&$}!~F΅~{i JHȀi8V.*H>&!IZ{Áă; 5ցn:.*vt%߃ )P|| <qVO[Ns]Qww^Tl`Xf_c'\Se_Dhjc4kh?#oRl,s]qBxuYqQ[0Tv]Wkc_T[_^a^bRxd}aDWgwe4Cji"nmrr wuW`U,YNXFu[`[j]]^`}`QcUd%Cf~g3j%kH"snornrwlvS\~IU^t'X9a)i3Zc]}]f$PahBdk3@h{n!lqqeu%vx Od|RGerUggXi\i[CkO^mB(bp 2frk!kttnpVwxuy$L$l{NmvqWQnfUCp[gXqO\sAl`t2eMvC!3j5x uoWyt{aGx7xJxoCN3yZdQyYUzVMYz@y^y{s1cf|! h|n0}]t(}jC̄vG`m~K(CcQOXjSYlLW?\1a gsmps~A umDڏl6Hߎb(MWdQ8KVL?F[a`0`!:f†@ls}@jtCkHaLJWPِ?KUq?:Zٌ1`b!yfnlŅ`s}}_?Zt~C,wkGOaKVP@KU#?YZ[*10`.!flms\\]N~_QtJ`Ti.bXr]Sd\Pf_Bic3lhO!olsqqx6uLJ\[Q}]BTs_Wh~a [ \cT^bP eaBVhe2ki!ogm`sRr7wv3RYUf|[iX(r]\Zg_]\!b`OddAgg2PkkN!Hno.1rs%wvʄRU\}{ W^q#Z3afk\cqZ_tfNbshA ek1iqn m~qquDvxDbR-cyXTeoWge%YiyY\kM`3m@?co1gr[ hl2tpwuy逢NkwQQmnFT(nocW-oXZrq>L^r?at`0fPv# *jx oyu){~QJ3wuM*x-lTPYxb,SyKWNWuyK[vzz>_{$0 do{ ip|4n}XtS}|3FLsIjMD`Q $UUJwYhn=^/c hU:vm瀩s܀zCwr_G!i]J_NUS>oIWΊ&=w\/aS NgmhszBBLqF2hJ_)N/TRbIW+=n\ /a g)/m2$syAęq{EsMhIg^MTQ钼IV=[{/a& fӉLl,sp턄_N{a5QpbU fd`X|ZmfK\Nhq_@zjc1mhS Rplntxqxu^Qz,_xTp(aWe`b[Ye^`Mg\a@ie0mi p\mZt rMxcvZ\UIyK]Xod_Zda]YNc`M f?d?ig0ll?kEoo27ss6x v퀪XD\ZwZ5^m\W`cm^cIX1ag*k/jnYn}qruMwFxe~TcuVeollYUgPb&[i7W^kGK&amo>eGo/Gir>m7tqwvsz|QWkHtISljVn `Y?ohV\QpJD_ru=Wc{t$.gulwpyu{zLvr8Owi,Rx=_?UxTYbykI]3z6`k*%eZWajl co>uAsiO=kBjRYakUlX lXMn&\ABMo_6 qwd(shRvlxq| v2s@gR2jhUa@j-WWvk[Lm^PAna5pe'riumxerK{vrfUNigPW`hZVj4]{Lnk`AVmc59ogH'nrIk \uo ws {{w pb~[hDd]_$e`#UgbKRie;@ckh4sn k&pn9sqwuzxno;_=bDf`d:]bfTiehJCgMjC?}il3ldn&nogqrt{vwFyzm[i|er]j\`.lKSObmIEeo}>gq43js& nu(qwZu1yyy6{kiWt;cZ0uZ\uQ_ZvyHb@w\=eTxC2UhyF%l3zpp{ Ut |xX}iT[aVYYcP\]F_g5e+i lӅp݄cty(^VxjWY>O[G^?(a5db+hz! l[p|tx[xiPTPxSdL9y/VICyY~:y\1wz`'&{Ndk|Ph}m]~r;v[vSSwYUKwXlCcx\[s:x^1yb0&ze8{j }nac~rwZ^u}USuX| |}SZelzLfzEh{&>'j{5l{-qn|$5q4}s}vz~cy]~|VR7bKdDfX=bh5bj-m4#oԂir1u:xÁ{쀇QaڈZKcDe!=!g59i,lv#o; r,<=uPYexz{Q`KEb؋;DRdd=f5/i=,kڇ#n-qot鄼x"{pKۂQ5ENeT>.q|5t }Yw"~yu|Esn@dtDo:Ztq-3wury,&vs$wumy%wzjx {z}r|}Epmw?qBxW9r?x2sWy+tzO$Zu{'awj|x} =z}|~g~d(Dm:>nT8p&b2;qZs+Hr$tGen 8{oK2p+*qރv$sW:ubwW yt\Z{}Cl[>5m\>8Jn1op+&q$rޅLtą v҄ y {5g}om;UQ6)T0mW**&[D#2^Wb#~f !k>IoEt4x;S5㊸V0/Y)\#l;`;}c!g %l`IpLtjx:4V;5mX/[)͇^#G:awePi +Amtq ot0x: Z5]O/?_)nb"9evDh`k 1otryvZqy8_43Rb.da(f"=iGl n @|r%ւTu?vx{B8 Ve3g .gi(~ik ">Mm7ҁ>oށ7r Vt wzy|70~l2~rnl-~mp(~|q!~s%~tv Exz&|R~06k{ru 2%{v -S{w'{w!|Fy|z7};{f }|~c}5~uh5y,|1yE}Z,y}'Vy~@!zj~zK{ |-}~|~vewy51x?d1d,f(dh#[j^m&io2r3 j[uwz&|0ql*,n%m'o"oq)3$rt\v ā}x:zV|wo~V/vt, 4u#'vC"wW&xy{ |n }I~y/\}{k+}|'Z}|"S}}Z}~ ~~~iB [~v 9E2h/:|~+{|H';|"9|$|}G}= ~j~qE/|'+^|'"| Y"9|||} }-~~ށN-R^)bzf ̋$igmqquy{,Z(M]r$H`&bei" l'p!svzb+ʍ_'a#dbfVitوl] oirمux{+"\da'ajf#:hJj!mÇ#o \rHυNu)NwzW#|*k' !mr"oo@!p.rބt41v9gxz_| ~f*js?&Մ%tt"׃uCv!x" yy^z|ATY}S~xOo* zH&{"t{|}l ~&~ ^"ڀQ*@}&~+"w~$W/X I /1C*vM&"J@GO=v LyzpN^S  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~mft2!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  BYC$H3GcRKD%[&Jv(T`O0eQ7IgW@h\JngdVxgIi_frsi##c(q`vR^Qa}Cy[١LYyW1TيR̤>O ׌RL$LBJ?I%H*H. _IFMRJ8/K7<~0L8|іM|*ɷOq|3ĻOuxP/wiB#BPI~F+UWG"K\mI(=chN1gP6lKVCfj[HI keaVchh.`grkxe~tIԺQOŒ8 RDMWSЈU9T0U,VűW^E>QAA]+D#eGt*=kF*s|M9xkN?QxTJ v\Wo3ubdYols@mv~i=_e]$c;`iQ]4Z*˟YU=֝QCᢎOeN\ Mj!NFsPoRW/S)lITVt_aW2oXYGlZxI @=T?aBp"irF;*unG.vI5zOB~FQCGs2}XxU36yadcuismqq#el_0emi,e 6 a_;^ijmZ;mQUPxQd꧙O^rNﶞbBO7cZQ0UuS9-U~OKɵV󂒿WnY0I`Y3m}[뫊[l]+K E[>V5 QS^P횥uO!RmMUEsX|ZSYYY建Zj[\F]{^0y^j/4_LMP<t]=d> :nA&wC.AFD5{*@1,E;ђdK I_(?OVeVOUc!^nvjqh 8|srvqqDJlGg6}`.[YEnV2W#Z꾯O0^=뻭l`bWfeeҨef£3gΞh"h)BkniӔ5iNЏ܍j,NU7&c9Xo9*|7!:'i?26C;HTFSEMSZmRaҔ\#u;cp?{znQvkgp4>fj;/a?t-[5nXaɹܩ]搹 S`ҴIcn YV5$f8,s6A|7!9'<.?h7EEkHqNO_ÞXyu^aΈlbP~xq{tl=©d-@^p^cI۽.f߷vHj3εج6ka-Xm쾬qPmo.opXFrVr_(cTqbzr`~Ms(ӐD[5Oh3xl5e4w-K73%d:D-z>8B|B!FMaL_Sqۢ\"hÞ^ޏrЭ9whyTzGqʉ=gj-1c ۓ˭кhmIԳn;p9`DnrEZrݓtt\tʋ[tcӅvusu*u䘠{wKntyuo_3&l3?z4v3A5$-5(:02>? ?cβBIδH[XO0n |W鄫ga~&n\~ׂxkYl؛6p`ɭStrŅvlym۔yW׶y1*zwzתz>~z}ys5{xꘖ{w.xxwcb1gn120r302k"AI3'W5,;r:ܻ@BHN EWKlJkSلw\{򅬾grv䥋4NPH{wLݚz,}bOǵ#>3##~ɂP\@m}mK|)~Ӡqyqx~wuUK3u}e1t/Pq/u0E0!^1%$5-`75Z=CRuWvtO~hrJީ{rUN]p𡠽mSsl֕^Jl 4%k菘jo,2 '%jN#Z!$..&>1-9B->UCrJ2S^ݺyߔocLɄہuؒSruSrʾ2q=4p-7lywl~Ikhi̝"j”yhJ#he?%Cg䖦ir+]r' 8$Ht0ǻג&%5`!᎔`|`x݊_TN! !1\m*_7JrPZPt~QOЌ~ R_}RcƄLztQl[uEo^ l[jӣ,#cU7K5hKA8GЪTIUNqQ_=LZkZpb.r|*:k {suP"l[JueQU^-`X-JtRNM ,L v QM^Y(VvS`]ngdjiJoee{{j`Xj1\֗owWn&RpO9 qL)׉qFH@!oeG tmFPiEie]EudC_;D j`KC]XQIyX5Oqx^Ukei\pgk^6bw`7_ (d/ZxdU!eRg2O*ai!Lm%jI+j>F$hBDdD;6aCH:^B[AAkmV@T\@T m)}6w'9=u-Մ+I ;zPLBtrVhH[l]NLgcTdBjX+`Sr9W]~\Y`\WU^ORWacNT+`KN`YIXʽaG#ؗaD1_C_.B[AAPxV@>S?r&O?GNC2GB z+xo9s)]=T+Q~{F5v;Nda%Mbg5O^qSW[z TKWsV(Sz"UZP7XGNo5XKY*IZpFXD9WC.YBZ @1Tz?-=N>'K=VG?:lBNNCCKcHTJ%[Ls*bfP2dU8;qe9YCoeM`NefYo`ednaubN}j% `Jrm^7yV[4\~X8:V1 TXIxQ Nw~Kz鏊%IxH*H. ,Ga~(II|{"Jdz"ևLzZ]M8+yNMuxlĻOux.Pw :&ACCN FCV)H$$^J*dO34hSk;k+WDP|kP]qNOgf2GCSD_\mF%FdvH+k/L3ZoP<2sTEwrHZOgo b-[Nkjiijtt(f{d2D`;\^GTZ)WS(SЁOMj LW@YJ׍KsMf8N܊P[R< źSa1Sl̻;T6+gU~ܱ UP{2?>K BTNA_Dm#g1Hc+lJ1sN;uRBtvBYOu`]oikJlrvi~efɎDxc_Ϧ]!aX"TPլ|P枞N\ MjK’|MZP~rER8sSƿTHUSш_*V#X{ǮY۫Y8~B> K>AW)?NbC0$jE*qGy0=xM<{tPFC{-UL2yp^W]uiflIoo(yn{톆hqE9d5ے^|a]\MtZ*˟YUnP횥u^NN\ NP^8R}T U&X#C YOZUĭ[^#[L\I$D,<RP>Y?gd> :lB'uE/{JH9N&BRL_߀ZZ{bi&sm{LozNkKkh {f.b_hg[v$UPQ챨EP횥u^NNQ#`TPWxX 򜖺Y7YP"\ ~\'&] - ^eO_GDžHFP9eR<_;Mh= qA'yD0I:LCQNJ҄#X[P`|k@y0k~Ttt{n=Qj>eӦ8Na¦/\ƕWU :_R\#PbR-DܻUXmȎZcZ;>[񀛄\%]_\/`9t&a+~a,WcH<;:jSI:O`O:k=l!uU?'}|B/*F9YYJ;AOM݊U[4)]kh |vs{rћB~lU9hucMc]Wڳ0S;8R誱VwӖYc*O\fȿ^$šƶ_`!*ǫUaٗ`bCcIdhN(9doeAJ9<Vy7 c8Po;: szI,x%CV6YIFBƑKJXR@XZk dn|oqBu~-p(joeF_0ʢ W椷MUmW&[o넭@^씫'`hI`r7d[ߢJ1e̟a]e\e󨔓h&jh ih7&`N6&[5?8f82q8_%|7!+<*[?26Dj>іIIhPY8Xj a}flFGziy+Ut򊓫jnŝg`%ZmW恸@b]}\!`ȲcFN1xd߫goh;|QiBk:c$kO|tlzk] @m'ĐNl͋eN6&[3$h4r$v6l[8$::)t)䞗FGMYZ TGh^,hق=uؤy8rq>k0$b*\^-cf;fD hDSidm쾬qmFNonSFhooCꆰpSmp͐GAqirIP4L_3&n/3 {3P!4y _6&<~2^@;BCDdHLQPg,+Xq{K+#dq G?w•>p']dM?aKް+;g&>ka S:m o Ÿp=r/Ѭs(Ȏs ɣtΠ[s!;tƀ~tx>}s{yXtMS3:e1q3;\}]1}*2xr3#b 8g-|?=A8ADP@FS1 aMqfLKTny.`՘l+v}u|ǵuð\whS~)kvo's̤tڼvqww2r菸wImڋw튪ɄycxU ~y|wn{w.zv 􈑧xvxUV2)e16t/Pq20E1!J5*19j3+?CoBN2YNIasPwBY<]evAӈ!ϟ~=O١Drw[x"Ϊ"|Ŝ~~M|亿A2~M@_~閵8D~ۯ0|jH{t{}l{*ĴɁ/0} d{҃|x#v,vϘts rA^)0lb,}J}9+Uy:+l8--mn/$G3E,i7p7ρ+H{1zʍѬx(kvnjܣuYsۋ6ry(pX]|oчL<@n`-*px*C@+U.%c1&%c+f.%ϱ319A@19>T}zD.jJk,OKZـnU-2JbTAG)Cz}àxXvן锴vG/@u;y8xsߖpp𡠽q$hlpkQm*6l鏖ŐHkjcAc֏5bbx5},b]>o$ XW x9eˮlT0)a;K\C a- KxVHMb6mn,|ܰw꺝9i n`39w\y]^Xk_*Ԕ֪<_mq_ P)_輽_`4_Je_毺Ԏ_i`[*ۊ_\҅_!8t* Do0/?[ 30x* ;H&D^âMfṟbWɟ[bnbT{ u)hԬ]+Xd)WWYFؾҝZ9Z)Z¾ [$Kˎ#Z͹][ Q[j}F[ Z).|yjˮ)[?!W/*#:B[DXķe"Mj~@Wz"cF 6lm$_|*yr8 h1|^j+͗;VU/TnT-SrTJ-UchMVԋIV b[V#ɋوAVQ[VMLVB+~DWwv}VY{$x0ג[0+1;@vDRNidNX)pb}Nlwpw(PojIђ*fdp:^Z󼽍`VUoƖRQ֓qO󹏏O\Q;>QT'Q|"끹R0G~S(vYRexyrSYzTxv(03l#|[1*<@ȰCJYO>^ 7XOh܈Hbu|%!kS|zsuP"l[JueΚ^-`W΍Q]䃍NLpMmHWTGvK_;ZnLfbiJoee zh`QpjZf~jVlRpNxoKV+6pH@!oeG tmEtdEudDNaC{_C^Daa Dh8] ӮI 742$XpMq.](QjCcWg0kf];bv_]`:Zz4cUT0c2Q߫eOgJ0f0HzfFAbf5DdsC_]B[AAkmV@)T@TA"lU0:~oZq$?;/9)YCW3|/K;tTE~m%\MofaPcrgaRU` mrVa\2~Y4X[TՖ'\PN[^M]K*y^H`^FU10_D2U]Bi@\gA:X@%gR0?r&O?GNC=J lʖmf$#0:(B$/wcJ7oS@iYD~c@2`K`BAeGJ^Cn)O'ZmxPyWyScSnoV$PBT7NzYKZ%XH%+W+Fl~XD9WBXA`$=U9?vQ>fL=k:H<]E o7?1;8#xbA*pqG}K=Q$L JPKONěQJåQ'H) PEFPiDh{ȷRA҅O@ߓOh?0M?/N=k:H< *D 0(D# ;$AFAEdKcHTJ%]7Mx,aR:5Re9V>f]IGd9bQejq[9bFwd6a1xm]ߑYuU0Zמ{pWm}UZTXIPNqMχlKz鏊%I%H*GQ9G|4|H]|J{K{L/#y*MpxJǤMuAyO7uyOXfr4C"j;<@VFAEdPEZHO%_M.VcP4gU#=iYhE+iaT+xh&g_net2h6Ub%OoT`."zM\}7YWM4T*SQuNیK떍I{H*H*I}>oK>LBԀ4Mh}mN|:ɟOv|/P9EzQQ w߹Q*=tg4 Ay@4C?(ID55REWYFIt&^dJ-gN3slrTY?lX E6k` SxiEgF_gzqidsbW~^Wz[#TX6U`\RƏOzLeOLT:ZJٍI%RJjډKcKxMEΆ׸N$OTȁȫP?Q1~pQ=zSl7{6Szx8V@@?8KBTF!)_E%eI\,lM6+npQ=bpVF1p^ TrUlNfa`k)nnNg |ucڌ`n.]%[YZ=VRrC@OMjK69yYJ׍Kk*#MWN\P_bQëS-NS]TF~W'U?{jVS`z:D>HBY?(Tҽ3O4PM{ LW@K NvRZݶP KѣRVqCSTzUeC5UW@LW~Z:<D,<RP>Y?gdA#kF*qI2VxM=zRG3zYTHubc4qkrlxx~isVdbh`%c]4YU=֝^P*0N\ gM[UJN6QYD]ԥSS0ZUӑ/ŞV sƿV;|Y~h,XE[|};[a#ͥn[MA=#=}H<;:jR<]u?gA$epB)vHE3zL=~FQCGs-XUY{_buUisKqDs$kTCgS\ bm;5^.&Zķ$UP횥uOm OTʞZRݛiUMV*mĻX_X|YEE[q'\ըf\y]ԄԠQ]<<J!<.T;``@;l~?#sB!*9{`F4CI^;NF[KTR|ey\b|ftrr)nj7(e-ɟma#bE[>{UgR,P횥ubS~>[UsX|ZqYZ [N\XM˯q^8㪽^_Ge#`䞚`ƅgWc :o<"jw?) B1bG;JD1kQ Qg9Z+b c"synGr|LDal?fgcLੇ] MVzv S[vTRPLV037Zb_ĥ\~ﯡǽ]ži^sȚ?_򽗎haHXa}\ciӏD cŊٗodTdą@:L\7q]l9f82rwvȘ1^q]~bj%ɩcҴ^Lѱ;W?STmOVW| lZ쏪S^~_Rw bmzb랕_c)7dœf<ᓯggfsg-hgWpCz9/P4w^j6Dj7=u:S!;a'd=C-x]B9H#FQArLQS[_ᐨ]th.{uUudJ@m/vg6}6_|̌{8X|vsW`ب\u@+_ϾobaQEdf|Ih墨i~0\jLiEjkkk0YE6Q4aF4Rn@4z4J9%:+et=n1DcAPHLTO`]zYxtde q'yrE qxڵAnjb5a-[5;]w]wʟbKe黳7h1iW/jlOǣY/m𞠪#lm(:n˔eozb nZnL6W2eG2Iks 3f~?5\B5X"SK6'i<3JKA+>IѧREI_L\iUr`S*ld|DowT$m۲I'd7s`p˳giXXbl!葶'nockp'q-MphՏqIp񁝼r򢙏sQـDs(H|jtߐz?sxN~5Y3Re16t/Pq2v;M2951&]7?,Y>=#B!G9دI[B]QroL[Hh'c׊;x]*|,;4r;ʷf,;qj־nr际TAyӟ۱o ;(u=WΥx+i7{C@;{+3`o|lĈ|޲{졭t|z{{;^\~v{q|y{xꘖyw9 x#xŐvx{3P2Y`{05m,[t~,s.<R8-(0P"B\3*62\> DmwƿA;OJBk©R*S[8djϖϕ&sֽfi ܇~vBU}ƒ-{ƀﬡ O|!(zAx̀oNw%uFqs~ofr~ R{0dg-Uis4.((;j)&-0f&40:: >'o?Py ԓEve7MGx͸Výb_/x;vc{?fԏX˴;v'3@}U볯{M{y^\xBu=]vstS rqהq΅pwX@8Ez'rM3ZQl,dv]ύudsl偽r螶qKq SǬnoצjommWck3kZiÓxi h ]+Em)9~}#  >vvÒ/#,1!6;-G?C[AFx]%PT]tgmfV]|ȱlS8,ij1Ƒ0kAKjwȮj!jvirٟmh Th+ YgytBf]9fgepdYe>Zb+)r" 0[pQ )1 6;0ae?ZޜGqʴQ^m/~Ny]4%ziq]baxArJ`.蛴b4dc꾀cObWP\5da(bkgHbO%c#7`b1>nbdFa#e,"T {N 6Òג*m+pE7+!6"628HW@R~>4IBSOx$D]*Xn`en_i,lcdxe_+hmZsjRU0UkQBm2MmJi)o#GNlFPiE?ZeDNaC_HD j`KCA]C\ C)1Z \˘< ,z5'ڐ? 1,H;|CRGrY ZNHiYcVe,iY"bt\d]L`^(YrbaT_maiQocMeJye6H PeaEBdD;6aC(H]B[AAkmV@?_R@T5?PzِiM\:)}Y5$b>.WEI :uO>lYFfaPbfP`pT\B|X{X+YmT[Q[M7Z$Km_Gʽ]WE׫\>CsZBOZAU? P>PM>(K=VGC0T$ Β}'4 ʂ=~* ymF2npGO89hLWY@lc\D_XdI\ClK7XxwMU6@MS0ϋQOQKQIiTGNUEQDU^C:RU6AQcWRA/T?-=N>'K=2JG< *D NĹtq}51 )|%='sDE-RjMLo25fU<`K[@\a@BZhCX qFT.{[GQKJN_VJ#LMI aMGݯOYEFPiCƢP.A*PbA Qg>l-K=(Io=2JG< *D n\ێ  E .|.[u<$#nDL*&g K}/A`QZ2\bEX/9Z0-\;W e ^أF=>H5gkcve+zNmb[v^F}[bWXO"U[Rͮ@NdrKWI?Iw㈮,Ḯ>KSHMթN[PtꁡQ~bQ{Rly`SwݱTu+@ @;<@VC5BbNBcY D!aGJ(gK4/m,O8-nS@CoCZLm8bXk jggx4q d׆|)G`U\LLYŴEV RZOҔ LW@J@&@`J+o(LOM8NdžΦQ3gQhÂ:RCK}SfTP|KU~zV x/bS=^@goD'nE,1sJ6CxkN?QxV]LvN]Ylt eHgFo7pVzbjI{&e:`]!aXܙ0THOܠN\ MjOjR݅Q}R&U5&VaW2oX9Z+Rک[.32[o[. }53< x><.K"=T;7a@( kqB'aqE ,x,H5}MA}RJ]~2Z^Yxpd'jIoo(ym&.gf%_bzjB^rZ*˟YUnP횥uNɞO;QqST(U :ns>#uB+|jF3K?DPKkWYH`i vl|l^p5zijydyD_}ZƥUeNQF :O!RmM׶V(CXlZ<<[m\e󕓪](J^RN^j/4_焨J_Uda*58 A84N6)]l9g8q;!{/@+UD4eH>d׉MH"TV^jzkG~ztupPmHFpgݟ7a3\`VٮR,S`ܐVKkϫYV?Ą\ [^ص^gQ`^hè``0bb*z-3d ^ad U:;:J0F9R76^7oj7 u:S!~=(hA82&F=+KG8SQW[kׄfd~x/tJ qОFkRGc﫡]ȸVKSMqV+я[ TƟ])_XUX>`J}b{c8ؗclZd#ɏe`FfZڊif:$g399lI7HHT4c5n>6f\|7!:&7>0JB9ܕI{Ho=OoWvٓuXMibm|pdu2Ymޚ̬ex_0ʥ W}IW辳xԊ]!_Ⱦa idnʫe2g&ߟ>gch󗳖7iCĔkji׏=jC;jF;k<6xMx5WG4Ie3r45}6 ;x8&<.=AH:'FqFR%$M+V䚞Urhߔ_T~_҇hmBܠ]mkbZddSgV(sShǢj@$l ;.mlm kԔTn'lm#lm_lv =;5N5}o\c2gM1v194k52#pd:D-z@=6BlB&NIS Pf\֓AgP~xq.u=llM{bԦ._jjdEWh򹮲j1ȳpl랯\oZn“p% p:po?TqbzqY2 [rؐ)AqirI}pnIA3kP3J]15l0y125J2!f5q';4>>DM5M"dD V|VcoSPt}zԏ-p6ŗpdv.5hމ!Hma‘o(q}跶Jr3ȱїs)Ȭ s[vΉXnP՘ӮarC uŬ]xgy5狹yu"}|k{"~Px~wv}eDu}t-}6s{hK2W.h-zXx)(Uo-* 'aM- C/6%2F6 3;CнBZLIsfCS`+ѭw§OĠ-WP5X ]BrČ d~U|lѪ y.wӠXv2u$Ts΂Nr4$pӁuoLW0]+EkO,9}'! %u!"}p$%`,~!Ϲ1-m8>@? SXF{r$M8ߒ>\<{pE[O˨D{S+l e*(+v@! Dh08!0.(C;IlCiE{LYM:jLы ~zgb)iٮffz>份Og8胵gԿ;goF7hw>ghg$f{f桸ٓf!neNzd_cZ^bFY'H k& }{m=1lj 0*y.+:FڸC`}ƸNy[}{kM\|KvwRhî ^Ӷ\ǯ^|_q `1S`Sk`ɵI`mMq`+_`V3kF` ؅_t_y~9_`" sZ`ooÒגf[ H }/)n8;FʑDZӸNn/[!i+}9y{Ds+iѢhf@]BغWVDzWEߖ YaZooZîZ9 5Y[P_[ӓ́\*{YZ$ }7ZOYy-c wyjRn) Y.W&:@_DSPg虗\GwhxwTpB WfJA]ߚU؜Rw+QUoɓRSlT4U@qTǂUJVeUqhzJt Cs!lQe)]FSHVNPZݏ#N%CLx:4]EeBnQSpZ\NuOddlMnkgf{n`Œ6pq[oLsU{tPvM6HwfJ|xGDpGZIniFhjF[iF.grGgH۠huHeHdRÒG7m"M/=!8,0D(; ;PKG{YYSp/c]ih/l8` dv(wd6^h)Md&ZOui9U?5krQI xl/L |jvJ-bnGGmFOi EteDNaCS\C*5\NC\'C [D޲[גb_\pt2 #Ɣ8)uC`5~Mo@ u=XKsj`Rf_ikYaX'soY\ZX^Tk_zQocMeJ cFaE_#c CY`B[AAPxV@?_R@8FRj@+Q?"PcnP~tB z.L:O)C2xqEL':oVD#fOZ]IjbDfO_qASJ[z TKWsV&Rco7VPIYzM7Z$J7̺y[aGȓ\D3ZoC;ZA)W@hQS%?-=N>'K=VGCPM=w7H< *D ;FBg˘$$,=}7 td@5'msGV-TffQ6`Y>2[sa@uYfAlW@pDSlyDQoYHMzFKSfIIW,KF5$JD` LUB kK@ Kp>$I=9Hx=^I< *D :[> MG#x{{e&7t3|!o?%eh"F*YbO1U\DX9/Z0-\;V%d6O}@MFTDJrDH4D}EzCDG|.FAX D@^IG?\Y͆GZ=kF<\D;m-B;SA uz}|c v)(n[/Ej#=!5cEj&_$M ,XaS0UW2hTm`*6KQrf6LOQp9mMzMv9Lp=IM={F =E]@DIʤBQA1?;@?ش@>̿A=~RA<@:|?:a>E(BB5@BvB AJHARKD%XtLm)_QQ2aVF:bY\rD\bcGOcQkY``y/b!]+Aj[sHW'wTTG|Qr~JO\䁈L+-JMHSF|~F3xF.wHbFwDݘIuְJu/Kj's;ɱLWruMRrNoNn"DR+A &9A#BAN{CydUWG"K[OKM)QcZO!2BdU!;e9YClda4Ndqj;ZY9bFwd6Q_jp]XvHX|,URsPR[fM%:JH*H2|G"|Hs|dJ|NKNzLv]xaNMuxlĻOuxOEsNPhs*On!B ,@ ;<@VC5AZN{CydZ8Gf$^IA(dPN2OhTR:0k+WDP[i(_Pg{g] eBt&g bFsw^RxZ^:VpgT;ۈ`PNapJ4IĉIH*IN,K>eL!ENOg}<`O/zsP/wiP+tCS *uN Rp$A61?g ;Z=D?OrBYE"bG9(gJ/zmRV >>H>S@^B")7C<?>?;L=U;aD@!9j&E*FnG.uM:ay(OAm]w YQdt'baOllCn2i|<{G/d_^_y?[kY/WBĆGR=.N;L|Luo NvRZۊP󬐼ѨRiS!]HUSш_[Vj5[WduW~ګ X}XwzYL wQ(g>Aw39, >?;Of=HZ<d> :lB'sEn.Vz8K;{N@]{WRx__rjbqlzfIb]!aMXsG$S6L^N?Mښ0O;6QYD]ՑRUӑ/%V!.WBloXZ 8XZ΄2[o\Uw!\gz)pnujiYd2bu^`ݜY%ƯZT1݇OnN\ R!qiUMuVzXd񕀼Y\Y12[]㫆\e ]_x^\^ϙu^[|M/+: 9$9WF9R60`9i9Lt/%,I;wTFl^PeLJiW`uZ|\ဆ\W^2Shi^aO_`L7cEHbF?bD'`Bh\dAkmV@DS@8FRj@+Q@`S A6SplKޖͩm%#A3 <*a{msH6Qp> Ri>wgT]IbfP^`qeR&Y{dQVTR/gIVTNPVK-'oWHYE̡X%CYvAWI@1Tz?-=N>'K=/KG<E=IE$ +11y~p<'sF{/skjO=6dYA _b2Fe[kkHWtGU&)=LQ2$MM1OKKQH[QEzüPC˞QA2O@?CP?/5N=k:H< *D < *D :Z>O:pˆ@|y0Nw<#$=oE,cgN 2y`6N |>K@JrDGYCeD£EBC+D.Af$E? :C>qE<ۯD; B;*A:=gZێmE}uiGo(zj9eSB#^I)WPJ,tUW2S_4P0f53OeWp8Lnv 73JY~9 I/!=8F =D74@? >g==)<=M;=:> :,>99U <ޖ˘y'l`gi'k e]6`?YOF#uSMK'ZQT=+O[>-6M?^,4L,j0Kr4^I{zL6/F4sD5eCIiw7Ais7R@V8?Z"DR1A [;<@VCCN{CydUfI$[L4*bP4.bXq?b]GcfUas9^2^kg2@[ܒ4pVŤUu&T"{.PT}xMϔX/KRQkHjG}F$A63)A ;<@VEsCN{CydZ8Gf$^LD, cP4goU=g\J5eLdUldHp``2j\rlX!$3?j4?>>I8BeTCDZEq#b I*gN3si>TI%H*HdVRJ8ߖKp֎Ls}NNS|OOyP/wiQvPq RpR oR <&>t7C<??>;K :lB'tmG21xM=xiSG9v[gTRoggjujue_vYATA,Pؖ*MjK69y)MXJ=O> QaRTjهTVFHUW~WG{Xfz8>XMvHYngt ?,,Y:G 99F9Rd:`:&i3;qA'xBD/}0J;~FQCGs9{XTue$ilrwDhlb!`3Zܴ~V3uQaN\ M7O򠔎}QYPͦT%lUSƍ-VhfXY<4YhẒ%[~[xɛ/[8vK:09 &}*B/^CI^;NKEHXVzbi9 pdo|jicGO2]!aVڝRbtN﹞WO›K~RόU~VHX|Y*5Z3[j!]c]HY])}^2e{J^xN!!: 16 e?B7MP4YV5$d7q8_%zI^-_j`yBa}aaWcAblb#~c8}'85L4 [D43RP3J_3&l3?y6J6" :S)B8G"C@OmT@Yh‚g\tYyjk̟?b2NY[߬iSMWϬ k[cil†^s_\(a_[VawޣczdHΚie@0dif:$Oe[΋fleI{(674 G!4+Uw35ob<0p1}2y3 J6&g=+2D'AaL%RX >VPg拍c~)xuLV)nǐ£\d[KZ1X茳\e_ePaAeeҨe𲟳g|&3ghqΔ0h i:i{'tj"ޅÆj,27iƀ-c5] T<5sJe0V_0egM1t/Pqb/x`13#z:0B7@gIQSpg;`Cq=]s 2.f䱹]4p^漸ŏc蔵ze|h8Cj\AjnΣlUfm 8ߐjlVqlJjpmlm_ l^-m7)F}mw!+2F=41 LW0]-rj,,y,c, G.6=1#5*>=!tENDO>f OZ4lg{L2 r ҟkʭ[h"3#kTm鍴sop밯f[p'٩ZqRZqqKqhԅRq [rؐ)AqirI}pnIxqxpp01U @0 N~- }a*qpx*C@m(& &i-1'8(4׻@HTJ9bGձT}e",[{PTƷ#}zDu NUwO--xexΌwD%x^xU ~y|wn{zvДyuox,uz2wLuSv tXtjsl./F.s U* f& v# "U!26#+[3.1<CʴD]?MOF|z_ƥtUĮfَxψԫ gsŅ琹L\Z`},{ yEw@H uN9u}s|Ӌ|r|p{Hjo{π80~ ,L\- \S&r km$& @* $5}t.&78:;?VeKkvYZ.n+xD𩪽PQy尽!xH3vԯEuĕIݩtgo﨤#s (qՎ<qQTpX]|n\mWmk j=S}m?m. N_' c"% t o 'oE3*1c)kper,x4gk}VadYEζ"S҉MggL vx:4]FCӊSvT*Uf{'I`blFnl'dmwo^prXZuYR`vMBzwIwIQuiGsofH%mI@mhIܛlJAkJchKx)h.KfK7KdrÒl M/\"9. 2F>=RKR3Jsr5_Xh/l8` aB}c@\ڌfWviR`=jNUl&J ېlFkPFOi D}dD j`KD8v`_EbaoEݺ^Ff}^F|^#ETYx7ӡ42 #Ɠ:e,jE6z|PBnP\|MddiV^+XwWtZx[aV^WQO{_IMX`^J*ŧa6FaDMd\r~OX~2R.Ua2TPcGVWM!XI&+WFX)D;ؖXB.W@1Tz?-=N>'K=/KG=:^H/M=J}< *D ;/bAV9<:&3><ܫ˘^&'wz7o?%eiUK0`YU7Z0-\;Vi?}SrA$QG|CtMȆCcK*&EHbFEwG?D}"EKbARH3?8I >W5H=QG:<}y E:s@}9<8i82ޖͩm@ vx%BsV4,k@$c0G(\CQ0XN[C8_TkVdc8tPUm8MUt9?M;>J?GI@%EמAC'ʩJBAdz A?VB5=tTA<@;hB?;*A:=8b[8:_ { zTp#i l=2^f7>. _G&&XVNr*T;U/EP^U0APvj,6Mvo6`Kgx7Hg~7G(-<=n<;QU;;VЉ>H9Z +;"9;8,C9ˆ"k~YiHf<]U Gd-,aN;[DT"TL'UPR)OY+Ma.fKi0Jsj3Gy1E,5Dxd6_B%q6@u6Q>gdF7X= Z7:e6:I!B3->C :&ACCKcHRKD%XO,\mU6]\A!]eaI:\rTZ+[IzVaQTkkezO沨mzM'qJtH~ޖtFtDq?CrmCeiDkEjGHl%.GhXGfIe@IOcS/I`*I_B n>p(WB 1@[ ;<@VGoD:dN FCUI$[M,R_vS61`QZ@`b=K/2^tp Tb[i]0"XfTlP촲rsMAu&JjӨt"H!y/(FxOE1}t8D'=N?#GCN{CydZHO%_M.VbRU6bZBaaVLG`oWx^(}a[+j9dV"oR~vqNRwKivz/Io}F|FxEPuFerVGv"q١I}rHJfmq$9JnŶK0lyL~js#LiϹ'M:gN:9e|> O<'?8=O?>;K BUDE[]G%b_LF.!fQy7\fXBhd`SMEc-l(Y9`zdV\ Am( W͟1rzS@xO[|tmL7z>I血]Gf~F[ydFw^HbFwDݘIu KueK(sJM#GsŠMp NnNMdkҲOiOg%=?)=K5W: B!> K>AX:A`;_E%eI\,ipO!5lV2Bmh]Leh[c;ygN]p)YcxDTG||P?Ʈ0LٴI넄UHuσFP{H]|5Iz٦KzwLByeMbuüNtOXfr OoԲvP.0: ::FP9eSI:O]u?fA$1l|F+qK5tRBp|ZMke]ftj at\}Vr QtM"NBJ?Iw㈮6K#MEΆթN[yPQ~R_|RRz}mSwۮCUvoUrIUbpVU@nnW9#G<$1 8 v=9#iJ9<U+9p`O:j3>?!rmB*vHE3xkN?Qu XYMoc_uiqJmcqx ^~WXO"RʼNFĐQKUvJp4L,/N}ЙP~XQw/S̓;T6+IT} V$nz+VC:uVktkX+rҞXup9#%:}47 ?~8NMP4XP6dJ7n$; u?(3zF3r}M ?{UMtha_HlloB>eÃ{_-Y Sf̓^DNuB bKxFMb=O>Qc S{U&U>V pWj~Xq,|zYyLJY wRyY1sYqe=%86S6i c@5XN6&[5Of8+q8_%zIkoXtZ2u+Z֦Zw~KZr{=C[x-[_u~[r#<r'Q65L4 [D75 P3J_3&j3lv6l<'X@A/H.dF[<ǍMJ0X^|;fatozWBe]lך(VU[ԒUOSkԐVm3Y-H(Z[󗓈]0v^RN_+X_̅7/2 =2R N1\.-kO,9y,u..#;15'(>7ݜG|HkR4]ٍ\^t{p9rmeTc>h\rҾqZ,r ]tx`lcdeۣf-#eٙgʕ,fUCBhrhwxÊYgElwgӀklDYlm llalPmNn ~m]e~lEyCmV|&02n950T G- Z*6 i$/ z" !>O&O-~5.>'@PTGWO5St |d׊~y‚S7 TxAOG+rtr躶t:.tZ|ˍtXtQ$ttr~t5zu"ϐ^zr܋Ix)sVTv*ruqspc},0|!=W- My)} _%/ p! ;y $G.%y89aKBhSW)NwpnڪY^ԊsrgkPb0ƅ∾湻~,=]~|g-$l9CF~a<יU>zVgt9Ł}٢c|Jc$q])ĴhT=hMh8ﯣ-h0Ohh єgC1bgG׊gZJf;d&e`elv|Tdyfd^vk<&NcbiyjR=ǻse )yo6:@ CkVuQoȚcwɀ-yiqU9f]ޥ]^$w^薔_ג_`zH^ZH^H/\6s!ňcAC$n/1!J@1;M?px-ZLDddiV]4A{?XXŒ]Shi^aOQx_K(b=GַbgE [aCe.^B[AA@XAV BCVBVvCHUCnՀTDOU]2ߵ+"1ރ(>B+6vToJ6j.XC`:dKVZ*wZOaUфQRgSN\V\J-WG6XDoaX`B.W@1Tz?-=N>'K=I>Y7K>aJ> H?M=k:H< *D :[>:&3>9+>19>0|Nt !` }o2F1r:x iH,laBpS]5Z]RmxBN΂BLDyIYF'FeHkCXHAAW}G?8I =@H8 Y<=n<:0:N:;@9=D8,+:7w6p6N4lʖ\vylC_le' _3[ATJ)$MORQ(N\,Kf.Inl.sGuU0F13BD0KAx4>[|2mJ/;ї.;19Q/`8Z/7#/72514ծ0\ksiz]ob7mbM ,SY*! KPH7LBBkJAIHyS"=GbZ$PGbr'/Eh'C@p)Av(?G~)=);/* ; ╞,L:ۜ7,8פs-.6ެp,Z4X*4ټ,3,G;C n>p(WB 2ZB ;<@VE\EKcHRAN'WS1>ZZ;ZCeCXoIDjV\~QRSYTOߠ^#MMXe_JKhSGӈj,xEkC/kB>yf_A&aAZ_8^B __C+R`5C_ WD*]}SD\vɏEV([GE[YoGAZF{VE?1@&A5A@c;<@VFAEdPHw!VlM<(XR0]ZgZ,;[aCZnK45X[~gU*TaZdQbb#MMXe_J#jGkyfBcWDe5DFcUEdFlaVF0^cG`tG6'[G>[aGyYE=@0'?7@O KBTF!)ZH&_]Or0Pa V; ^^Eg^j[OZzVV{XKaX:A`;_E%bNm0dU8;qbY\rDAavh?R\yYmYzaU,j'P:qLmsoIGev;FwE1}t8DqPF!qRGWMpHoeIm J#lsƷJmjPKhϼKf|L]cHLblMMa?:.!*>&\0: ::FP9eR<[?aE&ifK[/VgR:e[DE?beRS_u\Z6ZdV,8l_QxrLTvJ zFyF)xyFd_uGstnݯHsOJNsB̀KVr)5Lo@Lm\MrjgNh,NGfsNqc1O`a>9#<,?1 8 v;H8NqJ9TG: ]u?dC%viI4.#jP9 j@XDed%U(as_]/LhJWPpGf;=.y'8667 :@5XN6&[6d7m >v#'q8EX-sfL8qTDwl{`U^@f,oc|_L+N}P|2@PPzrQSuu+RDt RptTaoT=ndT>iS0f:(;j(65L4 [E=4[Q3L_ 5PJiR8ap:Mv#A*xrJ17vSSE *ql]U ikfc-:r>1\r{tU;LP/\KO6PIl3CcKxHMOF7ȫP?mR7sC/S|SzxLT{v U?uTLVre7VpW`mۛmWbj89-c5] T<5sH_4K0Vu1c1k9k4u:S!{m?|* ~uG6(|PE9v[gTRliqh=evtQ^ŗ%WPBPX>%K1KAmM/P SSl }SfgU~ܱ UP{2.Vx[W vX'rОYorPXFnę/XbkZ60>7.72 ><2q K2Z$/gM1q3;\{g7q W;a'D4]LB9~WTt eHgoiKzxG`UX޴:Qԑ4L:6@OUQgsSijU:3==VΈŵW{HYvBקZУZye{|Zx>[;Puɘ [s1-ZDo[l>45E20 .@0 O.u`.klb,}Jwd.82v8%(@ 2JCD T-TO{bimv"}d29ZmR׳P*𴛗KSVJc/!Xt|YP"[Q%\e ]_l^!s^]u^[|M_=xΒ^fu=_r ^GohI2&2_306 DF.s U* c& sm(-q*yz/  4`#:-!Dj>T-P]SC]jRt'pF8ipӏub?R[[-"dVJãvUZ[̾\^V֬`.tY`xaǛbU>aWcc`b#~c]{b=wbzt;cpr 1n*K0:.+ }M)+L \% m" {# ~$C' /= 8g-|@;Bo t  !( 6/#J:7"9D/KR(hxcΊw|35uȩ# p ܠo6CؙapH0pԣypKzqL򗙫p;Ԕ&qՐ/ pPË{qG!{oznyAmU}funZyrTn\wu#/n*3+cF &[P"iw}uyr`)30-D>G Ldo]2}ip[??2LRO~eMr~s}V}E}>z|\yz}7xiyFzvxy:vquAMs]vR}su;&r t2S}qAszos/Gw@&f(U:&aP)` uŔ- չ-%x:fAƨHe^ƼWvįjʌρ~|آc|ݟ-I7V6Y9CDU ϝSmǽe܄׿{iyڼV}ob~ .gظMeŲlze~#meX󗔿f&decefKfQf3hdS}$d y!cfv'cRsbb·:p21mnC &^yjŔג:H`$#_[2z2F0@°@SJ[Oc(ۂay }!Gv!o*e6U]~)ޞ4Z'}h[f\#\GD]8RI]Vl]O[~]e|\6v]tkuy^5:t] Do~]6m)=9On^Ò0G .@%bi<>*jLW=]ltwsagiꀕ0]1H]UIf3)Q}RD٧ЇaSy_TT[2 TY|GUl5Q{VYLxGV߮FuuWpsVʧ+o[W7wmBVtiW h1?Lb}uՀ0Bl֬- -"L[`(ntoq.b0ՐwmX| Qp}L`K{|LT.y LuNC1sNqlOoHP}3mO*iOmgXPYGePlcfQHbRrt] +,;0JD{0Z3Ti:Jlb_hW@kQoLKpHE8oUFU%jCG037jcGLhH۠huIS[fnI|oDcJ}xbJyb/I+\JNuD[K5X[gc?0ǻ<)o~:,5CHe:sbWIJddiV\ဆ\UvH_=Pog,`qKbHc:EkscCe.^BjhZhCJ[{CY=YDcFYDxQjXD&WDLUEƞUEud S y`i<ɜ *ވ9_&zG3kVA)`BAeGJYNyOST NnUKc:VW?GFXDoaX`AWI@2Ts>OM> qL>rK?BhMA?;}L@[pMi?JD@ Jm>l˘ m{N)w~9#rVEE-fxQR8\aAcVfuFP7GMJEJJ~"LGMNiD)NABP`?-O!>7KS<}y E;3aAa;HOB :)?;HTA;gZA;ݗTB|'unwe7Nk~D (vaO1MX9^:GSp?yO|U@L‰CI5CEzCCEA,`G>p!F =']]F;D;WXA9P.;7}%66 l_36HH466ߵ@#v[u! n^3faB$\MC+UDYe2PoiO6M5Yu8Jy={;-G(-B?@T6?>̿A9Zr3+u$II]FqT DD\"Be#BtmN&@uq( ><}#'=+);|):A야+/8d+l5})*4V)4t鷢+,4o,2Q*r2!*˘G|qf<Z<QuCN#9I. E9LFBBsALBXv@^A!e"#>ng#>u{%<{2%:z%w8%n7 &*7P'5'4o@'34('3V)N2((I_E?1 O=B&A4 A|;<@VFAEdJJ) xP\Oh'StW0fTb9TnB/R!I"NޏWN\KRHW[F]!C?7[Ak^g@^Q?Zl+>KV_>qUP>R@PjV@S/{AUARdA`PƓAHO*1BcUN߿uBLG DC=@9'?7@M?>?RFAEdKcHPP (UsV2kVg`:?VlCIS|JyP]P"M]!VIIZFK^gD+`^Bn`_A&a?Z?!/W@+Yx@=>IBFPG!/TN)UU1X^";VjBkTzIRR<SNpYJ\hG|a0DncwCe`LAYa?[f?Z@ZAHZYBZBB?Y5νC_rWCV1D,<RNBcRgFU XWaL(XU3Yt\;XoiDViz'M8S^AUfOZuL! a HsƠd$DdBeB=yf@l|]A=c_BMj_ߛC ^|C^ҍDF\poE\`EAZ\EWElT۸G+VyF S6E>9 =0:S ::EyfAcW0Bb3CbܸD \`ӭEObΗE^4F^G \׿GA=ZGuY$HW(wH4UxDC=8r!!: 39, =L9nJQ:R<XC ^MI`(`Q 3E^QY=]fHeYvQ/V[ZQʜ`3#MMXe_I(hEj$bCKlEB?g BrfDVhDeUEdFd0ɿGDcDsHBa^H_#H ]`HZZ_IjZ%JUW}DC;=+y)946 ?~8NJ8VF;B\(A`aGJ(cZO!2B`Y?w_%cYIJ[ku1SWpZ`RӜwbN gBmJFɣm$NF|\mD6oo.C(j=DYk?xEA>iF_^i6*Gch]H^fȃHeI^pbfIa J_k{Jv%\­KD\ PKYJC:(;j(65L4 [B&5N6&X:^>kcF(g3M/1e9V>apaK$]rUYT]0T-beDOWj!JpXFrKDqSD7pn EUoFmN H~m~ЕHBkJ@k‰Jg+K2eK`c9LO`M!`M ]KM:pZ?189*4,7p5 TF5BQ.5[6da:dgCv&i7K1&hTx=c_K_9pUZф`mV&iqOmKXsnGt2EsEr3F%qlI"s!HJfmq$qKpKAl L^kMdhSM fȰN;_dNNaN^~xO["^=60>7,492d 8J3]Vu1_V4hF8k?H#oNHC/mR}l׀a{l2{ylyyjgvQwLi&puiWop()&w@ '!TA gsyj(t" .x$󑸔<3>gvKYb%[qin膻ˋ3Y%*;,|t屧р*z﨤k}Rzv+{y蘝yx~xvxUwuutԆ:tsts:r0~WrQpyppQvo3psnop#aTmRHf,ܒc)?cݨc&3_c-cYc(vtc01q|Ibxcm_vRbTr bd0Soua߆>lW`ㄡ/h-+AB^yp*ÒN7+mJ͋-{'Xa"qpzsbdxy[Ƴ~T%߽pOiсO}:QBzUQŊ uR2ks~Rnq>SWcSj}le^nkTpHMGsI"t I(I[pI nHJ$9kKljELFiLdg\LɽdcM6bN?`adM+]MuZMҨIYVu>..&"|3$. qC67w rU\HSe6[hVZ\vRPmaL5cFHaeE7^dDKMa@D`!D]3E]FCҍ\GQ!\(G}_aZGX3HdWwGTH_ S%htϒ\Ĺ~Q!fǍ3 ~Bv/%oT%@`IeJyX ~QQPBT7K [UUF*€VD9WAX3@aSS ?P@GP@tx+P@LNAXJOVB9KOA˂L_A.KBBK}v,Y֮ƻ<k & c0vXA;)hgP6[`@UvENdssHI#cUN{0Xu@^9Q!qe=L_?H– CfE[CeBbADi@fçG=rE;JCZ;WXA9F0;7m775!678892768:qlb?cɜ|G p6, g= y]>KP)TYC1OR{kr5K#Q|9G(-<==É?N;ѝ?=9k=8.5;8b[85p2N4-2"*2D+52+ʖvte\h)da9XyH%P_U*L:dS-HPt1CD2CQzX6-@p6>gdF7X;juN59Z@7^8S77Lܓ8>6p6<6N4l4/2*0&(.c"kϒtz<kG\ZbU(^ ~Z7}SE^MPv%vJl`*El*WCy,Aw΃4.?/<70:0K88p/k6/7?o35=14045/3-$2B)K0&(ik~iqc d Z%#j DS1OB:KN"H*[x%LEg(Bs)?{B(=6)7@M?@4C?(DG1HMY LV(MJa/Mm6]La|=IBFϝFDEKAwMe?QN=O=Q<^>L:G;&H;;G%;H:ޣ;F,$E=OB>Z.DJE?u;=+y*s=k @9 >%|@4C?(EEyJLW!nMzrR"h=-R<N;e;1Hb&;IrI>H.>Ge@IA>4)D?>eCC I_E>9E;/+: 8V;SsAR?X FAEdLK"OlR(Qs_3Qls;6Ny?LGHJoFYQSrB~AP6@pT0? NUcb=S:=OL<NA=(N.=#N=VL>ErLи?cM9˞>Ha?IA?Gp?FI@R FWG=DC=8r!!: 09 ::B>!ICOKv#PP)R]Y2Rh.:LPy.BXMH>JMPG%WS$CVAڑX;(?&CU9>_U=]P=dPg>R,?[Rې?N PiӜ?+O?gM(O@[{L,@cJALy>AHUAH}G B;%=)!817~ |:K9E<KBBPH!RP*nTZ3ShV;QgwzByO)K=~K<$Px1H0bUCeV_AP۬W@]Y>/Wgi=Q>1R#?S%k?S[]@?R6иAxRdA`PƓAHO*ApMB4L0BwJHBI9G;?9(;j%5}6S6i c?u8H<;:jO@QeR9GM!$TO*VW4^VieF=ySvEO2KIjKQHW[DAYA&Z#@H\C1>TV C?CVy?JW?@_W -AnlW~BZVCBU8CTMCRC?PO[CbNֶYDMQCIE>85 (E5d5L4 [@5XJ9<P>UFb!XYM*W5W4Wc=TuFPMVLeSdHW[E_e\ BY߸]H@^?OXq@WZAk[HB([4BEX^ChJYɱCVDD^UDULE%R'F/RzF7KPzqELsE=5>7*_28T3O dE4N6&VF;BZ:C!I[K)Z\U5#Ya?VsLHROO+N>VcJ"]yF,`_B`_A&a@]AY]Bv_۠C+]@D~]E^\[`EAZ\F8pX׽kF1VG,UKGSqH:S]GblOdDC; 3Q75Y.0r &;.2 H"4P3X8s^@`I*%^SQ4\^.A1XoID_TÃRjO YmJl^IFb C}e "AcBc6CcޔDdLEaJbF`cG`ŠG^yG>[HOZiHXBIUnI]TJ.R?81"228/ ?x. LW0Vu1]5dM=,eEG*icAP^3^]IB[lK+WVQ \GL!cGgD3jC'i14D}MhELixGw^iHfSHdlIWcFIa J_dK6)\ͭKD\ ]KXåLMWMLBU=51D(=/7 . E- P%+ [0e1iW9mkvC'iM4dXAbb_i/P3ZZvT aN"fgrImlDo{E|@ozG?o3Ho6ImnK6lKi^Lg#MA(eİMbʬUN(`;N_ ]OO\SyOZLOX ; 2h,"+:(_3=)I>) }V)l zb,i/&p5s?'JqH2 kUA.d dISj]{o^gVfzO沨mzJҽrFQw[ZHRwLJvlLEtƢM5s+O_wrvOoQ|3mQiNQgaQyd5RbQV6]ZQT\Q?X8.+y2/(7B'9pRM#_|"I .i$/ r+]rxS1xC|7!{D0v`Pv@e-lj`TOEbmvc~VZFlxTEuvTO˸{sKSM9 OH}YP{Qw۵PSMw T _t/U@qTMmVSkRV.iɘV-$AK?&w[YUoinsiYc)u ]v}luWփSSfmU?bWY hY:P{ˢZx~ZiuZXr9-ZDo[l>o[jɍ[2g"ZWbCZ`.''*">6 P4`R7myzXr ~"E-!8Y)E{=rycTU^yfQkgn&}~{}hUslb^ս [ j^1 ^2>:4}qNS]Q_j+Ù~3s~$up lZ?ܝ&i[i󸔛3jDj j86iG5i<}iz~oh3w|gs{fIox gClwQg^ltfNg}O#".7l cJcuNAq {)%9a68ؼHN|zyYpe쒈lxzE.8&͓}(Ędy*q}Sx o{{"?Ŭ]NVPaIkvv[}tHkXe(nX`m"a[&ad8`՛~ao`|aw`sa_ qkaLvnY`nj{_څTgA`XqfN_c& C^y'|Θsd.1)|(39e6m:JLn\aOvqsj75zan籴}ZһV1TV3}VzMWvX sXqXX+mXjYO"hTXveX`b Xn!_XpN]6^SnmtbMɊnf"4+~&>EAKX)Umomvgyb mY#JqRBΫtAN7u3M!_rNpO2mO|-iOmgXPOeQVcR@,bwQYY.^2Q#}[DQZQYCHNdNkzqBg .z d@5 zgSIfVixY[<쪉n` RWSrdK /gGVhFvfG)ܶkMC%xG>ҝG>͐$E??&%EI?Z?BC@V3D`0ހ~NN~'p8ldKH-`W5]\7RO0t<I6?DԡZAAs-C>C<ۯD;9C`9<8g:8k:C8 :9;^9lj;9։;:A<[:5;3 vaߧ&ӁĹ< vs$ Fh4a]G'%S Wj.LQma2Hd8!C5L9@M.;i=q%;};o;9Z +;"8">"98b[85(0D3[,%3*,4-Z|0 3F.48m039/Y߁<b-AŽFi}m%5j1": Ia4wWCNT(J|gS-gEy{0@h11>3= ~$59:s58=z<46l354633-1'/M|#/!$>.b#.$Vڣdj6krcd! M[2SC'KPp#GJa^&Cr*@〕,R=Џ.5;<ҙT-8ʦg._7gQ/5/24;.4/3U-{3+08$-+)~va00`XGwiZ<[Q)sSI.7Ku:GLE["pAld%>y>'`<[(:P)8+)5)[5*3J*2ɳ*2G+%1+?1*I0&-*{l0ۈʖk|oaSF|M'' 7G8i{C8DjBtV ?c =r ";{l$E9?8$<7f%5w%3/%3F'c2U(1u(|0У&0|(0Q'0Lt&{/$"'MGsgZ<Nt?@F9$\B\3?BA7;MN<];k9t8}!|7B!5sm"2M 2s"x0l"*/#/0$.$;.ќ$v.EI$?.o$iY+\wkG`2UpF9E3E'&;S>10 :;7EG7U^8 aY6Kj%6t 4my2`<2iq2M 1` .-Bj.2!F.",) ,5!DPˆ<Yz<oeeYOC6e<i9r+T 66Z {3Ay-2hL1gVD0_3Mi 1r/x#/,4/C.`*&-!,Qfa+f2*P*NMG=DC=9 =)=K3A,h;DDAuMCC1V;F`^&Fdl,D|1C71Ad<}?^{?<?r:CA9BZ9EB7<69J569#58(69zc68ٗ7:^79P7f7589HŰ76#87LFZB; 8 !;R+< 7? ;/BCcKDTMF_n%G|k,E{62DF8Ad<}@.A>D;[C9nA9"C8j>7B;;7<7G8B|>8|>#'9js>:=f?9W86<%5}18G `8V;Ss@?:FGNIP "#I \'bIh.I^x6gF;C@@֯C~>WE1A9H@92B C:CAg:HAs):[@O;kb@:;>89<>:x:GX;;IXE=604.%6.c37{ s::@?8FEJJO".LZ)Kh/Jw7G`< DcAiA,Eq>phFpECB]F?ŴI=ۜLGGfƢ>$E>'5C=]A=O>>~@>G@923p/+g144 I>5 H<;:jLCML+#BNU)yODd38MSt9J҇jAmGGjCJ@PK>QrN<.N;QH<mK<|xJ0=J#=K>mJ,>Iț>GA?Gp>?5E"@ $E;@7DC@/lAFP>8R/M"X0/z/ H:2 DF4pJ9<P@RKD%RsS_,Rfb6OrPKr=R><NA=\NT>Oې?N Pi|?PM?gM(ț@.K,@cJ@I AHA FXBdFӮ@A~RCDDC;4 .K%-0. ?Y/ I2RXN6&S`<'OVbGa#UQ,T`7S@o?MgCJ;0KuF%+POB[ S:2?U!K>PzTM>T~?Tݕ@}V<{AUARB;R©B1PC?PO[BLⶲC5X,a+B)&w<&H'` ]Q2) [0_59fbN@ `K[,#]WI9XoiDT~yLnTNk)SI*YJDi\pA`B0a#Cw\`֧E@QaJE_dzF^DG^_G>[IYYZ`I VIXToJTXIKQaJqOʝIL; 2 (c!%t3!C@${RO0$9Z% b*jgM1jC;iF**cT:]cGoWzPQTXK+^G-dDgohbE]2iֶGdkiT IKi$If˿J3dKKhcP=LK` L"]fzL`[e@lk]4_uhN*Z$#pҮUv̷RGؔzAP} ZR[z.SwUH:ub%Ur[oVh8nǝVkDgWNhϗDVjd)VbV$_W2E]VbZ)VdXM-/ !W3 YEVguy"PN 't3T A4UqRKnݝsc`Ijzo$8eKWwk`>}k\uٲYީZ0r\Ho]'|{S]dx/P\s \%$Q>T@cgƌwyxy~}~NvWo툓kKmjƊrㅸjiP}|`h)xhQthw|nqxjfvLgerucesx`dr/_{ 6^PKg|p)?a׆ ;^) :6MoKKӒ^_~snqr ݐy iq|cDO_/. ^. D|^<x^qu^Lq^<[n^⍽l^^X`]f]P]h~5[%+OC_VyD`'klN֩eՔҷ#y5O-n/gFB5YvVrnSgg̾Gn _LrXtt8T_tSwsTxpFU`mU/i}U/gUcV"2aVi' `$Uj\UΐZW%YU V07pVpk<ϒJiR)$\Y>ݑ/2#LA7}TJii\G_8aVWf(P|NǹhYLlκiIK<ˌgKaUCdLfcL˷B`MH]N.]MҨIYṆXHNV[N'T0Oe_IRNZyPJ jW<<j Gz/Jn(i< ,NZM>L>J?/ٴJ?JI@$ˑH?FuA9GpAFAN:d>:>F;s>g;KɎ<~;5>;8Oa`X/twnj_.^C"R W-Jwp%1D)5?WW8b;_OU8W9J̓8Y7F87SyH65(0D4Ge/D4p15M357W2.6rA365J3 4Eu/6ȻN2Mwvt_DthPc.#Wq?pM|R&rGjA+BŁd/x={0&90e71 6k2I4I1|4y70i3-P0&(/9T$ /%.$.$/qی&a1՚)<P`0N~Yk{k1]U GZ) Q<JIpN Ec%\?w(;i)r8=P*D7ݪ,i3,)3G,R2,j1+$2B)K/$"+?* h*p*n* 2Y'IϒXMqaA^S' K 8DAHB] =>WrC$9#7_%5(&L4'2|(@1u(|1 Z(0'/Vf%+/$"*{'T! $|`*<ߚCҝjS1Yw8h%YHM "iE4j@`Dp> W;qi!8y2 U5jd R4pd"2_I!1dq#M02#Y/ #-n##-٠".=z#-!5*{'y! \ց D{Fm!`gRCE8B1 @G;,0 7 8W4xITM1gVD1f1t-.{.:- + ,^kY+4)Y"**)Y)G߷(u%lFGz#vckG^SOG08a=4&a13 2:C! 1?R/.\T-9g-rp,Ay,*%*Qr*B=*Bq3)'<'&9('gVZMsYyncXN|C4+i3"$g..-< y+IK ,Wqq'] (gy)pPw'v(~((|'&&t%.$$NJE=8 M6;)'?.D c6L9jT:_=6lw!={';>*8,l8:X05158i33|2 4P;32,60)S01)|/(1-E/(q0+ 41,(Κ0*e0)\0(/'NG=DC;60 5!;R)<1BY '5IW<TI;_=nk!<="z~&<Q+):/8064K2/58k34rB35<562,6J0)1+tl1),62$u.z#2$w.4X2-I2J-oj2~-ɂ2+81*81*MMpFZC:e4N4!E8+`< w4 A|9Gm;Q=\\>j!x=x&=y#- :U/K:947Ŀ56c65Z.6X5|52-g2o.@2e02{.22h/]2e/ 3V03c/j2~-2-&3-|2s*LG;?1822%5},Y:G 5?>:Go=6O?\@h#@(x)>ቶ.<|1:ͭq5876O85C6.552}-W3A`03D23HO1,߷3RQ0"4g924 N13Τ5E53$ȹ5,I1g3w.s4k/{d5s/JE=600/5T|&@4j18G `7C<?;BD>ND@YFAg$XAv* @c0m=3:ͭq588b7:6:6P74M2(4~'445-.4>/3M56v54\5813R*5/3x5C2aX6E2\6R154|.UI_DC;4l.81'245 8V;Ss@4C?(CLCX D f&Bug+@s1?|6k8ڃ>B7=687; 7#t9>D͞9=a:D<:8;y87I:8;9$5GH95FP=5-*#+0. :7Z0 @5XE=3HHj!JR#Hb|*3Gr0E6A:>ͯ >;@@9ݠA9EB8>q>9==AIL90B1I9S@N:IAk͇;NA:=;>;u<úX;;:9<;@A1=bD:E: Dh:tE;YF;F(Տ0"D>v3Cػ=r@> ?>Q= >a<>;w@8R-% #3+$8'B) K+0LwP_5Qg@)PL%]PY-UMl5JGɲG<5 J'K;tJ{rL:β?KO?KÚ@IhT@pG@ F@:D=A"NCX*A`BA@/A>=3O](y!*" B 1sq@v#bLQ& R* Vu1Xw;WH$TU80Qg8M`~@xHF CJI?̔LT=O>AR?Sn&ASdxBSUƟC>S\CQCCtM޶4DDMDL'EKm EmHD/E١{D\C|DvB9.N"1_);:HR Z$ _z-ao7v`EC#\P0Vc L [[kc2h&V l1kN=!f:K/K\X]\@LW=sJ)RSpNE[J`#sGںcRFjfHdJ{OcȹJa K_\^LM3\LsZM3WMaVO>M}+SrMFRNMN|MgM%LJ-)k/%D%I6FVbimc t x)w5srEJ-iVA`xiRrq[\&VjcpS9ti"ǭOlÊMor=O6nŰPkOTQ[hǥKRgsRcTR`٘S1.^-"xxOA։nha-Umfwkcbj]ʫp²ZC[tNWCwWGw/XsFYspUYlYiiZcHgYcgZ^`nXr\YXIYKXWX= U&~X&YRk/ 6'i:N^n|sMYJf.'Zii8s*LjI%?ǮZSfupmtfQo5*ppjDvܜ0g67{;d@~ biCc;zňcZv_crςchNo&bke}bQgT| adz|`@ _wa6/_Tw_%[u^X!s^<"U@ r2E]n}(u6?.!g@m2?& ;B;R4TKOĩfb~{sAzʬ|i}ugzgiZvfrVf~Eeofg{Ykfx>h'dwudketadr5_is]0s[xrM[_HZn\ tk\u i[be\b[&_\]][ZN[k~WaZ#|KSZzPSO,F^v/k~!擽D0NƼ1 wߥƫ0%+B8TUKdo=j\dOb\fGVVhwRi3Q PhQSeRYc0R`vRt$R]Sv[Sv#XT'WTOUS݌.QRSP9R#M;]ZqKM׹ڕͱˁ`,3h׬*'~=1.xfΏP@eʓf/Q[[ɶ=VSȽlZN+dz}]J./]HiƱ\H gZ8I0#X9J]WJӭUeK=RK^HRFL<xPALMLnmLKQHKh1GPPKjj0ÒSjR!"&]]+# 7 $8oKH5]ۍaDTJbL6گNFxCP^B܀QAMٻqPA;RO'B MMC M0BヨJC)XIC GCî8EEGGE0FqEgCFb-sCcA}qN0b'atwV0 fEO*VI\}8M4cz=Eɚ)A @'쳹 C<<رC;a#{B;:,]AW;m2i@;[o@=4¯>< ;>:w>ݫ:y\j2NҬljmZZCCta|an'_&>EO V*Gct0@?13:u58]66|56b@M4!6N645A35A36V׍<47x/47At;447C38L?47Y2=9dI3:9sՆ?N<asv<~(mcdb$v &W2:\JP"DGlC(f=*8ע-=5ļy.3ԃ.2-r2*0&}0I'J0'N0'0ٵ(0(0z(j2lj*1#(mג8MX`r_S[6"VP5GL@d!~;}%N6&K3'^1( 1Yϭ)D0 2'0C%-* +H5+ +IT,( ,# x-:"g$ٽl+ ~V\DvyhUxSsI2vAF<]8t6"1!0;"/Ir#.>#'-j#-"d-X("%_Q$$$ïl a^o ([J<pa`2NFA- 8212 m7&<P-yF 3]O!5Z^6g6Sx65J!t3=d#2')0'r/'.'.%,P, ,"m+!-r$٬.%b..%V-W#1,!ŵ-#I-#,-"=JE-"gI_B92-) .,@1a&2_/8` 3#? 7zIj8_VZ9d:dtR!48l$6%&'5+2+1l,J1+0(Q /(&/) /)_1X,yx0*0*&W1=* 0(/'0&Ż0'&)0W%G;>8R.+(!.d)/9 26 7C<?9H1Q<TI= c*;}r!:ׄ'|8*5M,+3.2׹/V2~/ 1")0+/z1-1}-ۃ2dr.4O1+3xl/ȫ3-Ċ3o-2s*#3+VN2*2'E;2-C$&|#(.{-G,c33 g::=0D!}@Pt_?`n?p%2<݃u):-7`/5÷1323j0>3g`083;R1O3824,3a5*36<5" 6.36_83B6R16P1Ÿz5hy.5,Į6D-B8R-)%"<!( %1S(G80~ ,?B7A@,DCLTC\!An'@-d< /9365N5l755{55U6ZL6S87X9ԁ7i9 89M 88*87;86չ9R696+954M7r/O8+u/=4-!NIW"H,5!E9&{@+ E2H<HHGFX#Edj;)A.?"4;o~7+89*6:c7:8Jv>OT9N@:NAA; i@;=?;2= E8:>!Du>QB??kAYs?@.@7?+u@>G?J:_?N9?85q)S@ / : IPXVQ& X1KX>SM(0OEa1hIyP7FM!?2BE/J@>I>qYL)=NKt?4OA/P'BtMBL2CJr1CFHCsFuDG|D^QD%C}AC?fC>.Cb<70 !_)95BQ[@`A ycp+2b)8\He'UZ5bQp?MHKJJNټG=StDҞVCCXǣDgXNFWG]UcGSĬxHr,R[H_POHM:ILDHHח I1FHVDHhCoH9A*$ 9 0Z@2N[Ze/mip# )o[0~j*A&b6SP9QN[fG Wj~PPwSyWeljP\MBɄ`2TKZ bbKybîM'_֨M,]ONtZ瞰NWNUN6S2/NgQNHMُNLMIN4GMmkC2;+O:`MZ*hrz0*~g)SWx;k&qL9+Jh^XKfpbsWI-^^OZYdOVWgjh4Uj~T kmUhhV)re1TVbV@]UUZVZY?UUW#UT`kDP`jO7PKdYxXpm:ϼ\)gڴ d2u%S߬qD86WJ=wl[Zk<adt_eX^ٚ˿gZ֙|܁hHXPgX)dwXC`GYo)_aYe[Y$XXɂUX퀑=T%X}TyQfX{#N?WyLWUvI|0I&`vtYlۻXl;] #FO,f>/}?R/AjgQtaT CWJYޯ$ZT,X]P]=ٝ]NK]9NzZuOXOV)OpBS!Oؖ gQP2OSPM'P.IJP7IRP)H6P;XEA/[rKdt+_ˁ,ơ%҃"8&Xr=L{7_aÓb"FW½KPOK;UQGyLRE3QF)UPjF{NGrMGKGk HG.FH-GHYDHpCH}@HA?Uneܧϲ<<Pa<|&!) yf1i6F-X];&Oz@&Hҥ9CCAE?тӁF> 7E>̼@C>_B*>jA?4@Z@,9@Y@k?AL>:A26a2$V/n,4*@([:&W'%Ϭ'%&O#]o Z$ ycZ F ۬GZ1ʖ,]#i #rY*}n^EM9 8!%2T8 / Pm+dCj)|#k(J&$e % !#ʘ#݄##6a"v )}~MԈ#''Vt9}tf<WE3R5/ y-t36+IK (p] 'rwO&yp$z#&"!  8 !.{ 6R  Y]8߈8 $ YykG^EP?-/Wn(,&B#&W !g $vy #"Fa!A\d!i. QD ;| 3 d1 X   |pmdYXJo:o," &((1"W:t#N!a .na  } G  ?= 6 f D !z iU ; 6R_pJ8*XMtis^NR3C6(_?"$&v4H *Y"f_Tr}~i ` u v [Z 5 & B{LoN{]*w`lc XLX?D1Q% # 61@d_@^^ `VjA{ z lBq{  f~JíU'NG=DC=98R6086m?+JJ7]QpI#;^ &qj ^&x&L%+^% #.#1"ݫG#"a # |H` f $ ~F %!,X!K@ q"=LgNGC;8R5q4Y6<LH(< cQT%[] %iV &%w&݈&&W&?[$g!$ˎ#3&$$"a  "!]o"]m"ٰ!#5#,#J"^#oMpG>95q224?mw 5G!O1&[e )Fg'`vK(܇)h'0&}&%*%0"a"  ##S"b# $=_ѥ$S̈#$'%$%$JDC=8R2..4##<r"DR&NE 'Yd ^*gA!)v2+9* )*"+(e[X':O&3K'MK#$$*%nߛ%(%&ӱ%Y$&Q%9§&os$fI_C8^40 -,`0h8l"u@,'J n+W,e-uM)+*+*"+(q0(!Z='q(&j/&' (1?f(K'R X(I)(U( {(f(tiG;>8R1l++)E15K'=+1H A,Uu\/b/9s.)q-͗,[+[)uf)){;(/) )a)ؖ)ѿ*K*iǍ**z,f A*շ*ME;4-)( $&,&2_*: .^D =1Qh2a@2`q]W0fb/v7.  ,0 \*+q**O+6 O޺+F ,v"Ү,u"q,B!U, '-#6+}-"J4,͵J+)B8/X*$!S#")4,0|!/6 ]2AV 5N25^5dnn31| Y/O!-B#h,.# ,!N,! .%.#%.&ҹ.&̸/U'Ǫ/g&h/&Rc/ $.#:/#;."8.!=4+!H  &#/--*?21j 7;59K:>Z+8k6SW r4 k#1e%/ '<.uۿ&/8I&[/(0E*_ِ0+X1+1+ 2,jL2+Q>2*ܹ2*Qs2)J2!'c27&a3j'90 %!W-~m4%^:m- -<8>@E>V*B) E4 oD@FCQ@pd!=z{c'R9 +67/635\65<7 69]!8i:K9:=€::Ž<98+:d8eH95:6:JD3%:]U2n9u/o9/N9<-S0 !6%.; GL! O- MX:IzK *FJ`&C`u-@Ƌ4>09;m=:}@9A;U1B/<@B=VA=?Yx>?>=n_>;Ԧ?*:ݣ->9>7%?&6?95C>[2( "/"8DuQ BWCZ1% RXY4B@SDNW-pLmd6AH=c_EC!ӫCeZG2AXJ˓@9aLC@vLBVKRBIZxDAHD( FmjD3CD~DYoBjWDADO>˕D,:ERZd +g*f;,a>7!K[zO1=yV@c=ϲRzcEvOKL~PIqTH:QUhGUVHTK0JEQ^J,N!JMx)JKJIp@J>F'WJG`DAIAgI?\H=DžJH5:&7ET4`jNqN|x}u0ue%'p7{ .jH0ȢceZAon^&piLYZ%SGW&XpTw\ 1"42FTcAox7 ሄ9 f}0vǍ{B).|tS?l!fP5g}Yc^5`5bE^lIeW\ fF\ep\a7,\Xf]~[Y|[FWvyZ:T"vZwQOw-Y{OPtqXKrOVDFqVSEo VexC0BWJder~^R.6WǨ )U=;*8M<l~`LvCs[reb~to\g x[ljt)k۝kvqYjYko^i=Yglhcl)g6_k'f\jdxWhc&ThHaxQh`Nd`VKtd_ Ic^Y3G@ U+O?@Tdeu3ϔMc0T"b4$ןG6@ϝ P9yS<gbѷ'":'w˰N,7fݬbG7]~]LVTOQ2rϬm 0( J|3l G.\]=jSyAM(5E*Gʲ8G,DFGB`CfGcAD{B-D^.@Cԝ"=DF.6>HgK4t=i1n1=9@0uj ,ݫ볢ѼVǞ̰Mо40+u- _gL# XR:RJ"RV'XC"o+k<&.Q8k046035 /3i-3<-3q,4 .E4CC,E5$,5,6P,x6~K+6*7>d+~!.NL2Npp0NzCg]-O_16BajK;h`5*I"0=$-’$v,rM/#,!?,_!^, !-Բ"-y"V-v{!.V#8/i#/ɹ#0P #0#FRӷ̌NC_ra XQ .Ew' W9BAJ3B`T(.M):3'Q19&JQW&&]a %%@%&'oХ'q(\t))bբ=,ڌ,wf|x{Om{k[NC=d29 G-'W)tC&#x"@ "۴)"a!  p  +Mߠ C  : κ!Y߮}<F._؄c~qv0ftSD0V qh|m]wJ6)H+'.I#F[!a!Fy  K  u  &=+?N.0sdYUC/" "W(g@ WX|mqan}:Hr*8p?u 0},ROZ"Yw`kB]wO> +3G#/y;Q[#lf\}r0>V~z%͡ y SDגޝRHv<{ocWH8( A ( =,]g2l a H _Z ru T^%pK }sg]aQuC4%@NA3jFHYhi>uka87yOaGCl!Maw`kGaWJ?@1Q#++Z=Pq^k^r8|D85uDMG;C8R2-)+-- m4>IPU!1c!sF !σ !"w6 ) ͷSr 4 >g3G }/ F3 .s 5Ε S  N!!޾Mv~DC;5q-)-( &3*2&)7"DR$P%_n &pH% $Ɣ#§#t"ZҘ"Be!":$Jb#%Ey%a&5$%Ž%q`%jt$$=5q,f%HI!M*"63%>x)L 8+\*4m(ҀD(gQ&n$Ծd$7[$8&$w& 4ށ((UX(I))Hr*)8,()6){)90*$ <c")"'T-+^9 #.nHh -X=q,i,+('ql&]7' f(bD),!π,$!ɀ+ ,!+,!(+H,x,-m<- 65q+!NKi m&0,)04 3gC2T2Nf0|f-+cQ*QGL*F؆ BP* ,"ڢ-%o /'/'/&0L&?0'"/$u02$.0A#F/"k{0w"U)/ g0 %Vvms ' 07N"V9. 9p\,=lA>>?=E@Q5?N3=/=.\=-mW&=3>@J RX_]&(W Y:AETQL1*фP_4/Mv;EK2A#HkEM=FCpHqDJ' DJǪE2I FD G՜FKDęFiC)oF@sLFH>aE<.E:E 9gD56eCY3Cm2!J "3BM.YFbhmmel! OOh3a]scE)4]W8YplBaVJH9SvM$#P޲P0N[RȜM!S N7S XNO^NNnKN[ I,N~-G}MmkCELA-M @}L=f{cL@;{#J8w{K+7 0BP?]hqFv8{i؞|RD;x,mt&>2&n5P]6ͧ/g:bEcJyME` S;]HWC[¨YȇYZTY ZˀYIV|XRR{X$PtyHWLwHUHuyV-GsTjDhqRSA8pS2?oRi=mBQ1:AW00?@R`mx~g2C̮W%I8 7a#I3x\)BnqoQ npWPz}kР[ti}^qgPӰ_nufo`lteq\jdxWhc&TgbpQ}fvaN)d`XKse8^+"HLdK]MEb[BcbRZ@bY>1,> R|ap|4#ŔP [  0ҙ-qC!-ԊV*=o|)@iKp+TjE~<Wd|kYa]{οZ_z'Z]ycX]#v-JS\sQ \|qN]2nTM[lI[Jj HdH2mHGw?FGXdDHY]B6H@H>WIv=H:Ip[:I92I ~B57Hv{Q4F]r‚~ʗeǻϫN쯼e$aAuŻY.}fӸC3&WY4 O t81I &;!D2=@3=>=u>[S;>9?x9i?8@P(77@96@;F5BA|U4)A9k2wAJq2B 0YWp]~V vv>V'By3jp&k\<NSJ)Fwzo-@>ꋛ0;fO2@8'›Y36Bݢ26>06i{/7T}}/7k.7c-,8)-88W,T9,79}+9*9ʓ)l୛ձ&v'Ò0:zi_CQ4a}D6L=]JiP#7t'd%33҂U'B0/'.qѣ'..QѓM%Y.#/+$<\C' vmvMvkBaiUJo941G-)-)260=:FNQ]5 nI׀XHẅ\MmBzEu }6 m ɖ ũ :  ȸ 7T װ E91G(#$VP #/h<<84J]Y<j~4 I  =GOT->  ]ͭ Ȏ">]!hMD"D "Kس'"H!_=~ :4-)!NK~+ <6E V9Y!ng { } A jX H3  T Y m#"Օ$T&hy5$%Ž%q`U%U% $%7J&\$x-)%W mD n  !#/n*%?q'=Q r&Xd t$z#i#0Av#ND)#UЁ=#KO%ٚ'd/(vŧ)ƾ*EI)*R*L)+?)}ͦN)7)h̞***@ 02D#(_+(m-e9 -L >+`+u@o+#=*ƞ@d*2g)\ k)!݆*W R,"n-#b7.#:.";."8$/Q"i^.} k.z{.a/#-B- #$ 0g #.36!625 x)5}E/6Y895m~A4D!3,%%1٭X(B0å*0uڪ+0+2y+ٹ3+ڳ13v*x5 *3(*4(ա3&B^4t& c3$4K$N?3"ޑ2 V 1W +O28?+Db&)C+/ a^A='@g@P*?e$=z*V<2. :2`9 >4ῡ7o657N6G8"5`974J:PW2J};0_1ܝ;f1:.Ԙ:_-,9+c9**9(8&8%vW1,~8A/GMT[T$ aQ^6X^MKH#=tJ\%+·Hq1˼Fi6_D:ױB=@?Q???©_@y>FA[*~>)W9/8;IEPRX^/8c d\a/&]@"GWsS&0*Th8AQ~=ݤ2OnBϟL;EKBçGטIH*IH)/JQE0JB8Jc?2I=zI:~I,8|fH26{zGQ4yCGXd3u?Fk/tF/W/8=JV`}imOqv  p'%ׅBbQ4GweeAl{H|^J;f|yEMYaixOK]vqP[tu7OZsuNjYrDJY>o8GuYlSEXEjBRX hL@X)e>WAdK<,Vbf: U`#7U-_~861"9cNam.x݂ fmǎNSg+$HB7xBK-i_;`_xAY DTFQekGO GhN?EF NC:N@wMr}=N_z=2NXv;Muz9NLrl%8KLp N5Mlj[3M'jn2^4J^p#{Nt]M (z0l.D&]Q1Y49UBs8N䝁;J!D)=F>Df6>LCw &(R!WT,+_Jio/SDѭ1@3<4U: ק49㪊.2:nt0:/p;/;@-<"t-p<,~1y{- *ʖ1)TGz(~d)v`*G**Ҷ%.+,?G,-+ -q.!ニNvǟ0BNLKz<l`ZAaI w>R$ 3$>x-M_\{(wyv %+s"ف!65J !CM!@ p!t%!["ki#+y"v%8mO%o$bh"ήÒ!mŷ!ZmtdSO<2/I(V44 #=_S q 譏\ ;萫 5lRp,ا8̊ u ,T  4 9]} Ocˢ<NN@ѿϒeSyCme!^MM7\"0HO'nI!uiq41  ] P_ O1eˇjĢM<P-pZbÒJȤ΅vtu{hoZ~Iz41  :D .YxnMƂl\VtVʝ0r]r}ۥN{lod]-U~DY1 ,ZK; iV\v<Mހezlmݛ~jv~fk| ``RA+YX(4Ea yǣԻ <v\ilc{qgp[OL:o) "0>Vo߰0ɿXМ!xC3m aTE5$ O8eOUdayY9<ًߘ|N|rSf[OOA2y"=3RH[p?ݑPm<]mvkB`UIFqj0ذVIiÒE @;8R605q58R=EMXYdrOit,i@X ֊Ī=DC=93p2225q:WCJUbq|cmiP֠00ذ B;62-)-)-)26=G=S_nm4CذDIZq>8R1@-)+*@*-29EPC^l~~[NǞ]]֊'̺"ZÒ\C;5q-)*%$%(-5XAM1[jE}-ݏV ވ5 D#ͬȾ]7-F+UD8R0 +#!!( 1^:HXi| qɢn^;- m "҂ A̩ M V J  ] r m4-#V!+i5E. yU g{  ^ Q _A[ 14ݍ8ѥŹ SGô 4H ۩-)& _!O# c2>KB0S(ezXD8|K~&= 7 éF & )   g ݮ  % ꣒U * mmt/+z5<yNZctx^Y  _ , I Y!Å!`#Cj" "p"y!n" y"!xR!A!WW |&#{6 Icn"] pB#;q O#Q#k#~#@ط#Z֫$8%Ω&4=f'(vP(3@'()(<O*""(83(U(ayH&bO&eVWW"__&& ) =)f-,BA ^-UE-i-u}p,lu+̱+32!Eɡ* "i*!ø,4 θ}-r!U."(. u. 10 ].*Ś-ŗ.4Z/-; b+WW=u!x&=/81~89&88ڻ9VL8`7u!6%5&(ļ4 +3 q,p2x,33jD*4*45)5( ^5\&4% 5;$4#" 58"3 j+3TC3r WW="/87=BQI K:#H1 ECäDvW#moBl(׶@-v?,R1 =3gMF.O:TGY5Q\e YL*VU<0Q^N(wObc/KLy4\J8Hɥ;G^=eE/>9Ef=܍F4<F(8݃F#96;E.Z3}E2n2{D0yC.wB+u;Ar)[tA)q?%W^(97COAX^ dhiLJlK(i(#v l5e5ۦfaG&\Zs3X}Yp9W>EUAASkKD-~RHEa|aQAEU{Q~CvP?rPfu^Y<_wW:\V%7S\TG4Z(Tf1YRu}/ZOQ7. $:9I,XKd l0t̰z0Vօ' 6:2p~^M+ry`8Hh{v{w0@6bUt\C]js7`DYqOEWqpqEVEpyDUo,AU(l>]TKih;_TIg@ 9Sdi+7kS bG5'RS`L_3S8^I_2Q\/QZ.!x7K[g2rpztʇeކtL-/ Ɣ) 3(;rCRG&)d[82\Hr8U;$Qw=Mlpj=K.3v=Jv%t$৶6&eu&ޟ8&'{Y:ÒߥMb,@zNtFÌdǢSF˧=nЍ%51*#8 \&VV "ъr Ɏs  Ѡ SPo k5\ MЁ' $ ʣ m >( ʹt ̵! P  vY! \ ۞mtި|ϯյ:páyl˸^L 5ٯ'sL-yފMe1lzɿl1!BA$ α 1 8 x mn  L:+tÒ~?sglW؛Foܟ1p<3 @ _}9ښ;7o^:m[]$t;Ц[4r?#@VxhҤmjaiORߨA,N,nk B2nTs-y= =&p8Q118}sNiߐ\Nl=J*,WMj?l'jlljmmXt3xxo`dKYKS;)TO'Ea{iҖ#lN@͏iҖK|maslh``VJ8Z%">YqP2?vb#lYDn{y2ph^S E4#`O:oQg}{-02b-וޞcNv0ne YN?/ n 5Jar`'0?cm{sj?`TH:^-_ 8t1C\Vi{>?ʖ`7?E?GSain_zİג=v7=^I?Ò{; 3p0*)h)o)h-29EQ^n0}CtÒT60 ,m&%F!$'-5AiL[k|,){0oVĪT`4-%  &/@; GWiz'ekv<3i0"ZÒZtw\-)( mV")h5qDCSeWxq'XnNyYpGܡny¯ 6 {5|=  |*!o  e-)=Nav[$G:  %űz  ,5 ?]4\s@j秓ܤ J .%FVmS @% 6dJ ^-s#I bVћ>ĮalޠC gszM' M i B 4H ۧܥ͡VDy$mWWWWW - 1`FG"YBm _t- */ zԧ :Ȼ;Й`  N˸b!TD ܫ!C!Dp"v! !s!MO% L mWWWW QR)&"=c$Qb 3{%dE&%x Δ& 9%d%=bf$#&$%Ӷ'}'U(}(U,)*g(ח'I'Ր%&  7%QmWWWO !$-./"04 N1[H1\H_1&pۿo0i/d .2؎B6X "@o-+ ĕ>q?FK=S=%s=2#p=n"nW !x5A3%NW`ͱdiun0qpR<rUo)˒;l d[`;@YbrY@`Y?`MYt=<]W8[Vv36-ZsU}N3YJTgf1XR.XiPB,XN+ WM)V\KR&d !x4{E0Rp]تftbm/qv{C}_~! ${5>:yTx_H$mEt\-/wcqDr6]o鉑9Y8nZ;^Um<1S9k"^F 0%A[svV~:`10K0C{'ܐrߧk5&b0XM3@0D -:ZTNio~N<])<۞~ wCp .g_ONU>Kz>#Y//~ 5Mav0bC`ݑ$pҎM|mltm d4\jTH::h+? w=1EZ mVm?N`B=8^6668R;BI_P\qgtVЃ>;^̸2!?; 65q445q9>E OZterGmA.QVnҀ0=95q2/X/X0 3p:@BLWdr)mCAp:l= n8^3p0 ,f*,m-0 6?1I_UadoQW00^IƜÒ{T61`-(&%)o*1g;EP_1n0~32@_EܟV?ÒN{T4,f("e! m!$+5AiL\qk{[|MEM1ذÒNnN-)(!VV$/@;ITWhmJzw( onVÒq[*E+!"")o5qCSdOuF%X'U-)d0Ż"ZtZjZ"eommmO-=N0_(q|{!SOJf{+hBL0 WWW 0$5 FYma`lWēa ;q չض ͎U sԦ b%s[Nm C8  mWWW l,rB @T"JhYm|b +P@ݿL^S&rƧܢ@!uK  WWWWWg< &:_^ Mj= `5uks 3z eí$Pʮo’|f{еC @e!N]!q!?g"!6 ɑG Y > ` T t jWWWWz xP!3 i%`'+0)$D> ;*!Wž*Ckpv))ݰ(af'ϼW'\q'(uH)W y)u))͌?)}!''S[&fl'~$WWWX#)0:4Ј9C7(s -v6:7N̯6kc.5vw4(k3[!2#uZ1#٘1Aߦ#qi1!<2 v3 8#2/ 2DO0|0x{=/Exp._#v.{tB-W^W: J!/l7>C˿EJ9RJ!IG;3sDvEClZa;sBo:#B@u&*?=)74=ȭ*j<2+†< ؆+<*QQ_#FOfw(/M{,.|JLAa/,xJ1RtI32`r&HCѢ2pH$1oHt/l F+h;E(fEF'd Dv%bCX#a@i _@_?9u]=W ,<<^GRQ"W8s]"`dgzOj h $t SeD7 b%J!,vL^]+^oS\8s/i'Z'f3dDXxG5`W7/]Uʪ7[U>6[U0-5YdSUt0VR3-V P'+7TO|)U&M(SKI%.R]J#Q)Hz!_P2Gj 1?LU ̝]_e*R_[ Xc:ÒlRq"\vzg!+?e ">[c<?_S=^x 0ErRS`̰hq_qDuCS|mYymm c79 kW 5*KJE^c@%{X!;+!#8~#68#4]L#4H!45ڀw(5|\5ey5tvV4s25pD4pl4j^rFUa4Ϡlt|npόg/}o<^ΞW9JW-~ ?sD$9]14iv0{?-g6"+Pn)5CD)*> *\H*(87+=X+1K+:,C|G+y~,WvjKTod?RovV\Ri:tVeäUmG N= t$s2੃=9-JW)2q3%>#*⢍P!pQ T)Ӧ i" !5!26!"! # B%# ", ݜfo^qRztϏNcbyl^(bM/?6䶊/gj&Ap4!`Qx <WZk i0 >/̞p%6Ѵl2WRC|5"|ޖ=֎r2sǽ}oo=V] ^e|qídjVtEj/ œ+ áJ.àeDÐU&BA±t j t a,CZMΣHam}maN%~ϩ&+uԲBk`_P^?O*Ǟ _<΅YιuLKͳ^„6'Ǐ2ҐqCgN@аzo@d̽X]K@:=N%9 ׂ6גTגpm׿q<0԰_%̻µ'^ᒀI@epGô~wtml)`iTFή4A!x 2 Ol >i~ߨΊҖmQvJ¹3!xeoegJ]"PPAi0ޖ2'x.LfN6MɜMp عҖ0!Ĺ < Pb}ĽukibRXRtKOג>Q܌.a~' a*F`a3|m0eM~G0AԺp~Mx̔phP]׌TXڲIG?OYf<s0{ nǻ:~ۻӜ0Ī|9 601`//G-/V5q;BMpWdq0g<{ǻk~_d^n0T61`/G*"*(**1G6?1IXTgad_o}vmpRƓ`sGOinT<4/G($$$%+1`9DQo^l_{>e"]Vŋًw^;n0 G/($ m  %*5AiMi[Ohqx<i]ŋQRw˄ħ}%RE+  V""%/:EIoUetؽۻo j$"G(N4]BQ`pm$205׳ʖ<NGmS ,;J'[F<k|ڕ׍|.~גӐqTMj0[k"OWWWWWm*"2C:dUxfy..2õ>kȲ'Te?]Y0OWW &O)Q;SN /a Xt K ~nz6K;Nx4 8 w \ t)W]oj 5NI?v\-ol_y SRҺ*/l!m8Ƴ..P}4VD$}~T,ԂXpWWWg= `j;p,) ?R"OS3 '"fo {C"z"  "5\ǧ!´!`ȣ!YI_!|"!j""9"N A! ! ! | yZG |WWg/ ?/!l&П)7/o/4#%.6 ;/J: /^.+/r;.V-Q;B,ҭr,ޒ+sm+MC,d+!,~+{)yW*ow)6\s'q' m& WWx7g%,.t4?X9jk=??@A+Y?.c 7=AÙm=%Up<8i):~ۉ96!I8"Ђ7a#6#~6^"~7V!xR71s6lp5on24klO3$jn2g1y4e/t,c/_WWIe!i.ڕ9 ?EJ/M{P.kSHQB& N9ވKhL,JEa` |OHvl$vGr&rE(oFDe*lCG`*:k Bީ)jLBh'gB%c@!bw?*_>y^=y\<\49Xt9.RX8W!>&@6<@GP?U "X\m_KLa ai.Rb $YPZ5BNAzLH/@K1ϭV"]c`jbms1avy}|s$~h}" 4]{u7St#<\s#:rV#:1r"H:|rJ9m:h8f9lc8aaP9^7[7 Y/lANϭY} `/2tW-pA*;'f%iµK$7-#we#+NB$U$/$b$g%{[%xg%=t $8q j۴Rϭ_DipwxYNCcmEyÕbmØ`_OO`2L Bʒx_ympv'~ֆ^0@uСjy^tKP߭3?O*$# @E\(vv)h}i0M\xe,x[mb cxڥnd,YJ8h#ž @]'Ŝ7[ŎTpdG?OZ<fq`*}ml?)mA-l_d@Ƃ61`/+*+.5q:BN"Wbn0z Y)9vQ:{4-)m((()m.6?1JI&T_Kjytv@l{z/ۻ{G-+%!!!%+1:ANFt<Pm[Yi"u <vjm^|>9L'C+$V##,eN4@JWesDZ ڮًm`֪ޡҀ|9'$ "1'"##-J9ER_Omޝ}֩<ԋ<)Rћÿ "O X&tV2{?Mf\mj<z<ԻЉG:_/͐_aLM "Gm *7(FoW:Xf<avaP̰ގ Os<~GWW^O!bb0Dަ?*؎ParE338]j@m~eWw!Z0' ؓ8AI[tʴmm/>7Cʤ44Xg8v7,QCg!YW_<ޝ I(Ѵ.^Ba Vi|YTHr=UڙifX - ~ y#p 5i(4s;3NaNuQ\ J!   <z ڜV l0lE>߀khZ}Gywrs W*0 גmǻ XL# &+$&1Qk( Ea 좁(Y o](ml'ꀟ'V&"9%{m%uG%OA%&c}&(y%Vv)$* t*#R 4qL"c &n! @kc! k7 AWW 6x%+\1<45:7) Y6<r6|P\5dau5-x l4%3 ZZ|2/1z1dRx1w1_t1o0,kI0 h-jf-d6,c+Xa*z^x) WWqNwl'19=B"0DRGlJI!7G4aEGj{D9\v%CpqAXTl@dc!i?T" g)>~"e8=o"/d\=I1! c=_?<]1:Y9vX7,V6T5$~T4?S1Wy ً.m:ǻB0HmNRt,SVcaYj\Z\C|XX-0 srU@ojSScQh &^)PC}#YN%1UM[&RL$'>PK@ϴ'*PJ%OK<#NI KGJ8DgJI}CxTHAG?F>[E+=x&.6@'Pw=JM=kJC\1|Y1W0Te.0<@mIYT[rb6;imipu!yw|,j[SonH=R) >5?8d3W3n/녿,E*_~!(}3'|'|HB(`y'tl(RoC(l&jy i(f 'cZ &` v@XKOV~j_Vfn,sRCv{l`Rzt\na P0BU 9!w088z 1+.^Q e&i #W j!2 b4 ) lM- ]! =[9Y{$wD|t-p'n4O?Z<wc1tjrxY-|bł!,|prMftCXؓH:>2Z+睹"0K:dZ}P/%3TH˗T ό ~ Ņh ܁ ~ `zZ<e:Vmvz4|pr҉0tj^՘QwAi+hG 6K'O^C 4] +vͨnդ]U&a<ŤZwhm r+2yCS!@͎7xEndXR0J@9Ҫ#0 N#<78gUiooV 2<Ct}%tn{rh*_ͦQ}Ej?2NP϶0vD5QkVAFݗ6k|<}.r<}vzzlb0XRL`>>i-útŏf3OM2gcēŬ҈0ֈ0 <3/m`ωX&;}F ᣒxڦoi0]ϱT wGFl8ڽ)-1VJcz̰̀vE-}Ö0V!]|m|rltbiYOKD5?&=mOsҎղ.G|`xC԰խCԥӧjtNq1ϙs<~iw`n޳f-K^bV0cLÒ@O2̰#"ԭt,D\mMsNMݜ'ǻl԰:G<®%VGz teldR<] SHqʎ<>0m԰!Cۻ 'AX]p<<祛兰_ŋ<f/:Gb}v!3oBIh^aOZPOF<9- 0 O$=Ubvl~ScP鿁3Ml/ôBymr>mp^f<ʀ^WRMB؎7+V k&O:8NP8f|N.κ]\˶/|^vsoMi̭dR?]u֊UbJ<@ߡ6\,6!Ql63RJ_v0?'տ]9641146:KBJQZFejn03y mpĽӧcNp@CmW'61@.-,m.46>F=N0Ya kNxRd<^pĵ۴Җ۴~3̿!WL'40*++,N,m49AK4V0`jiu{ܿN3ٽ<عV9Ҁ@WL'0)m(%%%(/60=GPm]"<fs,}00Ԩ aÒԋԬ԰_NW')m%!##(/Ty8C-LXab[ol{Y,0B'mҝmlR@H'LA%K'V3DM=o:F4S`ڌlլx<XΊ |_̰̰at)L| u{', +26BO\iuҀvϒN˨3Yy1?ң~'AOtru ^SZ 70"0/0;lJWej<sNʝ0VǃRŐ2_,K'LmW^]6N='lo4BPi` pC??ˆX@Y԰1kC^WWw2<Rߧ(]-%;`ΈKȶ[l}1xGN=i.~~q?wW!tٸ~a#͋4DUg2-y9?GһKe%ۢm̰DǢ)<v,Z`t_+r+)0qa*>p+hk*g!(yd(K aV&r `(&L ^i$5 7\2"X!"W* `7Oʝ) 26e9<=q>@BjP~@p/) {E>B5u9>7Vp,w5qK_Aum^>]/n641./]1@6:EAGNW!`kCt!ӻ]V!p1*o_Ƞm'4.+++/X1-6=C8MU`2^flq}_ҀN`иظxZ~|GCu'0+(((),1y8?R+HQa[ eِp? |mF9XsϒXl# Ƞ~K'AO)m&%#!#!##(y,.3G9NDaJMiXRbn0ӆyoNm?%̴8;x-ʒ|ÿmL'A%!i&N.05?IlT`=it@mm<kpǢG'MV"""L;l(Pu1I9aDGًO ZKfHs3!)VBÊGʎJܕ0N'OAV{ { h)3O<=,IfVbƝo~_!3]*N~<!-MlSi Xݜ-08aDSD_R>k*{dv/utptUN3)C w x$'1Q̚>jL \mhmUym2X9ۭKC`~< xf)7bGUeupB!h<mKpBNJN)evgO0D@Q=`qp]8#Kuw|yV,J?0%}7I[myJm>nؙ:|z@xv0r#ؓv® jb#.Bo IUa )h I{G)!}@n C Ø ո `1  ~ x Wtq:n'mCjX`؎Ǟ C v,(2z;AAO anu)sT΃^?"ԁ,Q&~b3}Q?w`pkNj^#ge.f4xa@_z 9vvגͱ"rP #& S'9'1qd(aE(Yh {(fl iv'6 si&9pz&8>n%ili$"k$j-$ Gg7$ b#B ^!Z Y?wVU%S{o@!),2(4g60E7;}|9<)TOt>7< Fn7Q,iW6dd6 x`5##]4} Z3$pX2gW1JV1U2,R/mN- L+ J* KIJ( (GQ' 3G%ct ӁEʎ!q,J29 3:R<85<-6:5 92/ 71/ F0 (60;ClHLO+R~TrWeWY_[UYn-M\WBFVnVgBUl=T:GT 7Rx5R4sQB@I3P2=3Pu42N %1J71Fr /D /=A .t?) :.|< ߨ԰#o/B:=B0Hm6NR-WI[~g_*tb0gdXh3O{iqFh>%=kf: 7f[Q^ 2eg)/ie!|e,Od=V)cj2'bg&a@&&a:W %` t%^B $Y $U y$KR#P58#M#yJ԰%Q3=aHmmOU XT^Mb~e:t~iQil]FoNs>v?6vQ.(u&3)uKx%ua!tMx )hs ^swrv4qbq &apxmnahacr`]Z^ lWʡ;4DiJT[`~dCiGlv0qmtaweTz<C}0[&+SsCE&c]<!ct9uBLl s ~jT)gcSG!pOW_OKd Vinq@xCvoUz9c|DYDL;c' &% "/ =<菫UilZ/$(*ծ@N}yw2sGo`SO[Mnc?in<qw8||{r h]R=E3͑Pɚ>W4QOhabp~Nl}z<^elrv|m|̀UtCjb"@V΍LX=wb-.rW3Le{eZP~/lhmNqCvK{~Ywo2e\PƖ,Fz8G!'dNN2KBbwbk<tlNl::ry}xmy\sCim`X6VL!@2L4t#0Y0NH_Zukϝym'W8l<}N`N|^t?leJ^\mTHm</em <0O/E\^qw<*T9*8t~wYp5g ^xXKtNĪC.7*B? ',C-C:Y8n!Ò2 ®iN!yqkdv\,TJѱ@4(mmKCU/Q+HPBAV|j˧1'<ʀʀ̰sU}®108}Nvzzn0g`0XmQG巒tgHn0`hmma#Y:TK#B07dȾ.""2ג$R9w0L0`u0m3a1^ߨdޙZP<s&}o/vqpje]ϽXÖOCI>6θ-! גO O) 5JG]qw'C )cܸRo41O,m./21g5E:>NDa<L0T^1fSnUy0XʮˠT ?ˊϒs̿Ĉ"O.+)m+*,{.5f8AiJ8Q~[clXv0:Ɲ3mt?@ȣJu+(%%%'<)?04< DݞM]ؓW`_R΁iʸtm0RTů< GȌƆ)'A%#!?')18ZߧB K5?T<\mgOsN-@]g0P&Έá0`3'BM#!V%0"0,Q0r<:=FOˍYdaĶp}-JNi-!C"OA@r0m`%ݜ-&4A@3ϠLU`b0l|y0m { ZZ ~w&0HР:NF=lSv®^iu!iXi,eN2 2 Y5԰)]3ƶ@!pM{[gt-mN,*VIDeN2P~l 0Ϡ"d.29HUpc2qw@b'N|)A3u<ݘ 02%2BPm^l|p"Xw`8} k" d*M;OJ1mZ<.jz<VMvX;΁ZN2i}vyv0tdN0Ŭov!'2=dD!TwewK1i Lu|xtpnlNaTMC"!)X;Nsdat ^{(i2HJ0{@xrnjieaV,ޖ6gp : 5!UHJ\hXoO^S}yb{TԷy:wlvtbmBg? c* =` O\H[GXh`,|6 4?p!}tb"=` R+~!;@#x!Sttb!fpf!Cyl ƌ aj  gp e dW; c&cRY^~QYhTSVR{ON\]|p̰ >% (d+./|Q1t>1#MYl1*7EegX1?KP Obd0^ ]/r {Z/5V.NT-PR^,pP+ρO+pO+\ GM*~ I?' G&2D5##B#N?!h?{Q~prw# *@2(78Z;c>w@l2B@eC`]>B/rU@B jO@UW&KO?3k<G8>5.>C3=VM@?< >:,'|;'z9r%7^8lʎ' e6=y@GXLMOTvVlY_]Q_Ea=aX5@`}40*`LJ +`` (_u 9%_o ^"^E A!(]+ \KC m[T[BYZDV@ aQm\MJH D"10:B"IqO?RYj]xu_mcEcfAV0hmHlY4#o-o%o ,6 oGDtoK[moqcn4nm߮lkj}id_ [ X U @R1=E8FN0TYM]`cyf^ni"el[pmN=r>v*y| g } % }!> |U R|ok|Sa{N{zTyIx> w-v:qwlgdb^(BLRXp_R2c`f{kZqnh^r^uRxF={60"0C(H4\\NNel|y xFW2o~~xYspliMxV]cSizmizpmt~tkxb"{X^L.@W</=D3DLb+yf`0C0ѵ.ڊ~D|mxtX_f*cl;pY~1uxvyn|cc[҃QGF267&퐼pF.aI1rI`Luj38Jג<<8DeiSp^-tyyCpg_YVL@2$^"0t?80Gb]rv%,Mvlmyrow|ypt~ojb"Y}QɒF;:.00 R<P_N0DFT[ 2nҖϩm[nOyLN=v|bÂ0}|v0n!0ge^mUߘLAߞ 8<)<QNN.CXRl؁PNYʾ(/_!~Â@wqӒimb`ZQwIϣ?O2,%`~<0-boA4Ui}KPt´jǚJ8pysmDem]ҟ4VO(E920DZ#`/0p,0Ò*{>0SOpe.y0p0 , m8~vᗯoVh^`ҡ[&oSJJo@_7-25m~bQ'(m> PadCtv^ȶ%7z ǰΦygYwәql~Wft3^tKW`P밟G>!4)Zlʀ6!m&;ϯN0c`ttCвxūKK= F40V{uСn0hmc`ZVMD=Ͽ14*>Ǟ4`a$R9 J]qג:Cب㸔%ئ?ԴRNGvpӨemxwe`Y0RyKB:/^'L_ xN #!5HmZncAA߁_ߨw@~RQ!J؏C.+,,,*,g?049|c@HQPY`^'iwt|?"'N®Cpƻ 0NH1,N('&,%g,Q-2#7W=]EEN ^U_CgGpb}-y<!@]-C0ג®6'A(%! h"9(l-b4~ 8]׳AKHmKSK!]%eo2yz#H[ŋ֐</VNO#!82$0"0(!.4~԰<E [O:YAbjxe%V0`ȏ E"N= XX)Xض2|7:0@ȟL~YUi`j u{ 9wƖ$ה0,e" ?iڲ"-b*-4;?GQ%]w gqPpHm0ANA{  ג"n/Ɔ5BM Yen<}m[{-i֖@eA?~R Ճ%{0]tÌ <.,vm=xNp_ n*tfNM>3}vv0pmnjfcoN2ą0#t$DR5zHYk~<i }}¥|ԏ{ysmihc_O^|Z%n1n\ dnZ.!/A T} gzo zwQ t or ڮgp UoO |Tml|h)qa]RXURP!ƒf^~$0|;&vB:TrM`m^-`Bisf6 cE!6aͩ_6^;^2IfZ]?ZKT#PLuI H OF5,ޖU lK$D%|m's(l|*6e)11(`*RE%[*"Y V)l Sq( P*( M' (K&w% UI%< H%4^ H1$ lG$D-"bl?=5e:{99 t؁R O/%5*80R`0~<4|s5p8ch9]1<XU;4)"Nq:[<I7:Qi pD69 d ?8Wx| `;796e$6|5V[44)"33~ԅ3}3;} 33_ 10 /-Sq,o+;*(K*,'z'$~̓%,g18Z9x>}B8qcFAeHmWsIL8L EvL =K85m 7JJ 3aJR_G /Is ,Ia )G~ >'G0 '&&FB( %(E[q $PD $%D#A+!y=a!p966M54K1n̰0#*<7:@:AEI{OMrNP^fgiU^iHjqhvc gZ fʥ [ewdd$Fc b`9MZV7S#OMi.9u@WG_MSSW|[uI`ic`eU/hHik@7n$q v 8v/v55WuuMucuytBTs{rq>onTlf^b]\Xa="KF=LS1Z\}b0sek h^ak|WmMrF@t0mx0{=/-h1Jav~~NM}|m zՆyymsmjNecvK SWJ]-/b}gvkNn!neq[s@QweF9z:~+)-)0vH_1{s<ެ<vCbDyur(o ,SZ<`beVkvonPSOexډ'm"*]QJC:./ &Ͷ Lv'm9mKOǻ\`pmŔťno>Bd*muoVi-`ҡ\^VOHf@6L-út$ rɴ2ϊ%72IoZl}h ή,Α0js~Nqĵp뜟l~f|^YìeTLEö;4w,q!! N6֮.0"05պGF0W0i0{B֌տR]ܲӧ/+*(((*J,/4~ޠ8<=mBΈKT \mdlow[B0bm Z~`'B'%$, h"B)-O,,//29mBHǢRYRbkNsa~C0xm[ܝ<mH"N9q x!?'v?)ه/oi6={F=MVaPi s|<R3Nl_010O]#8+O1F9Q.AJU]f<vpz@M-4mO,B _~&,5;gG$P YblbyOeocM2F _  _ ,!:)Dž/M7C#mKOU0alwt]im\<>^^^Z0os~ -ǝ(2"<9IsTm]3hv_gmrO<y̰Y"-7A %N0Ygvr,ooovXv͋ޤVh?8~2֊0D$K/^`q!rXn/5l_͕jY/Ih۶fn*(e٤c b9\mVXRLKGDʮÇIh e y 1tDXo&3jGfNZb^mQ_=])ZxX`WP*ľU{TN>S3N :IO DB7?"=4ʮĄv Wp|vj dL"K]"\*X#>S#@RRO#e}L"yxh5H!ҊF~!_D }B?Ajмm@ty?4=Nl817$420S֨b0"4u(Q+1,vS/j0_\I1S3 M 4"J\Fx3w6-ZA.3K^?PAAD:I=Oh941o/++O%/49r<aCv]Ek@HO^KSMxDPD3S*T#RT;'<T=\MTSU,i^T}=S+RࢯQڴePƈ?O\؆ N1 -.(C2W=j?-}-ܑ'-@ET@{ vt~WY`czRd}xCSQb"tvΨmb o<o8xtzvs {mid_ nVOGu>1ޖ!(m<> iP*=POapr݄R o<Ŷ'  N:|e|Dv!og^b"Z<SLKD:=/P&tt *n;=!MZ^p!m< <҂L w%pjde]ϒVᔱQ-KG@e8</8 " DWC)9L\mn ~Y*3p0;Js@l0ia[DT^MؠXG%_= 2+2 T<diV&=8ZIZk|m،ϬPq^m<oimdR]uYà QlID-`92/ (oϵahX}Ǟ<#6GsWŏhFy(#'#R&I*--bT05fР:̰B<I=OYBb0hmvpBy$c<mp^~N% X Z x!%l(+O2G6o=ðFtLUp]khmn)wE&9jcw~ BeN9Q;ߧ(۴"'s/39BvJTiZdtlMuXF.NqviRY,FD0<OOގx۞ӡ")*0?Ĕ5N=EN1YXals*`~bquޡ+Dl-c D,Yʖ<)~0ct8]~AKO-T]o3h]rm&pRi{cJ(|qAbN0 o0lJ ֈ :!*04#=hF=%QYer/n`{Im\m[VŠ~_Xwb2~<~̰$-'8?}B`N-Uaply -ŋibA?0~~ڣ֊G -}* &2d=IBS8_O[k/yC@t6±԰?8yv0<;ˣæw -09+7ACQ[j v40`=By 5GO{Wv0tXpm~2ڣ֊2>5 $_1?OKRXRet UJ[n`~ysnlj8JÌk`o)!9F=Tb~iqnNN~?}}a|<| 9v0nlda]N0<?om.3_!A3?ABP|p_Rxny[wvtos®tir1s@ld^ZWU.}լ0N@L `a+2z:wKlt[p]ko~nmijiwf:ex7e`MZU.OLK׃r…c{Rw9!r3nFvj#WgWi2db{a_]\ZmYX7TeNIFB?OsҀÇo"O|mq811k[W,fIi,)a 0?] SRY ewV wS fQ ӚO[ OM L̉KI H6B<=9l7:4~DʮÇVWb i:Rz0ncD[7U$"P;8IKFKGS_WDrqA!>~<u;Xp:%Ⱥ99~8]7}2.,* *( (`;PVW =0 ~J y'4n%lda( V)I+:DI,> ,/ 8-DD4e,tX0+j,@+U})u*p'T)X%~($L'#N&c "&kM"&a7 7"#8]g6uu(*8,0Gx1o`42e6X7Jx99<1=+='&>=H"=RX$ =vf0 =y[<⌵v<[J;a:9m8'7Y40h 7+[ ^)6% d&.246y:p=e@ZCOE@QH-JMz N% QN6N-LRMa M5uLj, K{\J'IM\&Hu#tGFwC>o:=6v3D '.t5P: >xCpbGqeI\pMiROCWR5S#=W c[ [[.[\EZ\7YpYXUWިV:T˷TSOQMLQF.C8@ e-D4 FzKqNgR^?UTgYHZ<=-[,`=b,fPffo-beDDfyYegmd]d-cqb᷒``_ks]XTO`M:29BEjK{9Pr1SiXai\fW_L`Cc3fw$h9lppo,pBpWpakNo}ormn,im/kiwh}dC_ZX?<E/MQzUpt[ k^c`b0ZeQhKFh;l-n0q uixzy+OzAA/yU/ygy{yFyxVvtts8ojebJPCXG}\/u^lbfQrNcNu-KRϺ<؎|myZ])}b0y$hpYlhn0c2s\^vRxL{C60+Y ߋVK*L=Oaq5# :fyj sRolr<dRv]y}V}On~Ft=w1'C m ):M.^}o ̢ym98Фq_{msqo,vfiz7`}ZlR1JKiB0N8x0D#ؖ)b0)9.K@\mm~S00N ޖtOov0toVxh|c]T^ONG=ʐ60* tO`)P9:I[8|kK,|{ϜmH|ﺍ<Nr |ml2Le_iYGPm<HmDD:`1<(@äCbZ(x7I[Y9i"Ix8.90+oChb"Z<VN!Im?ۜ7_/,'BͧwUZ&6GI>V0gvvhb̦ߵ.< IldԏP^+YoRMaOEß=ޠ60.^#Cb0~<0)$5iDUPentݣMmv'+.ia[h3WO@IϣCJ:5,g#8 <s#33_BcgRLjc-rOpݓDDÖ Gp_M $h!x0"0$D%ll&tb*-b3Lj5=E0LT/[Nb0lr{a^b?:eFe 0#0mߧ(ڲ"-&~*]/N4~9dB"ImQYb<hpi,{>/O~u֎e7=ގx۞Ҥ$+0#.M5"<tEVLNT]ogq z<m33 ^~D,Y5%,73-:@LTyYqe lzxa&oommXj,e0A # i : xL:%;.6-6?;)DQ:Xa&mecw`2rK3,G <C-,eO0أ̰ TP \%p037AzJAV ]i"vkmv[<<<ZO?~-~ڣ֊bA --OX Z)R5qN=FQ(]GhsN4Cb=m!8tC0?|Dzt<;'7{ ^m%.-9mCN]X]3cEpmC}6KA=:#A0J8ztv0q~7ڣ֊<w `n&=3Gj>(IT`m}CD|?{Ov0pmnl<Ī<<EO,9lEl^Q}]{j }z~}r|y|ny|ܧ|ytnjca2ڣZ4^Nk?j(%2A{'Mx [?uhmv0yu--u/t^`rt9sNrqjc]ZW׃ġ0mi~f }+Ox;?rGqUVnenw/m|ml'i iwhܟhd]YU.QLN0?L:Nzewt$o4mxEhSOecdubW>b%_][^[<[YSVLGFBլ0Bctz<q lMfD,c6>_P:\b0ZsX>UpuS,^PlPxGO%N$MFB<9l4~x…b^VshbV]~%8X18WTRqK\P]MSoJȁHTnF)ƣ&DEJBNA`4@P?R;4~0.d*Ç:m  u i-\DQ^LGQ1CD$D?tXL<9Rj9T}6َ4yT32s1Y`Y/1.(-9+9 /& #m ) ÇIZ-|Ssoi"]w PD!@#<:C$4T%@(L@/%<+^%Q='&% cG$$v!=$+# "SM/!.0{1s3j6^9T~;H^>9A (jCF1F G8.h:FD FfYFlED#CirBbA8@>?=t9j3/,Wb }>*0R3O}7:t=FjB0`CWDL[Io?lJ0LP8mRT/TQ)STASOVRkNRa~QϑoO0OKMLPK<KOFA<9T(l213O; }?RtDkGEc KYNPRJFtT7U(WZd^_#h^(*(]@Q^T]h]o{\`\mZ<ZZtWVTNIE<,V58R@}EruiKOmpOdR.ZVSGYHZ<=Q]Q/^ b8 daihhm)hm>hmS?if<gOxgHwefe,dbގab_ONZUR92@2E~Lv0QntV"dYXK_\U`L`Cc6VfA*g jnr~rq)rN<r(PtrbqtqqKp^pmnnvkPj8f`]E_MQwW}oZ@h^`aYeP fwFh8JZZj!{)ٜHުu$v0hmpRlhyqc1s\OwSzL}EÂ0rFe=tm׍lҎ(!0s$+O2y6=BHL'T Z5bjt|#}-&˧Jre7O^Y-ɂ"-'v-.28.@)HmPaZtTa&j tJ~8t2mo<<R͎ee,A0K : xL̰jbih&>,3O=FcMtU]i0sN#bmޖ=,^uގt=̰ þ~."2K(q1%8RBH!RN\tftn{mln[W?<aܮbAw m!`+i5iIQ"]hpu,oii~Th~ytnl0<(m.,!Q,8iC}N0|Ztwdws3zt:|<z z9!y/Pz^|<}@vpmlf^cܐJÌ~ v%K28}@wJtV0qVbrrrtu0sK#s_sӧsNlsnha[[H0<mM~`--z.t9pFn<Smibjon0llxkj0h<h$lb5[Z'QQ#Š^N0i]} wwEq]$Km4jQCfAOe_Obn0ab`O^+^yP^Ҏ]]&YQLJFtġ@b:Ng,xKpCBiuudR,`<]KO\m]XlX ~UTSKQQTӁQOLFB=<0ƣ@k}sfa [Q&;W6TmHmQY O_jM/|!KOIQ*HFEЄDC@:40K-bbq3,vmaV QL0JCEUBg@Sz=h;^ :>7s9694-3l1<+'O#` 9Çq^zAnleWH^^B oY= )W9 = 5 P2 3c/ u", *+ H(; N&s % \$؞"T!s3 ~vn-cWL:W/ N* I%5!IR],RUo XmbQ|q.1IJ Ku_ igts s X!"c0}`upcl"c$X(M(!@*@.,`:.j/0y.Y0\C x/W/+j.}(.3-Y,{+N)(($ ~=i|,<"|&v0(mn~+b1Z0P4CH64:$D:=Y=F?\#?&>=>S=SgG={4<#x;:l9ÿ8m8<62.|)%u")0pub3n4d8Z<:QP>Gk@M9 C*EGaJLKODK'zKV=vKRJ{fIQxIωIQGeF)FDD@<7:3 "-e-b005Mx9n=f@]eDTHmKKK=KK1nNc$aPCSGWV(V'W;VPbV-cUv0U^TXjTmSR PmًPVNIB>x,.<49y@-oFef^I^KKVcNN"RDCS7U*X?Y N]Q`,`_'a/:=`VM!`a`sB^焜_^L<]Q]<]Z[PYVQ?K@m2]P: @z2EoJhO_RYSQYFGZ=Q]Q0_]%Ebm0digkkji'j9kLj_BkNqjjidifZf<cca[/XC?EzLUsPiOTbW]%[T^J`Cc6e+ghl-ps@r?t'sN9s KOt\HtXmsG}rryrծr@q nBm.l7g bI@|bOrVklXadR\H]o`[V0bO?gEhiP@}"-')_ 0m|6:}BHEOo%V0a`gcppk||oƲ)w'e,o}/Y-ɡm"%j,K27=NF=MtU0]f@xpiz'S^<Ti1D0Dߨ "!̰jb+!"&,4=D?J:Q\0ekNzJJ<w3%R&0KAE=̰ ]*]#1R&/5|"@GFQY<bliuiS(ǭ!0VZa~<w mq<%?+<2=FiMixUx^hKtm71V0~yyӨW˹W^m@~m$/29AIQ"yYgFrw~Ypl:j3BW}ztv0ܐ֊<z(X 'O)J4_< E<~N|0X|Dd|mq}|m~#a~o<~6vĚ9ֈ*{Ov0qn0</imm>L -|9~?BxJwXUvb0r<kNv`{wwyx{xYyz{OtnldJ6y<mC~' {d3^v=tHpRpi_mpinxqs]r srrtsld_uZ2>^NsY y t,r9nElmQgj\mg)hjnzjjjLojLi~kݚjhb8ZWQġ CmSLgpqw rn&k5g@bcKbZbhmb1xa`Xm]]-<^/]^]<]Q[/X PLF0<"N0i{sme}@`L,]#9D]=HZtW2ZgWwUVYUTTy R<R<QLFB~=Š^0m:K&z<ndd_/Z&jTV4S=CORPeNtwMJ GKEF<E7<EEU=92.KOvi[T QJ-J?FQCbAt?p=-;y9)8<9lء64~.)%"cCbq3zAneXaKpFu AF(>:;N7`a4q2z]0P-W,> *(_?'%|!mq}ti1`R+C!39)+ 4jz!/v5E+&/H'rS[Y$a"mJ!~>ٯp_c[qߨ 'kx [zqF h _dlT~:F7O%gXD ))"-DBW8,U3hD|zD 鋆 /l01D `c >k @vvpin4^(dT Hm"";%$)U%& L(%(&,(;3|(O'c'v'$3#몀"u!˧ {Nrk _(^yq "g%j^(T!*KR,W@.10m3 57 8#6960N5b5v042И2=0/eȚ..+(m#p%- ~%z)q~+Oi`._R2Up4M!8A4:6;'=.@CB D#C9B:MVCRa ArAA ?|?e><<0 O>}EfB-:jE.zGOH LQMN[N%M7`NKOn_NpaMiM{LXKJI<IF=NB?:$Dz,X/| 5!r<k=b!BZDPmEH@J>M4N&lOTUWX~Y$X6YJX\YmYlTXSXR3TV0T<TRQK@F@/3O{f;u@oG!eDJN\KOUxOLS%DCS8V-5W[]ia aYcc%gb7:bIbZncHkb|Kbaia/@a ^"^ >]Q\OXCT9}?Rv]EngHg4P_RWUPYF[F=Q]Q2@]'ab%b ftil.kk%ll~6kFlWlikyl2kkiuVjg #feb]~EupInQhRbWZ<\mQ])J^B5b8ad,,f!h:ims@s8s0t%t4tEu V-uftutAusN2tqqПpmn3migx?NoUShtYc]]o`[TbNeE:fKl0bl_3qkX?sNQ!vzL{C[}=5x.兿$ <c<[$2E@:N<\kKxA;Sdڂc0`tBZxTz<OI~+F@91,i"ᏒK qgi~ii# 1?bM[i w]XTi \mzW~\QiKO`C>62/(  PZ'mnG"0>LY^fAt2L>yܺȤґCZAcSJNr>H:AZ:ZK60-\'7&A מWP_$?p/|=K|Xa f`sN e MNNbNыގx.zq-0Ô !\%,6006N={CCJ]R?&[7pamau&b<U[><ZN]i-Ӆ̰j-=efb!-',Xi5iN=`B0IQV6_VYi)bq~+ *!ܠ 3 ̰ ]^o*"!p)-0m8?2@QIiMixUx7]wfopz<voP]OAO,K?}԰R0fL ` ^D$*2Ppk`v}=e#e+}5y@y/JrRs]pftvuubvv43v0OvkwL|mvqnfXX(m~ O< )x`1ux=s/GMn0Om3[?kfDk:sNnAop;qfrNpmstVojc]<zb {8uS!.q+Om6ul~BeKOeV0erdedpe#cA2iiddCjylVd?]PZVUCk&{$xKpi%e2 _:=bcIa V"^b^qr[~][-\^<[h?\ϴ]\\KU.QLŠ^0mSg{pmjEcO^+Z7ZFZtTQZb,YsNT-SATRmS*iRҖRTQLFB~ġN0vxKkGc M\V&GS2Q?NNN`LQmMJL{HJIuGfIuIuEG=42^N0m &}sfWTVMpOL/.cI=QHSM]C[\mBmaA@=da;J;;;=(:z2-#%^@OQvnbT_I=G#Be(=7:;H7Z<6lA4}z3m~2h.$0*+,)ߡ'% )bq3}ti1]PIA9I4 12/sF+W)j'Y{U$R QO)̴!lq}rmbYL<:,{)5$- ?@rTf_ww6oH ` p gt}vu/l~eLZ< NKAa 1  m& : N$<P`jzrlq ܔf ¤ : ԬpkW<T=} iumjaxZ<NDB=60%K !R!6sOKG_Dfq3(s:gJb,&DDk 2z0upak3amZ"Pq%F=&=:(+*},.5.: ".!f.6q-mI.c^t- q- ,*У'')%E%M$ D ({tQl"d,[*R1H.=>3O/5 7Q:t;l; b: ;4;I:[O;Hn;?:8]5.<5X4>4w~20/N,'F=M}%rv0(mk+e1]F6cR7`K9@=L5=)AbAq EG%E BFA E4`FGlE:XF jE{EÍOFt CެDAʩAܟ@!>9j2 &}Q)w0n4f9\:S=L@DB:D.Hi!IJN-QwPmQ"0Q4~QFRyXQi)RyQÊQϚPCO)M+LגK@I~2G@<."}-2wp5+o:=h?O`2DWDOHG?I>M1N'PRkRV-YZ<Y Z"Z3ZCcZUZe8Zu Y߅ZYåY?/W6UVSTMx60xq;pBjNF~aVHYKlSO6IRDCS8V-Zt!aYi\f^c$bcd#:d3Oc:C:dRd3cd]tdCcbğbᯒc:X`a`,]\Yy?qD*lLdKMi]FRTTN0WE:Z=Q]Q4^])_beh!j m/kl#lV3DlAnRma|m^pnflklmblj'gfbsIompOftS]XW=XQ]H^Aqap8ab.d#gWjN kosNt:tXXv!#tn2MtB&uPv0`un@u}tuBuǩt0sNmrZqo8mmTeWq`ZKY^|SraKdEg}Ӄ#1V@$LMZ<h[vQ ӽ 4`i[%kNTn<NqGub@x9{B1o|m(%W!6x |NN$1m?B=L[Yg~ucoОުPǁk{\mnX?sNOu+JyF|<~6.&y',?"%" 0@=AK1WterTہpgi쩱Ͷ%8Ф?WuRxL{F@x9gG3X,% bo;zi?ii4"0D<KIl"VccTp} tn[͇!@vTzjN!}IE>qg81*ޏ#Ĕ~T5Zk"8I.;IjTDaLnX{=8̗jE0D~@j[mm!d'dB-R4R9EB6<IiMiUv\,e{kv0mB<28tC" ̰ ]0%)/6=ElJqPVa~1i9v|YR%?AN0LY? fL `Em%],29sA<Hm0PX~ab|mi{r~N p{RI i?}԰˱YƂ"T- kO(%.0 6=C[|M yTy$_xh|yszK~|{|qgJ?}v_zBxH_!~'\39y@x!I@uiQt]sfttot{y Xxty-y'xLy|4{Oupm0<wkq~ "~.zt4v=txGr1QmXoennp}<qpuw;xav0x{unl im|3 }v&=t~1Qq;n4DanOlRYOibgIm]l~|nCmntopYn8rNs@nfXb8<æ<m)Ntr!n0+Oj5ij"Bc[HmdSb_cIldzabe_c[ccfKԖm|lb5\OZ'<<k+x oqkf#$a.-^_9bI]gQ[k]ZZi^zN\?oY[lZ.Z\տ[b0WQQÌ Cm]upnV ea`g\+OYw7-XaBSLW7]MUiTySRSQ1SmRiSUxOHEI^N0m&z<otb5^1YS$Q1N <KOHmNtYKiMzK K-F<G!F<IlIG1CC=4Cv,ylnbV!PLI,F9FIoB-U{@eA]y@?K_>(\<?=@>N;]2)#"N0i&B}ujx]RI C>$@6<E7T7Tg1t01.O-,<+~*ZN'#bb,zAneXZN0=95i .-i/A *0Q,e)v%$#?O<u֊&?cq}tmbXIw:a-%$*j!/ b>@21Q@C>MT>(f@w>M5Pm*S^S@T vLcuYvgvvru߄CuߒLwtX9uDtsRr|o:0dYXK_[ZA_R aKdEe a*EbQb]bhm`Vt`}]bc`b5cakkifX_u\Kkgzt? mgd%F],[7ZB0[#NrZZZKdZls-YjYWY%Z[|Z]Q[ YQCg,{pYhda[~Wj) V3-U@UwKOSVT}eDStRQw<Q*QGRLLP!Q<QfQLH<m](xKmi^xZKUSB$S41M$:=JF=JTLVdJrtH[pIHHm0HJplJ1GNF?=CN0m&}upiYQNOOLOG):E6DC@O>H_@fq-@:B@=bY2)#N0mh{q@cYM#F @<"<1;A7PR76_K7r 3O#43,i0|.,.(!~'#N0i$&jQvmbVK<37"31.+V.</O.a)p%EQ$""C ϭm0m`}uk1aTHm6, J&s$ &="8e"?L#8_zni=`f2NjΝ0\_kcq}vneZKPIC4gN K!~5G[ k |mu?]$̺}-W0Jdlt}vnf`UJ>/ n S 2(D~V3j>z`Y|gR~zAph`XPC8D( ~.BViGzO|lQYxq`jdeZSI $wҖ~bD YORN{; rl?cYWQK"A#65x'(P)+ +,) *(I*-q*@^*1R1(_dR)u*<)i&Bb#:#~!bR" ~   z@rE-kNb>!["Qh(L,B,7/,1!25f605i5 L58-8?8QQ7a76r55b31<2NM-ʇ-bڑ. ~+)hxysol"e,\.0V01"Nc2NE5=60:o&=l<1???O@J B0~A,@v=AMC:`UCGo?~@}:@@@W>M="׬NLMbL]MimiL{>KOLtJШK1H^HmE}8GC:Nt!*|n.i-7]5X<3Q@~I-BBqD :I-1K&=KOiMNQSOVXU.UUb-V<UKUZ*Vj(V-xaTV06S8SOTV!RIQOM*q5 g;a%>ZBTTFyNsKEK==O"5Pm*S^"TiT YYr[?\m]&(] ]-]h:]I]X_@g?]u=^/^]ޠj\Ǯ<]A\[Z[%sXRj>bED]IVN?PNAIRAT8V/W' Z`\]=`dffZe}f `e.f<gJ erVfcfsg fA7e뜖d{=ellelMbbabeI^M]Y5Q!QT LQWBY=Q]Q4^]*_p"bbdN-ij mnlm}m pG.n;?o1GogUpCcqQqo}nonnn0m]llavR+ZDT<U#XN\^H^Aqap8aa0c' ehi i~osDuv0u/t w-aw:txCHxUyb0xp w|umvxCtntutNrؔqQ][ V\P^KRbCe ;^f<4i+Sk9#\m3Knqrvy|m{a}}K 2}o.0|9~NG$~T\~`~Nk}Qy}}Q}ϡ}|m{Ȏz<֕y}W8`QnbN gEhi>j8l1zn^*pa tv!ow{Cm8!m,9riEdQr}^YjvCtß`ހTfOqhqIkB0n|K60}0]K,!߄Y%ۇP~+ ,8DP \@gqs;0Ȱ0ԡ  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~mft2!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  <?G@Q&D,ZG%<` L,fO4h+T=i[Hs~(I @{T$Bl^;F&egI,jIN5lKSP=nYI.m_qT^\iga-jn?lng\|Nuebe:HY_ߊ\^vNZUVÍTBi? Q؜NH L~KZkJx7J+KzL=No ^шOH%O+>K;>erV1AnaiD%fI-)lM5o~Q=sEUGY2q]wTzFneb/m/lpYCjxzPgKbeda{I1^l5[GyW5z$TM^B.QNN䝿%M𶚻eK|KZOKَ-N'hNbnOv3Q)?R5S ۺS}2TIzARGu\O/3/N䝿M+LN nJP LlQERM.T~mWUz tUكKUkDW_~jF;RF=M^p?Le?$ p;F -{uuIw4xM=}ShJ|JYW 2y adu,isdptƂ0jm4j_ٕ(fɛLcb [^MUZƅiV=,R)zQb짒N䝿N䝿O1K:Q%4RwSMTJ{UA-V`W2YȯYpZH:fS:`^<g?"oWD+:z;F3[L?ӁPH&XV'~_eyhgv:srUjp~'=l/sh)#dɦSGbש\ƺWPIEsT* R&!P̤O;(\PQY^K=7mU&7oe>, k=.!_sA)}5F4K@TAMG63TUC:m[defvvq ~tyapRB.kѣ0@g1bڷ(&]d-[Xdʹ Uq迳'S1 1Q솨S.Υ>UDW?ikcYZۚ \ 㳦]//e^'^~` _ׅK5lW5ee\9Mnp:U x@*+E4|IN?[݊M@H4JR1S%Yebwo|Lnx[wTFtFoLUjHeG_̉cwYּVAU!:T꠮dKW&eh>Y홧[Ϥc2]C^wv?`@zsaBuaTLb,cc'O49^_3i5r8+f}h=3'A1`F=(JFT#P SH xX~g_w:ej8]{uvx1}sߏmbhҴc a%[I-Z+|VuX6d\Eꔰ,__ꧩ`c🟤KcQ*d򠘝efAX?fCg.ՊS>4e^4kk4Lw*6+:^&?1w妾wKoxUN|Ow | wdzPvVO`P0m0g{-%1[@0,5E)e81}?/@^ڶAWH H5]CM\o=>W3iM`4k޶|zhkfMuفԇi#"wˑizh |||ѣ|II|ԉU~l}꧲0|jHA{z;&}y˜|Lxx|2xyODb1'n,M.0+L.Z('2&4,P;E DYYoJl PxYfݻe:r̝ KݱSJɾމ+LX<)¯c7=@߃6땰JhI}}l&zX xWҜv(@@'u=so.f.Kua-(O,9}+A7-]6/"'}2)m9:g=F(WA~U;EhALZ#Sn=ѩ[eT̤xy' Ҡw 0ߢ`{翙(ܑ͎6g݊BޝɁ-MDL疹~/}A{'핪x#uRbsDriZpɈcp2yi0,x*,'aj%)%x*.$2-:ZAaW=KMC3BayGz]qGLĘSl%`˭ڜuêo Xw óΉ]ښNTQߧǬ}:䈿yz{jy*۴@vG썭`u$sMqw9pnT2muBki*!z$s#$4%*+<>.R&CC6m9Y*E wKnjS5r`oD!Kq$E*j}HXj'uؙJpVppknJ-槺smfZOl}X@lNWko9iᣄ񟜿jfQgk`gIje Ίe7r#S ;%L*8^?>YIEz3AL.V5>ϕVc̶Wrvyԝ]u_nwݭT̀hq Łg*㭿Uhʠ8hdꃱqhR}zf}fPrf5eȧ\5Xdd#(dRXce8ʅu:" j;b[mШ&08^?2?n\TFzMgMԐu X\P]dg_r؄`|G7nehS<d Ocea\cD +c.G[c%¸8bzb"Oa򽗳bQBa.kaj4H`dl@`gͅOw=O0&08^?W@_hFtBՋgPjbRYE feƠAq摁 D}uYk,_ִBdWmS`A-l7]&g\TꪯK_8֧@^5_nv^֚`Qi^qB^[(^]!iu]X}Dj]&8Ag@[uHNqdmQl[sQf pم[RwFjkٺ_E7t\"Zc3X죩qZ6Zt[&ι&[S<[D9[)EɎ-[#CZ Z[fIiw]ԑ,-(" 7>iA[pAHog켳QRyʬ-[ʊ'*ezyṕzƨ]uRi~^`R݄GX\AWC"ƥV ŢU=7W fV5ڳؕ VWQ̖-WZXJX,WWϸV2|N65-@$ߏ8[>LKBAASIdRsŤ0[3Ff LJoF|V}{kBrgAht _#̲xVMTnT-SiRcUR:T(fR{H_S!=TFkI`TwSˎ3T!ŕT:#{SxmHS-%9԰8=A6N/nJ4^rS l'\xi kdFm2w5ynӎ(CfX?;^뿩Vm QJ+L7~wLzM7܏xwMLuaMYHsL mDm0ne؟ &\t62(TBGbKqUT:_/AZfla:jvk unnuZx]i!}bؕ~]iVVmRyR'/էM`|KpKs~JXzI?6rvHVrkJ vIapJoIܭuiIChƨ .$R72 CzE9JlLэRTӄZ_{b-f5rXghVk r mxgEraRv\xV]yQQ|aHx NZ۬{K#yHmtHVrkGcoGZIniF2RhFihxFy gFOd+Fu9c m֌]t!ʖ."j5+`8@k9HqBQJM8[LZw-a`CmEgbhqgfcezli>_2lZnqUVnQoNArJ`oWGNlGqm1E;fDd\DX(bAC$^B\Cy_pCis[̑D=  _<5)!xs@5G <ĀPDFbxLZ)RHp+v_Uhf[kfn_bxxa^$aaZ+ԓAfUWgQaM-=1I9גſ<-kI6H$ˆg=Z+B}3D1u,Ny;mAUALhZEbT_I`giMq\oMZ{SSU߃PRpUPWVM4aWI9VF8:VVEkXcC2۹V{AW=TG@;T?P>sLg=&I~(IB)vSoE T]I''aN0fQ7iWC/h\Jf>eYgmeme;zncbuu_5}]K]EYڪՅ!WsU/>vR`O\ݸaL𕕗 JIpHGQ#TBi?PANH LKkJx7KLN9ѦP6P˂i+P~<T+7Sa_zH,SqwU;:E<P>^A fF$)jIX.oYL62sRBsvXoNr`I[@ohwienpxajXb'g998cƛbt`|;\ǵ2YO杣bU?ԽP院?N䝿M&Lm2LPN#n܀Pѐ&RPS2'RwUI\U |V~|W$|j99K: WIYELY~(;L8K=7mW:be>, k@%zrF{/_xI)6>LNAC_fSN~M[]yem*sm{5Wo{lΉRg͘c`=[Vۨj'S1 sQj㧭O/3/?OR#՛IS˜VG X`X̒%KYC1ZZ[OX[nC]= A7yN]5Z7Je>, k=.!_xD.~G7]MAQNZ*]~Kb:lwl~tuy{n=jg}bC ]nTWϤWYX1Zmek[HY[Ž]J1_£4^d`1E5dR7B^_U74i;otl<$}BA-E5KClPNW^^miShv:uQ5sĂIn\i4eB_ Y*U=@)S/S-|V0.ҏXƣǗ[:ؾ]"ݝ_g_`b`?(#}e7?_eċRfRhI4V3{dA3l5X{7 I: &?1AGzAJdJ7QPZ[Ym|qb^oy{{,:vwT$`r@<k:UM!ccοÇF\Sˆ/Z(vZ7Ei]x蝴_ׯXbY證c튩G`e'fQޟ,g횑hؘ#i󾔁jgXj6jLL4`[3g2r3 M578&?3FID>uÛHJ/NYZA:Wlo9_1l[*`yM.{w+lGur}@pf6*`:|\`Vzb\0g{gĮp\jP컬4jvRjߢmfl1m63m nxvn'OL3k^_3hL2w|3 3?7&V=2@A= ѤBEIlKWRbjr\~>Keߘ@asa6ւ|fu j<dc݆fm–k䠾al۸in餴SnTunWĩ(p4y.p&Ks#񓝔Vqr󓕻Rr0Ɛ{|r\oO1`0Hm0gz1r2@5Z$9.AV>_9BF)&HVN8gi,XŃdb2 o?|G羅qyuCuJqA /kpmX%o?Ɓ+qަs#楻t6.t[mvi}vbuﰤ,lvN-"w:D|x򪙖zvsyt[VL16Gd0}nR/eM.2a2"5*;F7?C;rET|M k>÷SS~I\򷧅i^/Tz'$]բcZp{/v&8xwhG{bl{_yaꏊ|{l{Iل{U혪z?~y:<|x=>zgy%yx~wx TX}.3f/ua-(+/S?0. P2'71>B&AyMHdO {;WcTJb qL.12թE ap߉Wцγ$߆2ȃ‚EFz~: }ee~}Zᣭ{}}zy~(cw@~󤕎u~ tJ|0W* k-%x+m*z- [.wM- 3,ƒ:Z,IE}PD_H<>Jx>рR(J[IggezGҠw/;mQ㙘+׵ƋǧGI?޹ zꭲ{‰탪{N#Oy!w!tΆ-psRȔr {ŐpH[j* n ,}'v%~%(+)1)B7A7l3%a{)sqj$ "O$T!7B$$4%,^"2'.މ:C9M>=SiBiiHhF{qoٵTn̯M꺱n-.nZ4lzkRiii".Ɛhcgcf$ 2w" Q^[TPm8a "1+;6J0* C(niIzS*|aAKbsc MǃfjsQѱnwݭT̀iCjLUi3i߻c찗hĵzghgDfϨuQfڢ󰔀ee>d7S-c5h x:@;IJ?/&;L CpKIGU$zbw4pݼXz`ߟ0m-fJAGb`䄽1be-}cbt껰3d@)f-d)?ZcG4d5vXdUK{bTb_׏ a^`Z܅_#nli0)o.kKSaz?pO_l]&g]U]lzO^=_nv^Cd򕗾^%/^곞]Ɋ^[g^Ʌ%]#wsRYĸ֠hZ1o,$y=AODb>N3yeYӍ#dב=qs|%v[j꯽.^RCn[ZiW^=X6PY4Yվ lZ$2ZYxvYž_Yɾp]Z VZ_YE.ZE}wrDV\ *1.y:tif`12 .Ϛ*=nH#EWeO/hYv qedžmwn{@yAxWo]zUgb_DhUn㢚Sg;QGMP8'O؏Q -'PHPۉFQ~q"QC|QȨXzQ|vQCtp 8>Z! 2-Xxj>;EU^DMڤFOa.YtlcXx}!lO!svwΆlىAes;w]/WK(Q OM/-zMeu;MυkLLcހ4MN[~ME4ybMvrNxvQN0ʹvr)N*jobGmtҶ$g=1a*"2;90 FLDcP'ZCYe1`]`9k2xjvlp*rx-jބ_~OcD{`g\=2VgR?tMKdK)0}JXzI?6rvI5#t IrI/pNJtnIMhJ jќh2*=;$EnDPSkY ]{,`crgiUl0 qxng~;raRv[Nmtz.vve-_9)B3~zK=ntaUJF+lY[1Ie!c=QjbgP_s6UZ|}UWZLTiQ\OZM^)JR _G!'`E`HCJ]B[AAU@LZR>TM>b~K2KC(-oF.iQQ8dGWN=h^m]Ab]dEZkGUuhFUSi~IKQ0:cKM/L,K矬OH@1HK`F7MD0NBDOANO?L>MK='IMe}wNbuN#teO*yrX,; ;?@#D>P0@)_YG$3`J+]fL0hT{{_ ƅp[ЧX촺^VGސ*R#O&OLږDKX{J_$I_@mIJAM#9NN|P8}EjP,xPmvQ3t/;d <;:H>!MR@[DK"_dI4+hK1)n0R>/oVEHnu^)RAl-gMbqjnmoh}yvdO naj\K [RWYILTBi?PᅙN.;oKhKZJ13LfpL+֙O DQQ(CQ7}FSJ~kSWzTv4 9 =9Kh<eT>`TC#f@GG*+l5I0FqPL=tUH3t\TQpdba&mlpjzz|h9 c}_'\ⳑ*dYT|!Pr$JN䝿L֖SLh3L+܎ObOLr#JO k'Q8RȟS1T Tp#{bTvֲU|iWeJ{4L8 AZ9+\Lz<1EV<b'@!iD:(5oHW/tN-;,wuTG?wdZS8uc:dnmsmxف_j*+Beu0aʦ(^OxYǠ@bU_QǨON䝿gM[U'OhQݿP#ӈS<:PT:{U)UψWܯYEYZ~8o8aB8O>;Z9Y_e>, lB'ZuG2|z[N>`OEO~!WES$>zN`csjty4qrnmb萮Di$&KRcUg0_ܶpb\秏cUۓR)P̤N䝿QqRP:^U9V8ĕVUdXz/7YgUM[L߉C>Z /]f ]fT7I7 C7R7B^_9e>, q@'zIF/2~I;sO(EVU;R*M^yeCzhxItro1j̛wgԦI*aF.|]@.`VW尹sT* xQd駚rP퍥6S} U S"X>XcnYoѷ|[2ް8[sYZ]ӉܥO^YGy_o(_q9K6i H70S15a25wm[:^v:<$Bv0E8.sKzDTHuRTA[d-[exBLxGq+e@6L4`[3h3s6R}:%>/6B`6UHDwLPM(Uac`4xwxj-zOxkwcpCiF9d.=x\-Z+|YѨ_1ߴo%`raZd}dlA f)UFfphGWhÑi<u^iq܊ix~A5 2N3G^_3hL2v5wn8#;+~A;7U FC0KP[RUb`\yyrgI[tl}>uI˽]nԲf6*x_%[Iֱ`h帻.gb#f髳gi@6Shݨӥi]UjR 1liВlyˎl⋥l}QlkF2 TW2%ybN1en93N{|35w"$:+\=4Bz@7VFEJ4N?_s_X6x4bo@3 {Sth^qc̀QbޒDžWfXK,jk3YmJazn okTnĐp^p=qLׂqNpp=D'oH0YVL16Gdq0trU/d~4 4!56':0|@?DJJ]Rqަ]s}i=B|ąl{H,o'rwhS~ mnۨxnͰp伾DrL^sns2QLt멓9u[Ttg]uT͜ӀJv;} uEzcu$xtQAJ0Xt-rC1t3$8c/c?9?bҲhB|I:H\x WOqߴX`4dOjua)gdӰܝeڂyJV+vjw]F;yȧyx橻[y;ߏ y]Byz9΀z;&}xМ|nwBzyw yvtwtً[ML0]E-]k-%zB,!-+o/Qx:1D#j4*K97ݼb@!H< DWpDKOߜDe?JQ=Zܠ΂r[t!XS=팖\iXߚiʢÚun|ʒ,NzCPx8vbﳤtOUr鋒q󖕢pÈ7smm`UW* ~f$ 2vS" (ti%Z$&G ,!/(U745-0;G|D@^_Fa~bNiIYo,6?꣙N细ɡh!|ޠ|5x`窹w9 A7u 2̬sbqϖA p3n?ՂAU8FJgj(U~:a ò^z 8t杖göթ]گWXOV ŢU_SUp[-V5ڳؕ WvщUXM2cUWI~DWXWݽZ΃W{W~W9|X4yn!60 +!7;@NL7dxFUnr+OaUn7yy5p؍gF]_žVSg;QlQn'QP5󜐎R+fRwDSTʪ悊S|TM|S\vS*sAy֌M<^,#|ʷ=7H7nB*L8K]dUiaxkuz+vmP݈,d+D]OQbVMQIZNĊN֮&T8f6C FCHvKT;PVPaĆnT_ikpwQj sp@syj[ }*cD{`g\=2V:<䅧Q JLKdJCo|]JXzH$tIt5JK6r@JLpcJحm]J jJlhû2l+#72 @g=H2J_K UYX~:2_crgiUl0 qxng:~q\bu[v Vi%wLQ$R?xVM% xJxSHVrkGDpGcoFlF<{^hF0h'Fe/FpNbG;پ`b|R2G q[mOiJ0dXeQkYatf[d]=_Y?MbTdPpdpLeJeDFib^E=#ctCk`B[AB+Y^AH{Vd@`TS?/am7WZIdaNbeNL^r S,[|UkV8XSNZOZLY\I]jG+~]kEs[^C1[AcW(@LZR?Q>b~Kgbp_G_PeJ\'NmJY> xvNUObR؎QNXՙUPK[HRPIPVFmyT C#QBr`R~A\R@0hR0>sLg=&I]~;1^{;$rlD<+jXJ]/c>U%:^[>[aAXilAWqr_FR{EPmIHMxLJKHLH@1HK`ELnCMRA˲L@PֹL>rJ2=b8H=$MG;8`Ap:r? 0tʖv>7 y%*Kr8@NmC(g"J.vaQ3\3FX<8Zj^=W e7Pqw&>O Ձ3BLJ̉CI ˓BH 3FEOEBu DA3E?g1F>WGK<2b< <<=jF@;CPC%YFH$_|M.Pe%Q6fV?f]Jee}X&^en#a~d|m`؋vT%]-&{rZV񳲅oTRzNgKpJSfHXcGQ<;HqI)IyyعKb y],Mx0y&Mxx>M;sOHNs|O>p&k<s0; ;:G?aSBd4]Fl$bL.5dO3iVT@k+]MhdW#hckd^ezobxD_MX[ۧ{XĵUPhRxGjOLpsL𕕗KZIZHJLQK7$L_}dM+{aO~P|Qk;zQvµIR0ds';95x>K=oV1An_E%eI,j'M41mTA mZ1JkrbYjjfh"wrJ&e #{a],NZʑVTBi?P qM:5KؒKZeJ[6LT$8Mޅ֡MɁP, pD+wIR5|zM?}ShJ{[Zxdlmpp}dm~_iɏݔ^dg_l[Q]VAQ R)O/3/N䝿QqSU-҇TVXVvXv󫒗XdY̰'Y{ZBZm~#[9|06 < 83K8'U&7ocz=Sk=.!_sA)|E3fMAQqJ߂XZ~akunvkq{!RPkюCgޟa&]6WPIE'S1 sQj㧭Q_ɥTW!ՌUʜX kXrQZE&Zt]JZ]ӉܢY^ꠧ]D_B33 =|5FK5lY5Te\9Mnm< x@*+C2zJ$>OZKKV'Yx_n{XkY+uv,RoX؝7izQc^UIvWXMԷ,TR'S1 S*WhY4_[[e\]=&=_Sʩm_Hَ`c`xaxC`)33 AH6O49^_3i5r8+f|=(]A1`HO?"čMJzSY[O\l>hDKywusdŤ)l۝ݭufaq`RʖYVNqUX鷰YA}2j[ ]s _ء˴_񟛶adbTci*dO)әccc cf3I2 2E4RH3)^4kh3zw7+:^&?1 Ec<ΓeJIƓS\YmNc쀅E`q1xKpchj*b! y%[IX@까Y` T \ꃰ?_BIbbd0_0e 蟺f]|ggÔPIeq'h[&phJ9h 94~ -G2+S2c1yho2})|+5:"8l%'s=/8xB:GH /sMlUW6lq`щnPt}}u4mY.° d2!^ߢpZ{X_6 b9zeR_]egh5iJ﷣Ki]j=i쓝k%lg4jylkӋ7k71K dJC2EW1ug2rU/d~4 '5"{b:-=@:~DEAwIT}ɢSj}\Ɂjڛzñ{97~p-+hЀb"kufbN"e Ϻ0j曺j թ(kgFnϙnal mx7m֜Wo#m@o Ąoؐpx( nY:00 LB1[q/IiG.v/*E0|>2 6(B;g4 ByD^@G RD NVgfYiev׵]뀀݌6-y&[$m9ҁֿBf _jݭɉm)w\p~.qGq>r+zsa&rߣts!r GtsZs ~&t*Xx}brfyr0=0 O0_+5k-%{<-/i01 5E)g8o0(@CLpEPLg>VG_a+Ҝpш+}ܞuM݉sKWvw9GƈGwNЙxܸғxF%xC\pxwRyax `~zw{vCؔzwyux?tŋB/g cR/spb. n)pVQ'zv+0 1$5%-?=@,uAC PKIZV𡬪j 䨗J8ׯ/֕NωlSSؐS;} 揻ΈT҅T}'$ς{f|}J{x|cy_]{.w`yx;g=vAygtxfEe/{ VC+ f+5s)'**+6/"'n48,`9O:&-?9J[{Ea.8M|dV=+bθᴲ)ys` } kI5ވČ`J5uipO* cBN^{ !.}Wo:|ƀ0 "y2v(@@'ut?ܐ"sr~2q;}I, W)g f$ 2xt%i%Z$!.(.$ʄ5v3ӯ>UDTpnJ/Uh7=j?ؑP匓b儃8͔~溼؜}H}z&Lwn迶uܝKt펪~rxqp.pGb|nmHFklQ5 9jӉ Mx)4 `&O q ;%L* 5,6';MErDrkKI:W}@krIQז"Ֆ xքռpVu۪9q|oP%nymon&elѢlrZ[jE kiϚUj4h^g΅f~'Q% f$ 2x  1][1o+NzGp`ɞ{_]M`;a :;_Tz_~%^M*)^ԣ]jy{]n,tij< A O0D*U$|%U09xU%5Rti"/f6(<!0+,='BHXӣRf\q*ieau"w/mP݈,ej]l!\AV4+P?/OfN%CMNf~O!OGץ{OKeyP\xPljN?LCN0h0ǻ8F ~W.[ٌ9R( C;1y.QKH9omVlDPf`Nb|fqO]Qq%OZ{`RVz,TRKrMUNWjVKXHY$EXwCYB|Y\AbWQ@LZR>rLo=I!)=7b#l{@/*r_hL7:i"T wKSRK=&I7Y h5AUqCQ}|COAEkL2FIuGH@1HK`E*K@BHI @lL1HP?g'I>J,I=q5H<}y E;8`Ap9=7"2 &yz$tso._0nl?%gE(aN/[@T4 X-R\K8)U@c:#S2j;Pt=L~=rK@I,DF`؛GADCC\FOA>dE5?D=lЏCUA=p?M<>I>=w—?;3ξ=9;9ai<8i3:L ==(> 7?@>~(G4AQEZJ*'`}N0dgTb:e@ZDIcyc5QHeio[d^vgV_So] uwZ0V񳲅SUPMޘ!@KoWIΉHZ{G}VG^{I9{]ާI=x+xKAxZKt`LUr~CMqsMmظOC%m6';96<@>~(JfA/0SoE T],Iq(9cL/gR:?i YEQis`QfgLfs[esgOb+t_&|:[eW_U/>wQoNKd @J50 Iv䈧G+JA+J?}׷LY~1'NP}'Myx:uOTyPmvPrIONl:lA+/;\ H8o8aA<L?WC_F&gLz19jP8lVD3alC^'P\iga-hqSl!evnbC R]_Y_ŽUڑRц4O17KؒKZIkJ0]MdiNGNPO+P^|8R\ |QvµIR0dsSr~sE;)-: 66 E;O>;Z@}pcE'wiI1.'nO:oUC&Vp\Qsle`Kkno=g^|d3)_ae["vWjzS?OVGM:5KJ 8L:N(c2PP3Q)[R'Ԃ'Ry34TR~U|iTvBV=9u7//y7v 66 H;zTG<_><g)F=)enF-sL8vtSERuZRNscbmlqj<}~^fb7I ]%j*X8T^֋O115NH LBMOkO]y 8P#RK/T~m!TSTUzV}qDW$|jX~zX;v#: 4F8 ;7+K=7mW:bb=k@%zr6G0xK:yPCKc{;WQIxa5cGplt)n>y҄EiT[icR8<^ijϞLY`qUSrzQb짒N䝿MP, k=.!_uE/{+H5MCe.TQj|^av6itpvlzpf<`/1 [-ݨV}RkڪOCP#؞qRԔU{_Wj+>XLA,Y_xY?JM[L߉C[HƄ] GK[B|w[z/&+5^444 D4P'3LM_U74h;7s~;"\zB./G57LCkR/Q;z\du}]ghx"tvs{n=iiaĢ*aF.!\DsV(ⅰ 'S1 sQj㧭6S}dVOPBXz𰝼īYf[B\9\=,)^+Ŋ3^P,_iY_g^`~'^37949 E4{RJ3*`4,lB7Mvg9!C?,nWD6IBPOhXbcjvxqHrhbk"ڦcҴ]I-WS׷ T}&S-|W;4ZJ\ ӽ\E^^)_W/`|~fla*}b\׊daxCbSFc(j2[93 In3qW3Zdp2o4{7 =;*?1N\T b]nw0uj{yrK#j-qa6Ӝ^%[IXi0^u>ȝ`ꅰмcL`kMdK7f(|jf.tgV9g+[ggg[\lGL{9PD`}Zx!ng`yM.xЎ<@mɲœdRQ ^=l^㥾˘d޻g{Jg[뱮\jP컬4+jU,UjR |kΜTl*ll}Qlkm"p/B0 @?/ PH.d`0Hm0g{w0Q2_81 9/CN?<̪DsIMi_YNWxbsr~ yqqfjbm&Νg ;iiϳk*mPMo4 to\ʕnďpUSo@o ąp p0z}p?H}n#v00 D. kR, b. r.M.1\2}"6x+Ae=9AF#˲I[{S*vX%^oٗnǭ:럅(Zx=rыlIk;Loާȇr%s.s颴sүGtLt tbt 5B>t6K~&t*Xxztg4ztr SwsDT4-f CF- OW* ~f+5t)'+A71&!3M(]728~@:F>἞FZ/P1uJJZmR3i}靌tH!s"x6ՂWfziŕzH1zz蓷]z6'Byx1zo}x{$xSWz_vGOyNuwtً[v=t":&/0 K++ [j* h}*[z'&%P*u/$/6#1.=C+FBU12ƃKoTRT5 aﯷ痥x~$M׉Rsc<?!ݑs#ko~#ǀ|k8}J{x{{Zy=}n똎w{cPu{L'txeq{"(m9- N* a[( 5m%v 1#!7#V"nL)2&3)--9>Gs? P2FkϵSmȿw}q$YRڤۆnq_ʽ>(躷}%s{Qgy5)D xܠdpڀ']o&7Aj) {R&N e# u[ ti\ !*E-30^;Ic(A}b3H.aVQIG[eR]WVsʊ )ҧ+|Waz "Áx }橻v: u 2̬s6qKqܕ\p:(Gpnen.klQ5 9j:jWC&zT" h x:@IJCsa!,#H8AZA c/Hp TNZg6޲̎Wٷ+q\#nzI m )omHֵlZl 헩ko9k`Wjǝj6Ȕcj4hUg:+}c!c^bca8afwacn|9H_ pv8]+,8A?ۜA[ȑLr?6X##j As|tRcϮh]:薴@Za5YQnqZ׌ﮡ(\o̫6\ņi]\]wjJ\ϴ4]q^ &ׄ]#w[x{G\yThxtiĸd\<t+!۷7O粇|LHKdJCo|\K{~KO vLbuLK֌qGMЮbrMmiM3XlM_ipmH܂9ǻ*\71ͤaC&BMXN^X[awcfl0 qxngEraRv[KF~WS'pc']+ixn1dc}>h|^PȉNgYXkTlO`tlKmSHClEhhGE ah}Dd\C]`C_ C4ZD"d\C |WlCeU<ߚ1R-3$?1J?0vxU[Gm#`VSd:kX`v[]_WF_S,x`N?LCN? oLD!K}$_ᑘ3:!~+=+q{qmH6q>SAh,p\HH5bfO_]qQ*YD}RUTQTeWOY,K7YH^\DWCNYNAW@LZR?A6N>2KCsLg6V hc>dT5pE?PzANMɄzC[L2FIr:HFnGEk2rJA^G?U_G?+صI=0HH8@FFƝBD5BBCJ@ C=B;,@~:1?Q: >:[?A8M90tʖ2uhml$ afq1b>j^G&AVN)TSV/Q=]0qOd|1M~ol5Jtxb6Hm7pFW9Eh*~(JC SH?"[K*W`R4JcW=c_J#d e*TJbr|_@_g2\ OqhXaxT4~(QN L9K~JNHhڃF|Fhz!rGyFH{ivݲI+tXBJ_sΕKpȃLoQL1lLdh캜MUiau:r>t+/;\ H7z;2C={NA^YG$+^CJ*PbO2fVt?g1]HJPee}X&Ldq=basm;^͐w+Z}nVسR:P$L͊,J)H!GdG~bI9{]'IyׂKxwBZKt`LUr~CMqILgk;NjݵOj9;)/;d <;:E<P@ XzEM!aJ+fL1hhXT= h[I:hdWgFof`c~-of`, k@%qqH2xN=wTHu^nYphjPlOuygag\[zVɡR֝LN䝿M:5CLANLm܀PѐRt{ȟS1T 6TcIzU&~VK}VdwuW ZtXs~3G+655E E6P.7^79_Ye>, pA(ivGP2|K{<0}RMH{[ZvMekpqm~j*+d3d']QXm(RO/3/N䝿N_R@; STQcT8òU+uVE/WޯYI5YD~=/ZB }Y|wnZGuo2.(h2]64 C4@7S15^_3i7"s>%zE1McJ<ǃPiIY?Zrzdn{qq>m`1fȜ7^_p?X]BSQ%,OU Rc4NTyVG*WpڽXjڷ7YgUM[L߉C&[] GK[p4|ɞ[z/K]x`1?+J3{<9,3) {J|4XV3{a`4Tn8w;_#A/G9:ψ3[3f.3qQ6m|9P"=+ EY:/KG?SYs _.n[}l!es}w.kѣ0Qc[[9Uq迳'S1 U>ԖY.閭O[V־]"ݝ^,>`12`|j<_Zdaa`單aub2}#cb| z201l1R K?(1| P/2\2=nhL2w2W7"8<,%B;7I#FTRkYW[;l htw:yn oMX$fOU\bX !UE|Ye]윫i^d y` 1`›cbؗ!bd$ƏeJ^[cɅd3jddj|$-200 @- Sa1HbN1em0gzY2f 6"R9*%o>48HG"NDV|cY0mыf<1zFw{si q^bC-Z+|Z绶:_鬲`V7cè?deݩd}lfwefașf7gLqfoԊhΆg@Qh(~R$T22/A G/ WU/e.rU/d~0a2a7(=4DC`9NK^U!`Vo:cr_nGvճ+jxx_A]e6Ӥa}庻NƢc@ҵ/fͰױJg=ثi^LjZWli񵜅?k!kl4j6j݆k\l7UHk9P(18Y0# HC. 2Y+WgU,iGv,a/A0t3$9z0AAO H Sa©[QjK?^x,DPG~Mjq׭kXĆLߟngo5K3wCik.7o6鲟t,ƌs僽[t˶6t^+u:j t!\Ttgt>t6K~&t*XxzBt&jx:s]M w'r͆]vq9e,.C=+* Q*X :aI( 1ql#d 1#,!YX)+d1',;<)@iKU(Ig⽔So>&Uan,w6Ãnm|uЬIWՒk{ROC}Rr }b+|u?؀z;&}y|w:yx~xw wu>&vUt7.sju'0+C( ]U+$ mf$ 2y' ^[m $= ,!3~/fzugxLMw(Xu\s̀ːls5Tq;}o-~dn|5~81Z'IJ&H [r# 9iO M Dhd!V٭/K(j7e<?WFe{NCb/p;^=ۀۙ|*؆yg0v֪徼uatHs3vqTp}*poʌ,qm9>l͊k9VFw!%ReO㋂Hy<ٸ/kj?\j:琸?j겱;jR]jjziQhН󭔺h8dhcfo_fEVTdR9}dJx@cTe}0lm8i]d 2/r;=R;/H6oo^Uf̟|js錪V>e^<_r^ZЯE`|daqca rXbJ VbQBauad`Z܅`gp&T 7nTpa'ќ0Q(=@$IRVej`csZqt~gh9@aJBCZSANŠMwL KM tM{~NY+z N^vNqNoN|kTO缫jl+NBgb f̿e;/p$3=59`H6HQ-$UY*{b-f5mppDeFt[^uYFz>Sġ{Ml߭|Jd{I:wHVrkH*#qHnJo9I~tiJ j JfI ybfJc8apmptq>0$;1GATQXt`\\iV/mccj]__k8W)DlnRoNfp=IvXnfG lFH-j Dd\D}dE' cD,^DD]EVܤ]QFՏ\FA}@Z֌0גƨ0~!ŕu;.UE8zSAGsn]PeQkY_{F]Z͆]V.lvaQ3bM acJJeDFHcDcC_]B+Y^A:X@U @)R@}XQ@Q/O?pmMUϒhǻJ ~W6*8')E4toQ@i`\I1bfO_]7sR7XETTU1P[AWLYBHY$F˄ZUCYiAW6AU?Q>b~K^c)EZ5foI@Ut^{uIR&N!JN+mLL`ƠQ9H嫋O&FP\CQAK.P ?O<>KLg=&I;8`Ap;5aAg9 =77y0ߚP)'Bz5,qBe)FgK)/_TU8[g^>6W\j@SvBOm}A-LCJ:EHCfHF9JCKf@fUG?g'I=3QHG떞@E>՟ABլWAO@iC(=u@<̼>:qF=:)y>82:8M96N4lhӁPc}o`_g}-z e=Y^ FC%W}Px,RUT-pP;_0eNJek1Kkn2PJxe5Gu6ELa8=BV8A?:S>F9=&;|;6:9=9S9':8(:7 fQ66~6 2גĎ<srfYb( H\5YC RK$NS(M>Zq*Kal+Ij+-Hut2?EyS/Ch0A1@C4=2>1;2;r5827uĦ3^7b{R45֊352tٽǻN<78ui`c*Z)[% sW0R? COH yKL"IU$!H\$%{Gby'=E>j(D u?+Bj|,?+,F<|*;K=+;ȝX.9_ΣN.71׬-7´.5/J5|6/4@Μ.?/u:r>t-< 7;VCfAFL?DSK %eZO,^U7T`4[RA8{abZMX`%q1Xm\ҁLaZ%&j)VsGRvOgzLӲ}bIyX{6G5F&|EFmtLDpE?p5F~o,G@lH+k̼IPD1XSG$^L+`Q4ecYA4}bKa~M#amvX?I_|cz[ꏔn2WtT5A{CPƀkLJP宁GYF&|Fhz!FnueKGs.HRr$IjgpJ nX˦IcjvÃKJiLdhrKeȶK`;8E#M< 1l:3 ;:Hd=KP@ ZEj# c+L/;eP6fXA]e3aP ckCYa|hf1]0xqrXwU;~dQ ̃3McЄ 2K eH G}VGA>|A'GxHyvNکJWvJ}Jr ̊KXr0ĘLnILgk;XN\kNhͱN'f%9S6s)*738 >8~UKh<eVB@b^C%"WegI,ihN5jVB$Qis`Qffgh\cyi0`$u5Zן|PVسrRcJYMԈ@KoWIGQ, kE*&oYL62sBTDM`p\.QAl-gMbEht}oJcz^>.Y9, r8C+5vAJo6wrPAruYrP Qpdb_kqqs1e~`Y BZlT͟`yO!L|KLDNy5Q&23Q)?R5S/S!{U|iUwrUrbUp\tVAEnd50,3zN`c|oDnwk&%eT]y)UP`UP6#N䝿NQØQQS攛RUQ°V %bW.5Xv~YE0Z~ä|ZnU{ܟ [Wx期ZktWZ%q2"34/d/ F?1* Qa2E^_3hL2s6R|=(]-Eb6KAz7RPP6^Sd]wjyudm~_[g|_prWԦsQj㧭O֡o7Q9(T-6VGW{A\X~3Z1&|M[L߉CYZ=#3\iF\;|ʛ]-yח]Jv,]Ys0l$2Y00 DT1 TW2%y`0HkT3Oy)4EI 9?$A2HA1$70 G/ U#0e.o\04}3ek8l%'F>/Ec<MNeMVXUc%@f {'vu m q WcXલYRrT* U>%Y\헧*\FY]u𵞏^b Q^dק`|~faiaz`WaxCobb+}ێbzhbxFv uJNM(Uacbf|n| sUpe8_?!ZV{vZ:]g_k::ay7ͭvbec|_eqyEdϖ_eċd,e^$fOe{ąHfAyQ .*Z+.>.? $L) _)!n)pV}'v-+E1r 7&*=7CFJhtQ;a>P]z1!nĘvGiDx\.^⺢Ib讵>e<ꔱ8 gwgj/֜RiT`jjgXj6j\Qj0gj@7i+~Ji9zP(D0-A+Y 0R&N c' -r&# Z',!3';6SӮCJI'K^ XH{mh1e9o?"cHj)jzmvn础pExoQ=pxpUSp s@o Ąoؐpx( nY~m^%znOwn_}*"+g3D(UE&:YR&% j| N l  $!.-d O600m>=CfG][R{I`:bwBz}upup1:x4;!z]kyTyڋx䬤 xŀyXwW|x򪙖zvsyt[wt69vrt%shspa}#'}85'kJ!_rn!tD #vP/Q&8;&mAVиJuBmVHlChz˒ғ <.$˥C6澻jWb?}>}jzX xCw0~󥕉u~ tJ|0r{p{Waozi`ny{)'C)!ZTcEzOM\xt'4D4z!J7?sK*\sL kqKmJL NlLShM"[gBLzc>Mbba /!< ϒv(`˧a7-7CJ5''?1O|D(q[mOeQkY_+?`2X^aTdOdNKƶfTGdfDc}C~_hB[\A:XAZWBYVAAU AkRA~؛1POAJOxt'Q"3f4o"m@.x%MaM-=dG{>J>}Kx>AkI=HO iտ " /K{>(qJ4IepV=C]aCXStlJSiiIPP8KM -O_KQF% NCƆP'AK.P ?&O>sLg=&I;8`Ap;5aAg9 =8};8 ;RXhג]Mz~0t=,$SjJ /A`WRG3Z_>V=nRARz CN.Ņ9CL2FI9rHF llGD ?I@[`ŵGM?g'I=3nHistVhm\QFznN&] KF2I?eGPFEYP1C\VE|a%RCik&d@oe$>iw&*=~'t(j3c(i2 )2)@?!9"=P /DRB4MoFVTTMK]%ZQ/[ZW<]aEP[|o MZi}X~W.oaSןhQOY lL_pIdtFq~cE}v!NCmB4h#BMd]4CfKD"dVjDm1bc{EXaqF7_Fq^F\iG-[GW@:=S6s#;*0; ==F@;COD X%J&],O/]gY:I > SBd4Z7H&H`}N0`V:__GI`_iR$]z]IZxfUyilQs`Mx&J)myGzFhz!EBqtD|Fo4E?p5FDlvF+jөG~hHgžIv erpI-d_J|bvI]CYK# ]?/5x3)*78o8aA9Lz<1EV1An_E%cbL/dV\=Ac^pI!$b;hT_ye`[ hWȞrRvN-{J}/H5򀀊F&|jE>u F;t*Gq-HlNq թHn3θI3kJ.}iPIf"K2f( LLdRJ ]2K]?!5 2,6 066 C7O>;Ze>@EaD!$fLd0gS;g_\I]deVoaua]cokXsT5A{N@~~=K]HG}VFhz!GW"wHyvNJvKT8rʩKp K'lILgk;L$eMf NbOuc#:=51,497F6%R9D^=]eVD)&GQ8 >)GP8quNF9}ZYAqiok#|܀0dNl[|GTKӬN䝿vNX Puv[XReg=TfUgcV8X#nX7X8|>ZzzYqtIYqwYVnoZBl/ ,+.9- 'M#.z [j* iG.sL0}5U:(Y}D7XKhDHX!ZCyf+p)orwʄXf]T`O/3/7Q9(T-6VGXLAX3Yɪ\p]*̈́ݢ4\4~~\w{bK]xR].{tT\qVF\m.*F--yA-! .Q+ a{)sn ,z,28*&T@4yJnFT Y"#aq d0edj|dz: enues 6,#'}6'HK$q^$R fm ~; %Z.U5+/@?AwIT}R+VBo+hZ%x^Wh>a^8rdʷ{/fͰ׭h:+Σ&j¦nZj"ڟ'kْmlx(jΐkukj6瀊k~%jv{)|KjMxyjit+()':#)RZ" c& ut A[ $e0&:<&;T7EfR1Ppy+`ϋw!Ѹ|Fsxq֍sUnuJ葷'uK}Igl7Wlуnk=KȯÉbÞPFWq)~܅䬷}}l&{b𾠸xF򢙮w}єu{L'tyDsx܆qxpEvt|ouyot9w"#\6HU v`ui}m:_ ݸ-%>9BCf܅׵RiB fѢ<ĄYEu֯ؾ|tAJJr6qmЦpǖ𮠍o7.!o! m쏟mXmTKKl(sj64~)jvz4iFwCg~r#.8` .PXh|i 5x &&609Af@[@jN{d@b}rڍdw@dɭfiWf|eel e{gee*Ԋd3c3d.}cjzcLVPvb,qaqn1~A}]x'60U$`44Є}@PP N;m ` :}FyjXPZtԩY }`ZE֖zo[Kb\N󅓽\F E\5\̭m\ӧ]h0}5\w\su+[_q`[nnZj41NUn,PO0#G3/g:@1HWS?Na(l_EvwufNZ@RjPSV{R%_SM(S UoĦdUݽ}U7|z0UAWtUrVv oVjmUHhQUwae?`/}iء0q0'??^N;UĆ7^itpqxjdQ YﴯuR\߆kLKjLr}3MD3ycMvNϥuO}+qrNyBlOjPS>7GLJM|]]knhaċpaX3sR.˿uzLuIOurH$qGGm/G?jwHߗSjI~g7I/+dI_cCJ3asJ ^RIĽO[]I鸭YHct>!ʖ.=18~K"@Oud[\Rfl+]x^$aaVP\dPܮeoLgXH)hEhDnbC$^B`[C\xCyWC VDқW!EΖVC'QEySw;i^̚kthR(.};<+|JJ^9n XFbfaPZ|sThSOWO[QYKKZSG\4Es[^B[!AU@LZR?^IN5? .SM>JP?K?{vK`?qJ?ID[ߡNߚ'n-:%t&H2lgV9>g^,bEX7vALQ.LNhNSJ̥PGR DJQrA'Q?DP>sLg+Z׮F=7H<{xE;5aAg9<6C551y13-5T3xwk$M qp8weB$F]!NO-8U[y4Bک#A#@ݴ@n=u@;,@~:1?Q9b>28M96N4l5:0r3+L2A)Hlտ2<}oa`fB/ja ?Y7K'QV,N e0JpV2(I{P5D4C\j7sAM9r?@: /==1m;2:38yY26?25S2,5}24a140847->ǻxi^%Z`'Y* S,4fNAEeKL"HY$F&b&^Cwl'C't*c@}U+*>*;+9옅+!9Ǣ.7ӫ2-m6ƶ.4-24 -3;-k2& +2,NtʖV1z obVILA K/AIJ=kGpGAZES>gB\ Be#BnN&?:v]&=\}';'8)&7(g5&4}'3p(2H(2*2 }*1t)Ppu~rg\OC7uF* A6>>q;=J6=S=:[5=e"0; <?F$D}NH& tRuN(VV3X/`@AX(ݗA+fW(ՖAVEA@QBSBR*PBJOu-BMCE@:U;4)*75xZiHRqXyPTՋJXQLj`WMfJƈl*VG5.m}SDYhlCHjAdAaB&I`uC ^`aiC^ӲE _TD\ɲE?ZE]Y]DSLETFSn@;5 3,6 08o8aD9Lz<1ESoE T[JM(\3S4\[>\xgHZYyaR?W]5S?dOk xJ`ȏlG&qDpCHjBDuf0{BzdCdqD@ cx؅EDb!цE_(E]G&]2pFPZ%GX7G,VD'GT@951,49K6i E8RD=CYB=^H'v`R4J_5[,@2^e\K#[wVY `qTw&gOY lKt0pGsqrEsuD44okC9j0D+,id EMShaEgFe9GdG~az¶G^HQ ] +GhWxHWZI7Ut@:2"5 04 N<4 J7S:\@m`Gf'c]P4bY@wapc^M]uWY `qThBPoL# t4H?x$FxRDtpDknlEm F0jGyEjdGg$IfBIBc㽣I`I]׳J)\KgZuJ{X<5x22"3412= BK5GL4`V7[_?HdG )Af:M2NfW@4{abZMX_frnXF[0od W-mQsqL)wL HyF&|EBqtErFHpHwfpXINm˦IcjvJhڿ,K# f(KDdK `ZL%^pL<\^LY?!50l$2Y3E2 8C3%pO49Zl5Cc> Ng)F=)ejM14h3V@gdaTO&aq]8]f_WoNRvMͻ|pI+ ~F&|F *v\GAwuHuwJ! sfKyqKnLLMJkL&h3MnfɱN SdNEaQN ^N5N\0:=5 .1:(>1$70 F|1#@sH<2&qQe>\m]QZgjawaހm2\XwU*OʄJN{HHiJ`L'ǀQM'}j/N{rOwнP7.? $Na.# [j* hL2n\4-u;"~xQF*1xXN?$rsZ@Oiird,e}VqZ/]{V񳲅P|SJύUJ:L+!OVÇP+P~<8R\ |RyS5u,TsbSam,:SjNUi~T]eZUc5X.!l,3\-AB* U) a{)sk-%t1}F7!A/MA {UN\+rGddhwyula~bYBQyKْNp 8P#ERM.T̈FU-zU&~W$|jVw5#W(vNWGpFXAGnTWjW?7g?Wd / 6,#(Q5)HI' YR&% f$ 2s)}@+=d2#;+H>aROQ|`fTn#t|JieݐDW[CR`\tOS˾VXLAXaZZZb1\[W[ ~z[PwNrZxr[f,oڒ[SlΏ[jDZdf2.(((Q:#)O!G`" lz$sN'08h)0OC:MNK [g%swn 0jʒ_ ZSl㼧SViȼZ2wӽ\x]=:^ _n`_Kڜ`?`)aFu{`TxC`Ss` %p_l9_;i-(,#Cp9Wevw!B !)V1B"=6'TGK'UDfg„rpXKh[&_ 篤`^$d#qe/ejSg𗮙ug_3gLqgHfYwgӀzlopd'qbmq0Uuq,~qTnNppx(}Ro|6nnxn~xlzulmv.s{l&rGqZle;pF a #.9 NeyRYAh#&/';A BMGb*⬳X椂mGϯÖЉϸR湻YdҁK_}P¤{}}zyE}W혆w{cPvAyguyv1sub%rtx 4q?ryosBwdo:psLnpz6pt'1~A}]rPny ? &J58"CZ`KRxgDҲqQ豖sjq,p)A>6oFnǓmИn/zw9hӧYlYW{ X8hSY;YE{ZB[z[H%},[yZע4iu5Y.oZvsmZjZ?CggY=d1Tn,PsعhMT,F"8 ;g;{JSm&\ls7rt&cx 2KCSvBMȅCcIFFTqGCt]rH,@*YG>HFH=6@H ;8`Ap:r? 8C:n7/57e66&55^=476Mlt| er.%g>!-^M8,yU[y4BX @\@ȷA<ņ?P;։>:A 9%<46N4l5:0r3+L1 )1q)0$(2"`N޲n&ue:i++al=mYI&QW,}Mhp2Hu2F|5C\j7s@ac7>Nc8;R889KĈ8^9'd:7T7646Q4?4y.2V)x/$F,0nPxuz lV\`&w Ya6S%EMSs' J`R*F.p,XC|.@w؄(-?2>==1m;29@3726@3[5X|2562h4084-w2V)x0$l  rbT[# wS,0SNAJN!F[$RDj(ArE'?zf({=*K<+9R,O8Ҥ)-6ԯ>-D5{ͻ-3,2҈,22],2e,2-*x0&DOտ2<08vohm\]A[K*o KH<:!BE*ASAa!`>l "z=uz$=0'F9BF$C8'@7Y(5'4 '2y'}2a)2 }*0t(40)D0(2גv.ylbUxH;F' !B#4(?@@$g<L;Y3o0W]#D/H$/CP${/&.=$ }q YeZNB63>I;, 885C5NI5X4`2hlA2o3Xzl1>{/}2h M/n.w-X-3 +cI+L&G@?!5x:lA)*75x;V|=Q<.Ms6<{CL;I>I<DB=12Ctx=FBL>:CE+@;53g*8P8o8a@>~(G%DHTKJ!{PR*lRT^3kSj0?TM0<#M4<5M=\1N 8=FM?=sK]>QJ`?>K&>H7!>DE%>!DԻ>QWBE@95!+8m,6 08o8aAQyxD,PQOHLT`IL6Y0DaŀWkCڢ]dA`@\a=SEh=XP](>T>Q߷>O(?s+OVϑ?UM?K]ŀ@~KY}@I? F@?v@CE@5x2"5 ,48o8aE_:YJB%PG `Uz>AS@HW=@Tە?RZi@R,APAhMNAg{M!ALCB-J0BIE?!522#3d33 ;6F:Ow?SH?"UO+\XX7XeARU!vHhRzRQN՛XKb@_nF2`DfEdBBee@\%?JX@YdAZ6݂AH]W֐AUУB(TBERBR*PBRO5BLQD sN4C1I@;51'^30w0y =|5FI,7oQQ>\ZVE!ZO,YX8fY6c@XuhLRSSvO[ ~Km`(GɪdSDdB2h4@p^ ,@]*XBc_A\ BA*YBVX_CzWCsT—C%$QD QyDBPjDkNð;DI@94 .1:(>1$5a1 A5 2K5lT;:YE"6]M\-\ W\8eZcC/WsKGTT8Qś_Y!MyeHfh6EH]jB4h#;AbB)bBB]`lCY_1C]DD\EE,ZE]Y]EUdF@TF S0F+ZOխEM@5x/ w.1*q/h61( )F|1*,.C=/ J4/TW2%y[6Pb'@!dI,7cS8_^EZ]nWQPY*[1Tře?NCCh1J6ToFqrD15o_XDDkkFD:irFx?jөG~hͩHf0$I=c保I}bI)]J%\JtYIUѧJLS9J@Q;5-!k,0+A-! .M.,Z0Sz`4,e>, ixF2*liQ8d[rED8`jS}[`KViphOOnFKtiF+xWE*sG+s( GJqթHn3'ItJk1K?7l'hKmh(KDdzKl`aL_Ma]LYzeLVMU;/ 6,#'}5'HC&zQ*X :]-gg2lO9BqBg)nM7&xNI6tTFjRceRbaTGbAR"_\KR/Y?5*&x,#@cP1`" j$ t)|18#E4|`OEt_tZEht}o+_&{WhㅺNڼLVO?Re Sߺ&TWU|iV=xW ZtKWyqvWVn nWyj0W@f픊VbݏXbV6^m.5%#*"5bC s[g`s C! -3 ,?30XKhDɀ$Z\2ofmuZ.e3kZbF'SU S$ژf"WYY,wZ~ZO׫q[e[\\]~\Ey]Jv,M\qd\kn),\k}o\$Cgiv\Qd솕[`K* `'L9 O^ `n,}dn!, -#8-uF BY֐S[cgyvo쁮 PgV` GŮ\dxs8_Ǜ'a#;bnct'Ǘcd>Ict~[dC{@buJa#pambk `[f}iaSe-&df0 @!X3i"z1i$!1&>`<٤M[H^xӖu4x0opҽک jZ8Vlڣ'm @n2Hms…mKҋRlEk}d}l;{k yyfjgvLy8hx,pvi-^o4uGg"hr5he0hQ]6(LYbvPi #(76JFVjX+sxq&lόć? Ij &g~(lM|} {{(Mx{dxx"vusub%ssqs${pqMwpntom*pn@llTkkmHg )Y?[nt-͗ ".Ӳ2Y0ݮyA}NQk !f63}ΧtsLoGmј+ mOĔl* klmnjghzhi yxh53sh9|b~K=I>yKJ>QK=ۖD=8E=v EC5=[BH>sżWB>pAxl:lג_c.+ks?0&TeMT0Z`>RzxBKZ\EFTqGBDFII?>H=tQ9GK?;E ? @Y>> ?r<5ʷ>:1?Q9 =27(66V33+L2C+2,3o.r2o-!2-DR0C[nl$ ae9(@ZG%QW,}Kjq0F|3'CXs6?pv6ʖ]mtVbc!f [2XTCvLS &,H7b6(^Ew.I@>܃-=uǓ5/ :5Oai9\:5k7Yu68|44!3#1/". n.6"/CP${-b".e$l-"-U 0xlVaiS@F^9?mr;, 8H9r5cFu524S3_%?3&ji3@sO'1:{1>..nr/-z+a-3 +!*d+ͥ*?+b|2'{FoeYN0B640Z 36%3Q4K 3Bk.K# .SWR,^/-^gf-p;|+w*C,';F+W*c*)[(xa'B*vJ GE@;527/)g816;?2<5BqBIGfSO!Ht`(Ik/H{7F<(E BC7H?8H=A+H<hJ#;nI)9C?7E:O7<7=U7= E7;?8@VDIJ Q#|I](K jH0Iy,7oH >E ]C$C7H@ I=J<L&;mJ9C?8Q@28Z?<8Y? 8t>Z:IA 9~>z]9=9:9L: f99׽989G@;50l"5$%/6 8o8a?@4FFJOj"K[J)KHiO0JyG8I@?FDC@JB@L=>0L=5]O4;I9C?9Cu9g-B9*B]d:P&Bv:d;j@Hk;=L998:92E+@95 /"204 N8o8a@>~(FFK N"MY*MQig3BL:xK:JBlGGDoLAN>vO=mPN;mJ:F 9C ;fGP:EN܎;FD3;D;REA˕:s?;y?f;;<::;;9E@;1_0&333 < 83CO=.IGQLMZ$NX+Nfd3Mv|;eKoCHIDqMwkBƒQEg?S[Qi=*RF<>JLA:Feb;PHg;}G;G)칌<= =>E?/5. .*Z(13E2 8>G5E<WKC{VNL$CPX-SQ!d5{Ov2>L#D+I|iL{F1EPs?CmTO@oV8? 9Wg<0M\I >G?.G&>?C)>QWB>?سH==]E;5.k.+.70 BK5GI:*NARK|%RT3T./3SWcv7QtAN7-FK|O@Gİ}TC}Vo@W ? X+<O4J=P9>P> P)E?8N>sLV?Kǚ?J*@hIE?Eۺ?&DFA3D5?|@@91\**/,8:&/0 F2)K5lS?PUmI>$^UU1iU`9pT p5APQPJi}LHP(HV8DZAv{\1@\>uU?.U q@?vV@U]@R @/O9B/1P7…AiM`B@KAI1wCI:?A0EB?D@5x.'L#'}4-f C>, KY/S>4eX ;t[E#jZzQ%/Z ^ =WoERZMe2NUIZE˸^0Bc.@)\@v[5cA\Bk[C 4YοC@{WZ}CU—C%$QED RDbODoNDK7{EJ[DF?!5. .!%A%6'HC&zN+/lX1^79_Y`C$_N/[Z=+Z*kGaUR-P_Y5K`J_GdCJghAb:CbqrCKaWշD|`b͕E^nF&]7FlcY_/GWȷmFT7GS@iGUJPѪGwOsHM\F[H)9/)"#)Z! :#)K$qVa)X V]E-]dA3e>, fJ.c W=]gI"Ya}uVxS]^g!M4euH"jdNDm'D\kIF[ j-GxhlHeg%I ,dcQJibӹJ _&J ] J=XԪK(WwNK U\:JNQq?KaPKQN>5."8  1@"E(CP5^$R ff+5i0Nm9TnF-icT=bbbOT]KyGZ3V|e0Om IsvE*tH-t}J Qq̱KSpc K'lͽLchMhN5IdNaۨ>M5]NM\^lOY"NV#O.'S떾LNT3) }'L6U C sYp^xe1m# u,hx[5txAw+u~N):Thuit<( e+U<;64$ TKYn,\h* ǁ.k(A>CоǶPs^E}Pdbv颽|~r(tܘlR9˜jѓݏjk늲i֋j8ǀi| gfWvgmr[gM|ine.|wieFyf^geRv̭v -o/$uA<\USl mg^m剔nSsKOhu]KْqKe2lLȜj1M"[gBMNtbM_KMƯ:/]tNũQ\xN٤Y[N]VMŸ0=S NNaQNmNJhtÒ֊::0ގ!_v)P=>/-| 4QrE-e~SiXX9>_M׺dF{k~fE8McBEo;`[EBۗ\E2[EWFWFT G 4QGEQvGpNGʪLnGKRF4FL^{F֊bB߄Ҽ p P( ;'rIN9_WYepITW,P}KGSD>̈UwAA\U?q&O?LCN?5}qL@xTN?VJW@J?8G@FA+EAF$?DAZ_C B;:cCs8g?>yNу&>$y:"hJ/(Z`>zPz~CI[uGC=F4IO?i2ӽII=b8H9<:>a:0 ;:;U:n: ;9;w9bL7`| e n=4YLaG([U7[4LZvu8F*I;%>9=8M96s[34/4!/`4e14=m04e1 4r0&56j04ʙ/tϒl3ruq> 7ez0\'F#PgU+(In0]C1?}m5;r59s5m7Ysд5T6syN5M524y.1(/}$/Z$/&/Y&-.ݞ%X/%^B<`U|@v<}lV_ ].@TlAJP"Fwe(A{!+<ߏ',9Ϡ .7ȯ .f5/J4̦/3s0.p3RZ-2*Z/$F-Y(''WY(clMגrb`A(R% L>8GtLjCK`K"y?kt&/;/L&8(6(T4(27'2**D1י*2 ^+I1( .I )E&. ! /! hNxhmYILM$HE3Y@D>rW#~;, 69#4sJ0W2oh1u:0+~-g/A,*+M+@L*vJ (9)(۩%'s'v ߚ0n0wl`}SWF7.3>%01 .A +N *i\< )g*gr_)4z*Kw(>)(Q''Bo%$$mΧ%Fں M]0yodXN0?3O'0 a++k'7P'G >)U (&^. &]g $n %w'(>$Qtf%$m#$"Q"q/LE@;5226)c;2?A 7Ikw;Qn?E]f&@2j8#?LzN)/?/V>4;ϭ67:;8KB;696*94@3A1*1-y1&, 0+xr0+t٘0+2ԏ0*qQ2i-*2,ƃ1=)v/&IoE?/5x2/?"5 )F8)3 @f 9GP3;Qn>]sAi$@w*W@Ŋ2F>p75=7:9:9 լ=7=7&:t4?3K3"y/u2<.Q2_021-2d/2o.3S03h/o71+@2,K1 (GE?!5. .1:#v5d,6 06;?2:aE$>pN@\oAi{$Cx-x@31j@#$8<::}Z<,9'>8 ?7O:4?3K3O13H12[0qW4L.3P3)\0.:3W0Ϊ4H1n4 `0ť4a0.3t-+2*G@:=5._0#:2 /6 5=;Ej@dO"C [5 Dgi&D3x2.B刱4?97>~=<{Œ@9@W8@`7~;4AV1Z563E2M5U6n5!5;+556?54C2(ȕ5yA2k6%A240~.4{.[G@;..5.(1^04 N88>v@NB*LxDX!"G^f!*F6u0=C6KBz<>ɮ><n@s:0C9 CN7~;69v688696R8gK6679^φ666\#4@6&4653j6GS16\1E@5.*Q-*q/h1o1G G88@?3DH3H7T%"HAe*Gt2=E8C5i>p?䯌A5=`8D ;^E:+FH8j>7,;Z7<7= E7;Z7:}ѷ8p:8P99L: i8{786H99!5p<8MB30E?!5.'L"+g+.6I1 V=6%DB= HGsJ.R$ Kb_-0I=r2G <:#D_#@ AC>nF8Z?P8^?9GA:S@ҕ:t?!c9=Md;j@H:=?X^<;ܱ~<:=9Y@5.#!H&$tz43'>, I=1M6OrERGON'#Q&]1>OKn9?LzAHFD KM@No>TQHVM>vL?J*>G<?H%?9(D?HLB>?b?@@@?R6>;I;5*0 +N.`"*v9#C&zM.,RJ3*Wt?VL8(UXC3fRk];N+CDJj|JF(P(7BgT?W/1>ZTkj>Rx?R@|RAPAhMNALvAFIYXB IQBF1B!FB!dBA@>B>5x."0 !In2wr@cJ!U) Y1u\9-^F%ZYUV4VfC>R>~PHM]PB(HV8Cσ[g@],@\OߖB[CCY+_CzW"DZVFDSES FREtNFKܧFAJF^ G'EODcFsTDB5*o&91 C sT^$R fb. ez4fBq$aP3\b{DVxMa-PV:J ]EE~RbBdDf=F8 dJ H0Rb+G^ҼQI4^tpIA[xIXQJU⧯JESJWQ vJO zI-J-IH>I,F."0>6t0_ i:O^ [ el%p o\04p<"lKo2cf\mEH[tS=Tΐ]EN0ebI-l7FjqLIKo!K mwLjMhN#[dbOb9Oe^Nk[AOVYOXU͗O.'S4NzPHN LNMI-b%H5SGlX3dpj;xz @~(-}5Nz+E1pDWG4cl[;3\g WHp R ev6NSB{ſO%z·RvS`qwT -n:SjzTgUcU#a.Te\eSYnlTXËS.T2SS|PQMR"0 0?Tcpm}!W :/\>$.OFs`b_'تh{oHbԙy]|Y`YZ~O[y|]Jv,S\qA\m2[h+[}ew][bV[q1_7Z.[;gZ1X~/X8T {XdR  '8BN@a pm *%%Vj7@++HE?r["]Èyos}q̌]ʪRlo?!RhМb(ezfJYn,~vzr,aM12*- oǶ_;9oVMRYahp/ y |K|Xr@kf 瑋hwgmWBTq=nhwdy oZַsStQpqQ zlQ~ gQce*R¤mboS0`AS+i\PS1YReV^SNU{RmnlQRN_RMb;XYs8Y͋}կP۞X`:)h=2~+ޕRH-h j7[g[ڄNaRseK\ gI eI8`J_DIɽN[bKgiZJ԰JVKPTK!QQK8gPKfMKKKIKW4G!Qn,AÒZ+?sTB> "U[7&WuHM;!`6eLThSwKFWDۗYBDpWlBSBշSB́;PwC?@lNChMDY LCHZDGACbMDEg6F CPAJDE@d@97ӧ>oapm2w~2alG-ZZ`>3OE"FjaH@]"JR=#4IAC+=pFC(<ʔ{@i=Y?=hX==ƹ=[=O:<8> 9_wwtVi=Cpir-cD%T[3YK]y8C%<>$)>O:oG=9%<463J47 %664447E66q64j6(64q6rB37#I387aP27g_17Mxq0< ԻtV<*2 _~oR ~h?*[rAOfVB*=H r/A3a< 4Z8w56o}L45O2[4y.1 (0i'0R#'0g(>0M)0nջ'0'/˪&*0Rz&z<`bUM} tVav^& T-kCx:tY7pJ41/U.!.p!-f ">- "*+J+'h"?!J!lտ9mwhmYH65=, 8}=3Q&36e0xbd0T-g-B,A}+@L)9)S)7'zQ&"n5Iג{n<aiQTB/8&16 >1,J.P]b{.p*}***G'('UÌu$5&`N%K$1"l\NגpM~qeYL;-4U$)-0-D r*U (e\[ i!re !](24ie4Fx4ˈ 3!t#n2\&2׾+1 +0* 0*.- *+ +!-*4&+ 5ڻ*p*+* g**0IoE<5x3./?19e%?S*.'G< 0NO\47\7ie7x5T!!4ۙ%4*(2ؾ+}1j,l1q,1b*L-"@+,"$,s"M+!*ص,"Ӄ+- Eν+#2*i+-U*GE;5/. .1: 8(=[w.E 1N71[7h7Pw7$6t_(5,L3-Y3/S1r,2-.{#,!-$->$ ,M"yH-%--C$ B-$J-$?-#+ 7,] G@95.- .* 4_)*71!B K6{N+6cX}9fl8v [8dk%y7)6U-5Z10301-2-/F%r+.%-S#-X$].&G/''Ӛ-@#.&.U$/Z&.&#i,!G@5x/.).$2Y,6 03 @f 7kK>;Vre;?da<^vo$(:چ'96,8 0X5R14S33o2 2-/& /R'/)'[0A*Q/(Ԑ0*lχ/'˶.%Ő/&v/&/5$j-"E?!5.( )},(1,46;?29GA=V(?>baR\Aa";A-q'?>W-:< 1a:748Ï8h56692.Y3I12Z0o4)424[92ҙ492X4N1\05Q1KÊ4f/Ҿ 2+Q63h+t2)[3*@5x.)z"] f##)/),720 ;/7w@BjCM{Dx]#GCo<*%A0?-&5< 89};7c;\7<14$45d65u6$q6Y7q6t616^5Aȁ6i&4]6!C2j6GS16Q1ѵ5{.5-5,K?!5-!&u)q S3&M:+,0 B3D?r"HyKcG[U$Fl,El5@7><;d@ 8?8=EL7|>9 >0ԝ9}>(~:=>A[89:A=b;߮<9=28C<'25$<05v5- %10:EgJ#jQ, S15U-C4Q{R%+Nf3K{;F˔AB Fu>͇J!PMC?rLpʳ?K2H@>J.!@-Gҹ@FFA3DaADD oA;~@@=@t;ޟH@#9?72.&df g  0_ i:F5 uTYR&% ]2&]:?YdM*Ta7Ox@;J!eGDM @0Q>ZUD.@EcVӔB=Vy{CTDS鹴D/Q?xDdOEM DIECGE+FCDkB͚D@,D>uiD<*|0'N5SBnPX\e1hj+h!8d%H+[YZ6;USsaF>vOg=OvJW`^G]SDag>E=bƣG`I)]}HXQJyXK VᣪK -RJWQ KQN>MKK~I"G~IPE\IzBH(?Y"0Y' g 1?O]hp u)" "ui0qB)F/gT_<^8iN X僯YS䟖aOh LܨlQgLRnYN.iP;eեP=ahpPu^jQZG] OWK_OTXQM]i"ui}P{(\:;&qxL=m݁m_S^rdx a_ޓjK[+qFWxuѰUl`x¤AWgs4Wym&XiUY6GfXbfX_Xu\=WWVB/TZ,VIpQr}V"N8{U*JzTZHW"8DJ]uix E~ U ƒ3R$r4pEr;ZXYQtlcetn/q4iwBxeƓ}Pb鿀Dbr|1c1tbBoր|clB}bi|ae%y`_wt`ne]6Q.M9elbI{t8zlz}&v̻:KznsޅEu*rlrspziq7nuWom*pnmll mJk,hl1i^cjiFaigLf]hf3Y fd"U ed@ISBdbN*>X3hqz7AYe%aĭ 8/s_JFBTL^[ؕunyKwp^5{iB}f7l{ds t cmc瀘kUcg~4Ggc zjcYbx>`2b_v$Z]arY6`q Vy_pUES^mO_@kN8DQg'zv끺'1ѱ¾}0}гr1'lD=XRy2ofleRmceqV\7֡s(YJCrYrelXUfXޓdY`TYnU]XχYX WX]?TVOW|MWyLUx$H2tGcxx ¹b&gī*`ȥ>4S`Hlоjk[avfaX'eR ~gmNfNJ`O,]OZiOWP4VOTfjQXPPoP|MO=JO/HO(MF*NC:?]us86ÒRʀӡжŌC0!#n88)iwM=ZcseO%XuУU!Oë-XI|ЄZF %YFKV F=%RcFGQ8F۱NGLG֦JGGG鞻8FeHԚSFEGBaG?G> TnItCǖXΐԭտǭ|+߾C[ yE1GnQH1N[@aAPVGGgKGB͍MZ?,dL`>G>( F\??&%EI?3D?pA@@@F???g<@J02_&EDv\l# ]181 2c5sK24y.2+p2v,.20+2\۱,2\Թ+2Η+2Ȣ)2*y3[*2B'3,^(^^ T 0x٭ | sTגpRvehm^|QTF6'Q #* :+X+Kffkvk/ ~ l |3  8{s L/9u %0exmsbVL?1% y,="`R _| iQ~:]ym/6"-&4ǝLE@;55q1\5 =FFG!cP %[ *?hZ)Kw)D#S)Z''йb&̩=&C&$  !!WB!t`"zG Nx!!k^D"C  S >IoE?!5x5/.3և#u>R%D$!0$HC$"#cG@5x3..- .1:[92\%,B8(L 1,W1,d:.t..ۗvf-wu,: 0*Э=*+]M&Hpp&w&)( &ؓ&n'='T' c|&& r($E?!5.*)8& .*Z <5@J%?S** I 0 Vy0 cL2t0?//J!i,{!,W",!+'y(-()(D(T)=(Yɝ)Ƽ(U"(Gz(/(`E95.)&d %,#i3JN(:/mF 2R3a2q2+1ޖ! 0E#/6%j-%%-{$*, c+p,n"e+* Xܤ+V ֨+!+ R,O!g*I*q)s* +)%@5x.*"0 `(&/(",6 01B) 4P7B`.6 ooH5Vk 33];"22&0(}/)s/'.)"-$-y$-%.(-%Ѭ-#6-p#Ǽ.$$tV-#O,!, x, {,T?!5.&dWg5R"#\++33 6;?28L::\~:-m8#6W&43k)2‘+1KK,O1w+@/V&.'/:((K/(t0)$+/''0)'v/&/r%H/E$r0%2/#ǭ /&!;.)a v^&! tQ0'Ts4p0A ];:<3ID>Yѓ2;7}8w:97;7=hޑ90h?\ѹ9>id;j@Hf;;<;<9O;:GZ;8C<8@`;44;O2: u0 ;2r0}9.*!Y'mT&41N:H O7T,B S:OJ"K^^+Gv2Bϑ8?6e> @=G@b:ƛ@8:?.68?+J4>a2k"0m!>0:oGlQ]_[% ^_3ZD"RRX~0\Nqp:JfzCFJ+COj A@R^AOTGCZSD:PEt}N,EKFI\F6FBFKDEAEC?:&D<D:WD9C#70%Y90:oHT_gL.m$l,8*hC=g M_P3Xf C7ܺTEHLӬPoWT̃LдZ3J%9^2.H`WAJ^)'KYLVYM SLvPLNNKfI {LI^KHEJAmLB<\J0d@ej~bo`jqލ'_qWd`jJ_nd~ _`{X^^p\z\Yw<]LVt\_RksZNrZL$pYW InWZEImWzCf~1DYhWvx+0l㽟V'ő:-uٌMBYbUڔzwgwvOo(2st+x*pTwroExBom*pmkh ilmjLyekSiLbigza]Hh(ffYfdUFed@ISBdbNd9aM=d-_HaX_p5G +N>ThWv>쭣8ݧݫ T(S 1i3'#LGA;[kOlqqau׋捬jmn0g΀p4cnDp&aia d_}!``"z p\_twYJ^ou*U^UrZR]p~P6]mM>[m4J#[/i VEZgrtC 9CQevs:Ykոm W,J@3ވUQG_t"kYvi^``zd=ZBeV˝vJ=J;CD^s1~bsNq԰eёi=( 3; qpI@3D^Ța>CTz؁ILLFmNqC@NYBXKOB| GCod+F*CЬ=KDD qBDeVA-A;ؠA^;F>;ɻ=);):;w9<[9<@7<"7y,"(#m=` Tg{i*'M Ds0)Mx۫G- ]]*'PlAM}ppeVH9) h' )>`R Ri\>wwA<) 4cVRuRthm\mODa6`%P3DGYmv, ͊Q[pewlbVL=1#:qY*oAP`Slvi- GE?!5x55559sB /Mq[5h2 v_" .> T M    ~X )iH;_O̞dKGË"G@:=55//35x@ L'*Y1fuMv; g   = 0 j s- sXJ, DTFۥ  Ы ʠok¯FA;5....5=HY*eH &t !^ I ڕ7 "_4 LUE^ > 5p7  M w& x Ë"7 KE?!5/.**.-2$ =F JqU!jd  hr #2"."c!; JV - Z M- G 7t/  \ N4  T  > @95.*))*`/<FG"T;#Ba &!s O&cI$"T9#yD#Ϡ!Z"b- r D  ` tδ ?= Ŗ(2 @5x.-%"0"0X(k-J90!ICW$R 0%`3 e&pH'8'5O9%&90$%uE$co#F!."-#s#|#μ#B"Ŵ#t!a~!Y"v" v;5.%!So ~!;,!4& @'N( (]: +n1*W*X1'쨓& B&G&&X$% w%@f۞'>Q&b'` ɱ'E%8'2\%θ%le&~%5x.*!fL!d&x&~1:4*]$Ϋ-y# -&"+- "Zd-"친-w!, h./!R,F,ħL+(.%0{  jj8*[c1Z'I53 8AY8QP7Of 5R{2͒!70r#.C$~-"%+.P#/h({0)ϧ0*g1*^1)1.(#1&ײ\2*'#1%ڪJ1 $00"ߣe0">/ c-!0 2, A0::!h,>.? $>:>M ;u &w<l-BX9ť3 7ڿ7D6ڟ:i7!;O8< :4;ͺ<<9;08;Z6֫;%.58O;EJ3b;V27:w00:c.O9,8V9* 9)n0x-*5i>FO^ TT_/uPE@JU&GGl^/+Dᄌ7߭A֝=ى?bBc!=ET=KGUČ?GX%?/D{nA@YBԧA@AR=A~O3@h2>.߈Y>--)%5SBJT]cc;& ^9.AX L+Rb@8OOzA(uKGHM4FPE|RŴFg!R?G|NڡWGJpHHH-FÔAGwBmF?;hG=ppG);҈XF.9F276`ExU4De2 ~ $2jBQ[^fmr_rs Ko1hE8+`XQ=n[oI#WHP TV9QTZ)O`\yOD\ԘP{WrPRR9QOֈPL3Q.JgP9G OoC}N?zM=zL6:wK~ 8OuK/76W4BR`kuxN}Ѕ؛2*n80zo=)XEs8P;jdMe~W&!b3R]Q_64a*\d\D[e[\`?}\|Zyl\ VGx{ZVgR vYO*uXKrW(H*oV^EnUAmT?lwS=iaR{ 9Y2tASbq}<K9aBAĐ"F6U%nI7@}]I7usxZnqa{o(Gfu7liphk3jljjmf_jh#_hfZg-f*WfduH+G>Qcs3xjXaղooO q'/ "B1ِ8WD8~Nm TrL^j~bamdacE`c_*`I^#{hZ&]yU\u[pQ[?tgN\!p6LZn7IBZkkGFZioD$Xkh@Xf[?{XHt\= GW9]H9G07rHL~B6GY{f38F`s1^{w4xϵ8G^Rc{4-jJD)Y9\=9(O{g=HľAMC.Z B?ԽUC ?~@?(>?g<?d9?-8?.6?@5z@ QS42?n2@jn26?v/?Ç-Yr tC`ֈʝ>7JH56&pV%g'_A= 3OПVU-Gmv2>?(5::ͷS67إ67 84\6Z27>Cf1O6Ƿ/%7L/7b-~7$+7a*7,O)8g)76'o6k%n⛞j㠮!ِD]ab?1גOpeVl9Ul5F0O"p>wWp&u7^)62*R0z*3/'b/֩&V/M|%h/B.$/N$>/A#/#/᱄"0:"h17"0^0!10= Ixrce|D) ҝ%N{F-hmYD@K_&+E ;=eG6Bh/()+o)Od)!g'O'7M($'Я|'ʒF'ī))U)Һf<'R))'C!pplƓt)ו=ܚutlaKe@4/= .T_);%d##eO! Y v I . \a! T dU!dh ‚ "9˦ꈴlǞSa۞G$#}nZB5 -p6 (:V" v$`tT "MP&  Pp 8DThߨ^ӫ`aYw Ӟ,sA~mZ\8~ m\mK13c'D4`R\zl(g̝ڈ x]i^TYwג^Zt0!teTC/ ]0tQVih eZC 4r ]]גގ9lyl\mN0=*3 %q?X$qeB]]Ipif|ptbVG7' 8cPexa``N]]ÒNthm\mPvB3O% 5G\ml~8ZNdC̻!wlaVJ=1!i .B0Tet?2 cG@;5x5555;DKV0bq,ᲅ%*פ4[c9E@;50_//5;@Io"UWqaPq8 z N $1  G  J]~^[]ӧE?!5x5.../5=GT+`p3rZT{:a 8$ 1]ۿ{˲Ǿ%@95..**.4t9DbP _oMA*2dtT ;ޘz Tc\n<%?/5x/.*%%*.Z7B ON_E^qg@Rғĥkx͇0 #=2 s ƛ ¾)' z!:=5.'*"0!!&d*22 >VGMg_Z=ojWlRx- ) SwXY ߦi45 qN rË"7 Km (mjU5x.*"0!15% O0H=TL [tgm @ T  u g //Ng (  f͂ N4  Be 㹘8" %a Ư 5."0S- `+8aH}!8X#j !~ 7 # ]- M$ 5:\")"0}F"S#a"2!(Ƹ"0Զ">!]][ ͩ!t.%!/0 y5R( z20$C/P&1S '1h %{#a #2"j}"uc"d#~y[%L%k%/&XH'\%θ %%%ī%lc#$(!0mbE z! V(/#)=,;O + cu*y(~&~%o$VD&(Q*M̑*^+ +*v+#j/,C0)`{**=*Q)"00m~-p% X,x0'Ts1\6 <2GIX51'_L#/v,dj+*ʿ4*ت"+"[x-%8/'\00'p /%͵/$X0~$/"0"/![0!<.W8-z-W~-)06 < ;0+ 9Ca86ZgE 6qXoC5 2#g3|(2D+p1f:-ۏ1/ T3jd/u4d/5Aw.5-6I,5*ߣ5)u:6g)'d4%f4%?g3#E3`!z3l mW-!-5Z<~BhJE|H) b@D<4-5+9r(:)8% 8%0WW7!09:CKDQWj OX UJ4P0HA$L^3.$Iu6Y>GB*}(='WW 12tJ;{K 9zI17 xHJ5v&H#j2tF0BrFA.A~2t>K@YcmsNws{Kw[%Zr9!l^L2Pe`BzRayrK]ZP3[=TYAWX8AX~X=UzX$OwW~LtV5GprTUflDKqTBp&Ry>n]Q;k+Q]9*iPF46iNR4hf$Nj2AY/8>N]kt! |0C͉}_ЈE %12~}E.fx Y?3poNmyUSwjҤYqh\mg1h]j]fi\g e]VxebPcnbMb`NI`^QEo_^@nC`E[?^-ZR<\Y&9ZW$7CZVM?5%8*=QN`o( rS9z(]huI5n'LQfUHaI}mW ]B|ُWF[c|UZhx8P8Y8tiKXq6GX?oVEWl AUW-j@V]h==Ue:UTbx8MTaN16SA`#R4#!9:O`r 몉Cձ]'ퟩ6")K 7&!|~L2k bBa~$HY˓kKT/'~MPlQMN*L=M:GNpkDeMɂARMD~>N{=Mx7:Mu$9Lr=6Kp3a3Kn0o2bJk;/5K@`r VgVi^L.Ц<!b^BM/;oE*C^}Z\9U}y>eN+A~ICECdCɤHAC>kD;DS9CH*7DF5Cbd3>C{1`C%/Dy}90B{8,Bw*}J `s10:훏laՃʝ%2ݵŹ2sl'/cx>!SV/WK"t3D5d^6?_7;,8%:F6:L3:|il1u:|0W;=,/;(.r:X+;;+U:o')8:['9'&\9$\Yr ކFNP ֐,%0o]K¾Ʈs;a gD X7ITP$Aor(:Ύ+ 6OW,2,1Ƨu*1*(&1&2&2Ѭ&J1٩u$!2#3 #2X"y3?J"63+!2O2oFNJ`0-`Ⱦ@e{#h0]sؿvLף.#[?2 I~7Qi.1 (-WA * ) 5)| )ɀ)$x)E*)Y*V)%*v&+>J++Eyө} ȟ "ֳsN۞`LIAr#^5PA<.Bar($% 煣O"Ĩ!8)"h !]=!=!}0!""ɵJ#R"#~!ۢ ϵaǞ4Mǁ-w҆ג^ۢ{ߘlYCC6j)5$F_X 9 z1 (7c m۾j g?`R7Z++ XFӑQ͂=c*ˆ ǽ2 5 <ɠܚhO@ۢ\CsvcbfT?@$)4 MI_orRiiQOwܻ1֮ђ[Z}t½OڹvKY_A|KKNu.}pa:PY7_e@4`L }m _aQrגiרwCt+ߟZ0Gyl\mH1+2Q)mx1ϴwk`îas-SP? qdTB,gW (nGb0|n̻cr]'*:ޖ9Nwj\mN:)=#X?Yqici-b?u0QmzobTF6% :oZSPi|NlJNy0brhm\mN0?1 '5J^q?N?̰JlH9vlaiTH:/+ i=.E!Xixa?NNE@;55555x?!EMVcqտlG@;5x5/..59@J"V0axq ~C]ߟFuϞN^L@;5/.../5@GSao Jj-Gwdi?!5x..***.5q:=EP_4m~So`Cӧ=l95.*%&d%*.5@N0O\Qk^0|ol<htQՁ@5x.*%"0!!"0*1\ZǾÜ<B#5.%!S"0.9D9GV h {_LH >d @ V > cl t z) X.)!x|3%YK3 CT;ag>[{Z<}WǺYi.ݛ'ѵI'Ë" S &tتp*"00 0  a <.?(QwdOzI6Ʀ[XZ֡?3<U ӻ oʀ 8 *12 Z ϰ[a O 4 v _ "0 WW %F)P:lAvLaf<BwJ  6 o, p ;r Pץ!G#]Œ#,+$#"at#";"`#"#t!+y[ WWW { #2p$G<$A] "t #Y-#yD#|N#Ϝ~$Oq݅&89(3K)u)ns)X@)Si)hg)P(D(<(w{&l%yx W~W%),x-+-\@] -V .ll-{7,,*!G+fI#(+#ш,#B.$/$6/#e0"e0">/ ,/^.d. -JG-4Ċ|+?0W~W$3+@16;s;#](m:7:CM+ 9ch Q8zc#(6ΑF(5Qp+4`p.K3٨/׿4C/j5z.^7--D6+h6)E7)5%5$6$n4!^43#Q3Q$xWW*'1-:=AFJz,NTK0כGCEY%Cp,HA71?à5Գc>L8<ѐ:Mx<:.>y9)?j#6Օ-?(Q3=/>.ňP>,dž=+=(+~;IqA̚H*CӗSGwDfHtBۋI ?5H; %G)7~WH 46z=Gb3yE~0uD-sDr,TsUA(p@&mXAu&W+G9GQZ]dcHimp <pn Cj4b#eH_)_w\I8V\4tI?鐦YcKE1W qIUR@KTaLf~]TIDKxTBFsFSBDoR=oP:lPb8\jO5A5hMc2gL103eKD.dDJ+cJH;)FY);?J XdmbsweΈ|p-5u{,wY@%גqU;5 kj*Ct{fhIsf-_Mmd1PibQfbjQqcnbMa!_^:F^]B.^%\"?p\[<\Y&9[W%7AY@VN3XTg1VS}/VQ,E';?M\YJi{7|FbyėI]OxKXY~wbKWMwJV2tNFJU\pAT=n$>jSj;ORh9Se}/75QcVV3Pao1Qd^@y0N\,N`Zw+;%9N^nw<䇦N,љS} ~1]uߔG)Tf_]i7\nxU=Uo'@P=6BPLxTBJRAJL>I:^H~F76IFzJ5zIIxmX4iGt_0QG\r.G%nL,HFl->Fj*%Ffp(5I`q|׌Cˁ 2g0iw*Zh@"XWZ/Pss4&In-6DߞzQ8AJȜ8?jm7?M4~?$mn2>ώz/2>->+>ɃM*>^)g={&> y=&L=w $̰0.PARN|mvi_h%O0eBYsJ:Lh4 !0U#-h5("+!,,,-Cj,,*-,/-ӗJN-h-׏.xq̀ Jiװ:|NÎDeTK0sljaP- C'3 7$9B0b+G 'R~]$Ž#hsu#nr`##80$U$"n%Q#Y$7{$ $=X䡌gl <4D<Kz<kYB6 =+"9 %Zg !qZz |ݫX S0 bw G;>mԒ6c.% +  w tT e 6) PCۢgNb?ēii+tdنRޖ;'~B Gs-sIQ<LQjsmT*H s YobϘɴ =M9nwݱɛǘQ2:bK<s?0yn_OM.5s0  B~ C|d< xȭ2wmʖ԰tp. Nϻiڜ!ޖv9ilZ<I<2 63WKxaiN '{!ՄmƜSaϯ ? |pdVbE:P/.PCmؓl!-%ǞZ0&\zxmX`R(?*+Ge~x AV7IuIIRt6n~uhmZJ:'='%B0\mtDdԻIxdv݅#< ]6zm|bTEc3#: x =TliioJΕ.NRNޖ}qg0ZN0?1 <7Mib0xp3J0@+i\tlaiTH:,1 =/DXl~1 2NޮN@;5x55555x<EMYTerVIYJƻN@;50_...5:=AJV0cqwFMx?!5x5.....5x?IoUao Jm`Gil;5.+c***.5;EP_n,} | =?5.+c%"0"0&d*.5xAN0\qj}u, *]ϓN=5.%"0!*!"0*0_<KY_hz th- GM]@.'*"0"0.7E/V0g?[y ˻ΗbZ6\@pZx\V*"0x0=&d0_APcvV:[]¯s +A} "00 mm0t*:=M9+`1u,wFAu   { {``~9y=q { Z!x0WWWWm"85 I o]tbGB+Q+Dx ,4 F  ޭ Q ϥ [5 5x0xWWWW- +[/D3[ pF   @. W~ IO $ 3!L!T""#! ^  k! v J0xW~WY*0<\b&!Z<y#RT %#g %e}%t+%Ё$ >%kf%G'̵(mŬ(O)Q)Ev)\m%)i(‘J'%%s&"WWW %r)+!+0K0K3 =K1H1^Ϲ1t20W.#ú/N"F.w$D-^%;.N %E/#٨q0#M2# 1G $00v//l.9 -[,~,]WWW?'.5;>EB^* &?J>ƛ>T=\kt#+;낂':_+9 .R*8#Ȑ/٦'70H8.x9,9)싘:b(և8%o8$H7"F~7!k|U6y%5Aw4Xt3CWW!.9BpIoN~RN־VRˌT#H Q-70 LK%#AJa*3Hy /F䐓3E)7&Cl8Bى9C8LjC>5D$h2|CL/zA+uAY)esA(`q?(%>p)>$n=!l;mh;yWY"1-=-J S ZO_Ҁcffi%b/b^C!Û_YYW.VoJ5(TN:qRq>_Q@j{ObAy*O@uO=pN9lMjO4iLn2hJ/(eI,zepI+RczF'aET%`E,$]D2"Y"5ERkZc`ؓio`sYunx_it'SpO;G+kP,4fPe8vdc}>naBi<_E?d^[ɈFa]|Fc`F^jC]u\>[Y:Y&X7,6WWhG4iWHTj1^US..TQn+TO)RMJ&RL%"8IT~0b0 l r`w}-E {  JA4d}O}In'0oy^4Pduv<^t&>XsD@nU,r"ũARq@IQr&=Pm8OiG5OBek2`Nc0kM"bi-L$_+K])/JZ'tJX*&JU%1!8IlZfonֳxͿ~0`m'#/z|r,vn3BN!"`Xg.9Wjrf3Pꇒ'6eKކ8H.|_8xE݄7EZ5UE<Gt2Dz/ Cuv+Dss+Bqn(CUm'Bj&/BNg$VAQd";@c_!5J lZit}ꅉP.”0=mN|po3%$9 Oa:R㚹Q'K%mi*Dޙ-!?1 .tƂl8m P՜1أu!i^+@PCN=u)b.PCpK!Ő0ߡ԰9ʖ~P<KNvϻ~ `Mzq9e>ZPϒTƍ=5.'*"0"0"0"0).5xAMx]omi}u, =lGNƻ=2.*"0!WS"0*0_=oJY_jS{-ȟs'JΐV=c*"0Sxxx"0*7FV0g?y_/z|!GTl cl"0[x000"00_APcwKYtVo]Zd\\9JN!x0 m=*;,K_Nsd~NMC7EAO4Cx0mWWWWWs!i2ENZv*oUt{ "æCN {Rׄ F 3lAz   Z F )0WWWWW~2)F> T?jҖ/Ap~}GP0#h)'UGPPDy/%dKTus3mWWWW. -!99Mb,؈pxp q =87"_΂zy@ ; !O{>"\"%"tx!/ds/r.1o-a x".27`@~D,HiK= LQ}I%2TEyEQmDR\!QBso&vAXc*E?ݡC-'>C.=/=@/,I>,>#)3x>&u<3#{r; !"p; 3m:ky7aiV7f74dv5(7C KۻQQVY2[_'[+*.lW>nʔS?R&Q[i, OX07MĘ3zL^*6=vgK!D7BsJ57r.?-JCT|[_a4eiPk0n bls" ;i*6`eiK\#y:`` /q^x+4j \ՐX8dg[EF;8_YJ(!'LG} x!1-CDpN-ZAؔcincjsw: zq]|a]}n]vz.&vLvDJZisYo+'_pxqK2?Y:o:4T#n,6aPgl6Ml_(6}Lll4LDj|1&Jf-Ia)I^(#H!^1&eE[t"G;W"DW4 *FS ExQX5GS !`גirthw`{Bb~C`Čti.& g<ZY;S%QlT*mK,F.WC߹V.@- .B?~z,?i|)(?>v&>r$z=Yn!=k!_=#hk;,zw['LxL#)EI!Sа"2tU ?. bc q Rl n |   (  k(vĽ<0fPsdbQ;0BZ%]4R yTZ r2jՏ6 Ɣoib>< \ڥ0 |f# v^~ݜw Йpwztk͹]KK4`; ,,n̻Lt kX D g:ܻ[ˣ hêtd![Y\ZUaS/b=1>}KmqϽe®W%D.lC1l׬g7=^L~$0ϐ:t`KNKw_ko`QQo>( DD95ZzޘCݳ9kۻG6:Š_A2.Ũ~0ᾀtÎ|eƹrNg \PӧMC9%j O5iiUu ﻓ]גEiGqmϲt<ǞpYx)ϻmcCVcܟIp6"N kD |/aPmOolIl<`Vހ`E0o]N]NmýUˮ=}tiQں_ߒSDE:2!i *JfGlMN8גE0B<Ò_~m<Ӟz.^pe[?PB61!i ='BD^x??j`>ƂC}?v0`l]cZO;?O1#=VpK-F=AlKϻFf 0y\=s{iaW:VM]<.k8 7Pf<|?`~7/M,ޖ?t~]xphq_OTF7*N-5J\^te3<N~:=5x5...55x@GNZesNNUKhlϒxL:5x5.....5;EMVcrEMҖ2:xM5/.***./5xAIoV0axoC0nP~ϒiM5.*%"0%)-+5q;GS`gn,~0԰\J~~i?c.*"0"0!!"0%.5xAN]omi}!i-VOhϒȏ?c*"0!!)0_=oKZir{NBSNҀӨcl"0!xx!*7FV0fw<mPN =NJ@!x0 0="01TAPb0tA8GϒN~2 ,N WWx0);,qKO3\go=09<ޝ<ΐJNrmWWWWWW023C:lV0iQ}E?: ʹԗ\&4C[JmWWWWW'3;Ox#2cGx` Vb /$9 ˬˆ G O-vV.j| - D C$XWWW&1,[ 2LbI^ѫ(tS̈/: [`l2lu(u` m- G ݌<و/l qNWW! ד*/o2K?v T zT!i 5p"!˔ٲ[!p-!SU!]7:!h# #I$^/#N&F""| =! ~y X{ - wy  u* ]W D!%;^)s- ȳ/ ÿy.Z5? r/5K(T/*`滋.v;.>-c8,ݷY,ns,PZ-cb.;.--]z,Cw)+]t*Nr)/p!)27m'kw& ;~%^160ג:=> á@7C5@, >OAǟ=W3dcx,a]X8Ќ{\4(0׋>t.C~-L!>-lA-t=,~ef,y-ub+irV,Bo~L*ke[+ih Y+oe;E>Rًa?m]ēs}z~ Pr }o \ƛN C % 7?n1tZ[,mv5(3%k#T5"="B""R "5 "% #5 7"|~ K!y S u X"qX UW`eѡoq"x~+Rs`dS=^39P)Bb7! $aT m_p 1o 6! FV'Foki 5Y%ɉ G"~&lgq2z<m[y`k]QJ4ߴ#8ȷ.:;VNN^dkи܇lpEb P!Ց nckcsţM?sNȟ~NEV09,K|mqìdͰU\C-. !_ąA&Ĺ`=Ĵ}Eĭ .+,$ny0Z)Nφ3֖0 wSev0j!^PI>0'o ~2ΓΈ:oZoΕxP0`4OKʡ8m7.*nv{pe3XKƻ7#԰ \ג7 Vؓtsؔ9JNMQiƒcTp* >ɻv0Nla`sTE͔4ҖKa4`R3pY06힖^N0ztÒphmN\: PmA/UQ~:s0mN~l-kX0 mؽ (QŠRî0ϴ0`ᾀ~vblA"e԰YKX<.nQ O+Ifڝs·9ۻ,jî00 (ƕ{lorh?abVރI<F+RC{=' D_ziZF20ۻ,Mb<N0NmNԷvגmfN\SF=8c*=l#?YqήCl0\:0Ƃ˨֐yskbYjQwFJ:c*C :oS-iiNϻp<ڍrwqk1aYN=E7')*5Jx`uN?NN5x5....55x@GNZetZ<`,_liCҖ~ƻLN5/..+c..5;EMxVe\riZ<^4ɛlLϒ:N:M5q.**%**.5xAKV0cqQ~hQ GGӨ~xMc.*%"0"0"0%*5q;GS`g n{>-I-bMc*"0"0!S["0%.5xAN\tkN"z;4 x~::~cJ%!xxx!%0_=oNKG'Zfvv@ns:Nƶs.KLN8-J@!x=00x!*5xCFSOcqP]oi/NŋޝJ~N|Nk0mm0x"0N.|=0N0]Co2\~Z<P;wMƻ~2MJ r0WWWWA0Z(j 7FXCiPyoXFÒpcNL2/NE xWWWWWWW0um.@QXdnqv T 9}QNG@EV?WWWW46$6}JEג]rNΑ6={¦KQ}Zj(2$JVUWWlcX,ՠBPWb> lbx 2l;CE F=v;lnXX~K   6 7}w '{+Wqk `Uτm%_;kPX6e y,*0 M C%^ ʥMG *{sx  B  Mp`=kW{&yqSw7s Dqzknnyl;@q!N%LS%0Tаo&F# ɩ'[ 'p+'XZ&]/&t7&~O&`Y& 0'O'&6z&{u4%Jr$ pP# m# 7j!r h!! Rf"e&xג,1;46#:58I'<6;46Rْ6 g57}ׇ4S}P32ɾY};2fԥF{<2_x3)q38 k0vh=0LSd/Wb-&az,C^+4\* [0(  xg )H3Դ9 >EDGFIK LKTI3}F_HwEU^ ySDu4sstBً"=nA$&j@q%1g@ %de?$e=@"1_?T[t=X:yV:&T$9TJ6Q5PA4KN1"z:2l<]E!a?=RIQ' 7eًB0ζJPmXR ^b,egj}m 8rl#Ggri9R{\nfNSee Nd}#&I;c1$E_bL^$Bax$@a}G#@^aZ!?^M=hZ = V{?1s.;<Ӯ֮33Q=גkNRt_᷒aHAuM阖bR!{sli` ,VcHȣ:)<k֊m'ߧ1wfLg 0i̸ޝ8ŞJjmxaNn0g ]R2GDd7a'ًQ.,J dR~뾰CJz n29ŐV8o_[P}{snm6 cw[ҬNaA4^& < 2'E>`x^=Vh԰9>Xwʀpagы`!UDLM@V1$Dv-` #1?YteN 4ߚ԰0`ᾀn{͋smf]lSJ>1#;` :KRj`FNl}ޭgyqjr^c2\P<RHm=u/O%W-7Mib0xal<7F5.....55xAGNZeq}uCG σlϒH8..****.5;EMxXep{0- H l2NHN8c.*(%%%*.5x?KV0`nlUyUtD`NX27HN8c*%"0"0"0"0"0*5q;GcR$C]~iv0CV`ޝLG=:8J%"0B!"0*5x{AN0Yfpt0Z<ݜmۻNݜEN-J@"0xxxxxs%N.ύmiN˨h8/ 0WWWWWx I.D$D5CܾTQc-Ҁt}9 o<pŋN`k0xWWWWWW~ B,Q;N"]ipKˆGGN0<~0V?WWWf  `92E`EWEj•},jjX~rN#P:X{kWW#֊W(R<QYVe$yƎi-rkpOݕ`e@}zzv4t]<NMƼC) 4p J_H_~tOAN{M)UR`$(~1{rx u"<qt nt l52:AٽNdʨgFY+)T@ǣVIU Tk X   ېv  ꍂM6<j +G T m ~ w( A q8mj pfd.Ob*`6P!G ix!"0$*N,/C /!z.6d /?L /#b3.w'. |-[yz,ov,lˇt,X]sJ-I+m,f+a*_w( 6^8& \U&8 5W%2 V#R" N s& c(302 4Y;=uD?{AsCAr. ?kBy>Y2nr=o/Sm9<2h;xdH;~a:U _y:)k^':w[^:=V8S`5PY4gN1LO1tJC/ IE- G-,  i^'i4=u@~FKzN0QSUyS%ooQ;&ezOPP l^NfXL}:S}KV ONJV"1LI"II "hHI GHIEEgoCCB@@'6====<\:H;I9*87l ۸0]:ZBLPmWV8 X_\_ard$jOe&_c 3I MUA`HZMi_]_G^qwqC]ʎx?O\T[pZ8p r{*4ox0n4/.7n,[mc#+m;+ymFI*Gh )+b (^ (iZ :($X &Uk '$R d'P e){<^NG?mQZ-_ei:nrttvxh{nXItN@!5`:$ &/TP |*:mo3&ӆ)#>t!h~ڴ ~B˛ }s } w qmhQ.fucl_GUW\@IǥU!\^e lqjJt=o'k m:Yut+y]n׸ l`3fr*z*~%p,,ᑿw2o%`dRmW:@IQ6ި!.&I9EV0PqՋzǻVke~MrV}-ߌ0|ri^CPөAqV/޵i<M7SOn0F v ~mNCm:}w!mqbWK0=!*"bĹ4`ĈOje3H.oGkv ;RN ]y.qY h^MqTViFݸR9&~sˡaq2@̲M ef׀͐ͷg/s2݁ˡl8DimJN|mubl<bZPiBe5s%NՐ(Ո/J0c0{֮֮԰_ӧcϒuC-Mᠰ?PSexpm0i`WKá? 0̭" %ڜvs|-mH3G`ߨw߁=NձOAҖ0^t1{0tm/Md[SmHm<Μ/C  ,<)&D,] u鍌Y#jݡM3Pptx p1jŋb0mF8a+@  L~&AY qh/bM0 ϸ:z{ev0nht_OmWNגCv7,W ( =CSlJ?N N ÒxtrkyЋe԰^#>ToٮLVޖA%7+RC -7MczmN...*+c.5q5x@GcNYcmx]lV0Nŋ_ֿ22ſNU-.**%s%s*.5;EMV0?akt^=,elN۽ދTHU-*%"0"0"0"0'!.5xAJR]<f<s?~R^jxח (Jֈjΐ<U-J%"0![[!"0*2%:2C:CN"wYeZpm~օNV8N_~|@"0WxxxS"0,2<=_J V`snz<<PϯlДĔnM`G/Kx00=xW"0.8VoDaQ^#ikv0g:L˽MӼ˨E<Jcx00 0x'0>BCLsYӞf<sN0_0PduJK90xWWWA ~4-R)ߧ8ET9b˱qmÒ6¶.NJ/Y0'WWWWW~~"-0m?CNʀ]Al{!~i.y }Ց@RyWWWWW=fA )m6˪HsXCiy$)ml:iNcEX|WWW&ޡS X/:@PcumTS#X{kytWN~J@ĵ$Z8?@K[~^qEсM y}v8rqlNۢҡR92.CW6l$ u C $ j j h  y s q=]mejggd?0m 1 d %% l&oi& ]b%K {]$( Z4"gV!sT&RbQN8<8ג˧!)-!146]7;1%9'=x8R=U q8Sbl 7qhf6~7b5ߓ^O5[04o)X43ѡWM4' V4Q3GL0J.f G, E+ C*? A'@a'"%N2! ̠,4~?9>CBEGIyLq^LgJ5k ^HJ1`WH`jRaFvM3'ZzCMu"zfzy#yJyPx½x,xr8xy *uOni$d`1^,[.XH:EPK~V\^Reknq|msNqvgB{W~H. 2$+%HىaȉzQϒ 3Cj 0S6p@|v0pYmjijgdeʝIS0Z/bcRiFmtw~ys}jN^م N?) ;V ="V XwrNlmP$0|yurSnT<`QgnuXx}_ေxlcVI<70"0>8DVToN:ڵ#[dm/}Kyb0jKrw}-iz5vqʎ,gF\ǓP_A0 v6Rk0zhśFƻolmltz-xv>~tkbV,K|;,~MN5PW.h^ހgJϮmlڞjNkl4yccbᔱyÖn}f<\zRDO5%E%<m=3;M#pe|#5 r v ߃EoX}tkNbtWLYAbC1ϳ"0 bH1JbyRCÿLmQ<<}yhn0tf \ƭTmI;/l 0_h/VH _v0̲ ̰2Nv<uᒝmNy.s8lanYƵ/PǺ<FjD9+Od A~O#,԰E\/]Vr/0Vղވ ̸#®DN |mubm;f^ULB6*@l-.)C8Ypֱ֝,ܽ> Š89NZd|0ϧlϼҎaڕ.****-.5xo ijbw _g [XrUGVToNoNגȟmC+cA } V[}xu ks o J l6 Li bgrH +e@ dL_^sXSkQFOOL7J'Goג G^"%})-b/{1e y1!p1;7?j{1Mn d0b _0%  j>)c<&=_06:=Fo9=8=K 69f35 12 // =/ .,,+l+.  ZN!^,/5,=l@WOE@HiL}OwoQb0TXVeO$Ux&avF'T< 9?SSn9Sj0653Rh1Q7a.QLD,mP*PF)Pwr })O W'|J &tEk#hA#k?e" 8zXq LYh}L})wqukfca^NFLUx]b,glowqnhtcyXr}QIρb8R#+i@o07SwlsXe[um}KwrokhOXn<`f<ijrtt{%yp|mf<N[OABd0Z.60EPi_4/SV9~8{x:sm\qmf<xk-r?-tM{ |mNsqTkN<aV0pHq;*Λ_4N2e2},ќPm90~8iVpJtmz?~~xn0NeÎ,[PƔuE5b<$*:Cu3OPK+cz!vu]:AUVuZ0sNy]]m0WxڋNqώiϓ `0W#JLk?0a d 8kB1Hmawڡhzz!Mk:m|ma|Ővc/mg=b[dQޠG:,X0GS_1SubcEFtɮ</mjtiztqi`WNB 5䯞) MH408.ÔEn#ZqnÒ%ÒÒápvcrÙ}ttl~bd#]RIϯ?G3-%>׿"˾,XB[,o7RV1ǭ9Ù}voqh:P_iX)QϴEܶS;ѻ /̾]!b<iˉuӯ6*@NV0k`zӧ0ԨLҖvJAŐmNy.s8m!Fer]oVLF8Š.$ ɔi,?l(Q>Tg}PܾEHڕ2FӚͮ|ubm{h:b[lSKA6,WRߨ_$Z1no<Njы"0"0B!"0"P+~1^7!BLTz_ahm,sNmNûi3:СE<6^K[xxx~l"0t.,4D<FXPZe:'pĊ}%m!W!џp~-^Yxx0=0&.06pAqήLVa®myT:NPr~&x]u^KRc0    \ ߁*2;IŜS-6_kB%wtb'ԏ`^w xpc",6B0ÒNmZ<{f<tX-.NNN{Nl-c:xWWfA-ݡ^Ѵ$Z/< KX?9epml-iN s2,] VWWWW<?I)6qC/R[<^LIlzzF|K< R]<~{kyWW}nǙ2CQ/:=0LwZjzk<c,-]Vz.w:rqW:&@@ I~%4E3Uemv0JA.1-{kv0qnliN~CtԭA i -x?OPmatQmQv\CNvXyqlhe_O_BhVΠgK#N6^SJM]pm#1#0~%jv0nhbc]ZWU/ vȜY = YW-xYB9W%k {` oxU yu GKs_ Gqojc \5X\RZPN]J l<NƕYK B" Pn%|R;vPqU2dmL5yami&6faU%cd-1aHƚ_[|Fn]~ZwT6NJ`FC B?)lN> 0@"R$DJ$x'xp)c}i)1Zc)F])\RrX)q) T4)# Pq(J M( (K%'; 1H'H G'A( F' -As%<n="9 ~6u5-2,22NגήÝO}"p(!-047`w28i:` <GW:~:95%778w47ʺ37{2;7 17 /4 +0)-8(h*'`(^&i%#,#p{%`-|31:tN=iAi0Cv,F5jI[LbMNSFXNI=M5g7M2M r1Lc Q-Ly )L?( 'Kye $K6 p#QJo "%Js!\JA/FrA`=9Zt6;<r40ג !]+6<=BVDJO=xRkUi^zVOY_<]4K^+=]-%^F=!:^N^JS^u^ q]w(^/\&],(\~i[^cX+ Q `M H ^DBI?b))4y=zEkJ4NSVv\nc^Nbb"TbDfB1XjUm; mt%Fm@ mX mp m4"Wl{Fl kĤiٴi(e`Z~V RNK@!. ;UE!oJN]TVZ_Oyao eDftiXjKnc:pN'u tzq{K{Y5{ QDziyyy `w|v00u=romfa^NZjW=IN0V\a`ez00 T.~8ytqumXi^?d=izFn~[tuvm3z@b}YNC 60$Ď`2 Jv`x<T?2_ޞe1|uyakn<tmpv!y3|mrfo^?VPJ<Ҏ/  >]V1VHb/^NuQi-X\FٴMDRkZp^xK{}ρbrkcYYOD8a+K>Kǣxav/:PF~a\HKqυ<!mKuitz~uun0NeÎ]V0K@T5&kD).DZ<oϪv%m0vNNNmz΍xtjbYOHm=ϤC1#TQ-(C/pWHm MD_lmAФ`imjNme|Őtғm(Tem]-XMppDG,8B0.~!YkT|0-,b@_UbÍkr}h_llY®//߯hMr}xCCqib\APөJA660* b~~'*|p=ORiʇ{˨Y@9ʖ,`ȵ ;2W}t9/l~bhQ_@5XK{QH~=϶4)|, YXу2'CҞmÒ1 t ^"0"0"0"0$D)..1N9@KORˣ]z:f+m+"y rMmNq!ʖגA-^!WWl"'.0l4=ѯEN0;Ybmp8yCm"m7u^Yxxxx%|(z0 :tSA,L_Umb?iu*<) ARcx0000m= $Rג+O6Z<FHR[m]ghcs?~?t~Q1~-@w0a[? O<=a"0/9C!M;XXejo:<|m!|ACt] :imkA-=֊W(m01="K-Ukak%w>v5,KO@ۭ> :VWm T<?~ 0ǥ!xi*y8DK8N]hm,v0( j1Y?Ƞڒ V{kz&W~mޡրO #0>[KWgtQO1Pm<R<|myt}q~cuٛNäO (m60ENSOMbr Ji!0)Ȱܜ<Vz&tqlj ~mS=/=P@QN_~n}0NKŋ }vv0qlg co_O0/ڜ'O9J Z#j~ ?jƂs}<|mtelg%_O[hY U!~ۥԡv% /BUg?z|~.zxzupÒt4sqi`ZU!TOL2VCˡ v!6| ;&;}Py5cvwr-oclnjh4fd]TCPKFDAq Zŋ-G> }y s:4 ntIie^We)rb O"^ \%DpY߾W?US9M)Hu BH =>;u6u6ŋ8 lR|lohj!`!*[[7"@TU"U}P"j:L"~H"89ME!#Cg!& A0 >y? > |B;DA5 20S,2+*0N>ŋ0< KY!)&.m(m,|m/pi.-b1~V"4# O4!FMG4j7 Ay4Mgn;4+b63w 52|33 /2OX '-1ݲ +!1s {)1U^ (1BX(0##,8!( _$2$v!^.ت> <#-155{8p>d?ZTBBCdF2=G4}G.].`FFX)F\I%SFru!FR8E.DE}bDÒtDאnD8CP>]9D%5 0 . ,oKO(,7C;Q>=AfzH?pmIftK@YrOIQ7:U(W!hWy&vX!?Y)XPXnRVX<W Vʬ0 V! HU9T@S}qMHqC6>_K+LbfF^ fl $f7[fOfgf~deYdEc7yb@an_ZUPKGE'3_=CJMstS1|Wt[lh^+]`TgeE}f4i"n!rsNr2rLVrdr({qpߤ`ppn,̰nl2Gga\$WTQ8DDHmN=?T~[Y_@tVakftciWjKmp?q",%sKw}d}O}1}J}Ta|w{{{zozʎxw8Mtzmgc_O]wCNV0X^dw2hmjdn,YqQtDx?5y&}u{:0Hm_~0$ hG^0Fnm]qe.L&^N,~z<v0s\`h0lz pq4tjy`zYcOD8D*r.`EYYjnj:j<C qФd31}Qf)bnpa|uu,yDmb}c]օ SL@31&=0Wxp-CWkNR82DNN`0n3)w~:zcLvz^tprRoo`m/lkj=0f-_YTOIQIVC~ٗʖ!61 {M]v04rIn[joZgCed/b^]ʥ[ ݎZUNIQC?:7NVۥˡv2x~wVpj,|eA`z VL\@ j^X ~U R P. ѵM wyLJt(GjAo;63c0K-xˡZ;wiX_ X #sS=9,MOHcqE!wA%/T>A;".m9w67V6 43/b,Q*(F^#T m#M ]c [,ul1K!v4$zi&=[(L,F-Qdx>r-0@28-Fn3m-[.-%p*,'k,@D%+#&+Q#n!*& U* *w'I"? 6-ŋ)jX&.m(m./u2Nj60\m9N:;==1?*?'!%S@G?J @RV@4k$@s@ѕ?\?&b>Eu=:5< 8~4 /k*B'$ŋ0K  x,.06:=v@lC`DDQGB-I0M"PyQV$Q8" QO QeuP{O^P5!ONMʓLޕKGB<^942N'x-59=pEvcImL`N^TQHS9lV$ZY :^_m_"/^I]a^x]ό\\WZ Z^YNvV^PKJFtB>R%!,-060z<tq-mCUp_azޝ5+O!"Rt(:M`@qƒfq#fʌ92eGuLnohJb\mTMFV;sH3+20bti<VW'9J]pC®®0ٷYq~jeÙ`YjSҤJ@A-851'OR>?^Ɠ^%K8DJ FZmm}-ݢQšFǽq=mDo7hmb0\V0ZNG_@cS7N.V$H "00#Ε6̰FYiϭ}ϝ!lAϢ3̰́"0 @0%)+060Z<<EÒNW_OgmYw_<Np׃WRW,3N\ ."m'.0͋3Om8A H%UZ<{ekNzu,<ŠtwYxO9#*X/:5<HKP#Xbiup~=y:><< xFj<C00 K V iً0%$,2Š1lP:=]BMTQ^th<py|VV22t< : }- ֊8?q$.3O@G?P<]f3o}mip V f<?֭i lW>%1o8MAMiVcmycPZP<|cyA րJ i//^L)4]=J|>S^m;jxaa`ea<Vzxq ڰ@~C 6I .k8E<P\m3htc0!wNًVz&uql ON G JZd'3@QJLV- c]erP<W3{m0xqlhcuAJ,}:=HmSCa.~nLz2_Ts~|umf_[ZޡN^#4_B0~O{]|yJmQw[}xv0NwwnvrqQkG_]WWuTOC@ԭ0e- 8&+S|<xKltQZqfjxq}rAnk}l!Rlifo_DWTMIQC2iN~y"s 4nErkAVhgezZdCa`X^]od\nZNTLMIV@~?9ʀ%u8-pp=jt-Vd?&bSO^eq\.y'XaU\T4ROԉOMG ?9400Kۥʖv12R{n0fi`$\M:1WNS3bOvL:`I0DFk1DS|Bs1I@?CY9;1-x*$Z!ˡqY| p Ob UcP {|J62ZEEG@ \<Fp9T"5j3{Ԩ1U/!-Fߖ+C>'" lG}WˡZ;mnu{|`xancU! E#;%P4%(W/|&?f3*K&yT%& iR"&O}%m%]%IF%G%۵C$A# uvt ŋ;m,ul? %y)pm+OdK-xVz1H275&7P 8 :A97m:OB9d9yF 9=1 A8C7v7&46r^5/4Y/)$!/<[8YW6),S.x3=n60e 9Yw`iQv8|#uxv0zu״Xtƣsq-p]jd_G]wxE0JT}YsN\mnT`~ee[g SiQJmi@Ym3Oo#:sNxRp{N0+B:@T-e}~y~%@}}԰|z vPmau Ϙ]ϼm**t|fzѤC5^,}"0Q |<VO'&]60WHW/hmazt]*˨R+kj#mc㖐^(VPIDG:2ѫ* α?`.7YƓ$K4UE%Vf<vwǞ^mǢq ğN\ Q%)-0/]5u:t?OHmP W_h<q||m],|ʝ p0=nt<=0%G)l/86-=F=M U]e<pm{0L0<1>!^ ?Oً m%i*yc2y;!BHm'Ry<]dmyMmkOڒ`? ~  ֊8ΉesO',Qa6=FO2~X?al>wYxm-N _V(0<?֭V KLe]](m.8lC[LFpU_vht0mo`l< NKtmVyA ڿJ "*4[4<GpO\mhqJ~KKPJk{hyt}ڰɜ3x L#3X.:]ByNV-coR~ep@y]|muqmٛN = CL*W42=IoTV]0kN} iɛxunjd-|utրb ~ A\.!.07lElN]|j(|~N}o}}̰}~vqkGc_OY@ր"G $3?N@zKwY\tmf<uvvw8vsuDvv0ofo_YTTu`it!|,y@;wE8]{6~43/(#rDˡ-v0Pi]Mx^E@ {*q; B@6f T2 h. | +T ( g& M$ ì"J չ  c{1 !Zvqi c|rrh\OD?0@ +b!PR%7 QM.lbu7vw 7hkiq+ x&, jQ z4W ? {=~||t~ ahp!^#Q-(PC(~1.0kd1i20 82G<11\1q*T1Dž1:0/--,+(L"YYlW W %|'t.h-^1R4H 689%=8?B?A(B0B0A'YtAboA@{?<8>!=u<;:781-Z&"Nmul *M*~#/t~7`l9`~=T@J@;D41$(.6?}7[t>lCb0BX^FNcJNANI3SO %wRTYZZ)Z@ZtV0ZeISef<bxbcbCb0a.^][%SN0J4R:mAGkuJo2QrdaT`YT-YJ]?_4Ya#bfinnmb(mmb=onQrnbo[voomϭ(mؾm҂k1je_ZVBmGJySOpUgZ9aa_XKaObFe?9ug-iHlo~txxw)v4<xXOqyb0yt6wyv槗vڹv0t۞rqwkeaL-Ray/X&s@\ilaic-cZePjIj@Ym5bn<'qt Ny|sN0)~92N ^q<D~ʹ2+T}y|f|mv,q%kV-{Zr`Qmdae3g]wiTlCNcpFtsN:v0zN$z}B[Ul':PM/^Nn&~)Ml<<!zv}`sbklgfk@`nZqQtJNyDAH{6-~-m59W/P`>(;LX\l~NUNN&C`tw2hnjBjNocr\wVztL{Ft<(2E(#P ߌj0(m8IZ<j |i<<~aDeqmirftxC`zW~P.I@.7.$vcm .)bR- '8GkXiy,K/J^N]iއhVmui{`L|[vTKmE>=ώ05-"!3bK'?:7G]V4hYv0k<:mԸ<<{j}c^?W|Q HB0;2]*N%ƟiH38&=60F V0!ev,]t,v[<Kfa[xUN)F>9!/k' pPC}(25qY'5qD~TlbubPRI|YieÎ,]IW{R KșE}=o7//$PC<D$R'3CS|br݁pR~~s!oV?|0<=0$pD%+?140=BQKOo[?b\lmv0bnaSsaJ[c=ًӁ{ǥ!x(.3OJ8Ku?HmPoW2Zcliyu~]|]{cPÒ҃~bV_m O  @ ֊8-0 [d$t.4m=E^K,Ui_Ahr}m/t!mҀ@Vҏ͆ rH!%.8Aa<IQ/[hp!}^-??dӹJVxA % JF@"?*M5$<DROVbm]xpcÁ00/Vxuڰ 6tb~%H.60ALMT[]kNvDim- VyqqNiO !b)om2?F=Q<ZDi~(s&SVIuqk@ef}ր@{ ^"0m,78C}kM{{,XKwcztQwz{]y{{F'~|munf_]^ٛ-| C%~?21y$:# T [-W9YwUn0cW~G`<M 6U#"0,8/,%sM'`#t ņmlɺFvj%mh YWv4-ATwU l da Xa J]?:L&'w Z0  F6[-n i _ II?P W?+ ==xKZm?e=YLZ=#+&x( *(*}'m*>*AV)k(8( &&t0$l$D%#>" ^0 W, oxRu Tn0$b0&@W)-N.@/22 $3 E89 y9&t9=~7S8h7}7:60z34^4Xj114N+`% ~m78 !!)v)n1b3OZZ4N6F>99="(>AIYDDFt&tD=FFRFgF { C׌~D 2B0CB*@X=:A4.)""0+~E-y4oB6d=^|>|R?>H^B"=F/I K48LVPQQ&Q;QOPecR[wQjQoNޫO:NMMxJLNFB< 78 m(m.3y:tpp@gA]CVJPKKCtO5O)SOV0oV\OZ [&[:=ZM\b[t\IF\mZ ZZoRYWAV^N0JBm,6:=y@rHgKGaNMXQP:T2FV26ď3,ސ% !\<&=6CQ`%p ~k[ t,`~#\փWOnI~@;5+O!ߛ%. gxF3%3YbBQ_q<~1Q^<pؓwK]ZF|S<MfuG:?D8D1ϝ*C!t%0 Ce%4=AbP_-n0}`jٚi0V֊ًҚ%! &+09kC=E>KO,UiE_Xh r}dU<v<?-^ً+rg]N O֊8{<s 1o$Z )NE,3:CKyQ/[enw:, .lʊz3mNV (\ ՠ Ǖ q| !'.06#?1 G*PKVajtV<Ruz^~NCaV{h  s] 9wg"0,3<^UFtJTZeRt~<l ƍu Vxu !i  0|%g.860/@H Ok\K f@n}A c;cג<^VxuqNO,J_0v&O*3O3:E}LzVxKaixVmi|}}{0|q}p<]xqmh9}րN #a^<O+O%7~N@zKyWuZ`grZkNtyz<xwxv|)zz ۃz<vqkGd_b\ ~D' y1x =ztH?oQop^]nRi@s}rpspq@<rtuqh9_YT G-Vb%~zt uv,qd8RmCjNh[dgfVwjNjXjojYwkkSn,a%]^TTL1w Qrl$Rip1e>eMLbZbh_v0a \v]weZ]\m_XCTLCALe!x6}sCme`2+\7[;G?WTZfUsTmS0U^QQTV?R(0MIV@~=O4i|od _RW&U5iUVEOFQOcMtK@J]G H GqͽFE~A91=(#v!@!vl\SQu Jq,G<FxNCabBtCm1C0b-+O)(_^%,!i *O0wUn0cUD;6&!!314.I+O]'q%V!iC0q,eɂvT0l Ox16-xppgB\mN@.a)=$x/ EX]Ynl&V~; g Z=hnWWWW-(ybqwi`tSF5m 7  >j( us> XStsfTy-vў V f QL ; BgW zq) g^WTGk:p'P" k"8!": WQf ztV̞S+_llB3 /H  ={crjE` UU=#H&='.)P,r/0 6/d#0:-O/eT/y .- m+*R*(`((m0#+tPoyD|N|m@sN%j1%_*T~0L/?335$O8O:=< i=#=8B>N>b9WC:=7?)BAmBLEHsF.H#AI7H KJ`2JrHxGRzHm<E} EixEjCpB<781&t0+O|K1s68m9!c=Y>Q@~GF?OHm0J#LNRSSOS%Tk7UJS]ToTfWU]~TRwPAQ!NObJB>)1}O5x:lCeB]GT\JK"JAQ7QQ)SOUTY\O\O]]%]6\mF]Z\mkN_@~]ό^͞\m\^[Z<XCW8QJ6?:=v@nEgKO_NcVRNR#FV;>Y11Z@"]B^LLbefiec%Te:5iGOgkX iij|KjٌjiiQeNeba]wV~=vDoJBhQ`VXX.Q*YJw^?_5a*bftfjnpoOq%o4qFrWthrxq>rp̨^s)<q_wpanm hb"yLs_QiwVccY.[]T`LJbDdC;f1i&jl o~tvyy0yyl%y5qzD{Vy{dz~40+B"t 1gFi$4BQ]jn~+Ê<úȤ JVbgsK[BuS@zeOW} IAb9և81(u0> Oi(iM $DX3AN\]mzt`89 1ģ԰-\w[W~S1IςC,>6/&S \<|$03?:N0<\mljw[> 89]><<EYT OIoNBގ,;<4fS-_%@Yx!=i"1?BMZ4iowG^))20x?".C q֊8{<sV?jz|#M.03O:={BGUtPaV0b!gsR~)L0 1C ۻ  Ǖ qp@"?l(.42xG~O2zUZz`xhm{Bv|2~S8e{xqN_g<K~   V)u0q9pDazKyUQw8`thmyyyw^^z{j|Pm}}bynuokGրN J@bNO? !+Z}v5.wx=vIuRq\ qg;qtwuewrtMusw8z<<upzf_@N^!z $x1uvQ!N?L[IJlI}LIuiB8DYHFKC@~4(& iOCI{tfbUpqTMI,F9EIA9XB-lAV?>877:7=($Kyi!!;@{ocU H=A22>%N.*F*$+O"hBhsTh{^ ќWWW6-zppi%`UI8D#&<^aP dfwf3@W!k)!~sn0eWZN@1= "6jLa uYmk̲0!W !6vk Kc }Z ODa4-$Q Ph6IMpZa?u]v_P|{,6 IW} vkdaZ<QE8a (#x%''%'D(g5%K%;^L%?9 i?G A/3?@F=BZ@'mx@~}A@A>0=԰=);Z7:0a*NR}"-v)n0xi.=^5 U7`L:D>/8D@,BEFJqIuKOKK!!La4LDaKXKN0jM{^JKKiIھ8IDGFKiC>8$<+Kw1pq4!g9`~=W?DO@GF?OHm1TL6%LxN-PRUSOV0"V04UpBW)W>Vg%VvWVUUpSS|aSO0QLF~E-y4p8iF@~b0BYDQ8JILAQ7QQ)SOT#X13YU]]W]^"_31]B`T-`ec`t`ab`V`i]ï]-6]\ CW8Ry7r'@gkNEbHm]NcSRN=NmTDW<|Y}1Z']"=``cfiig$Zj21jC:kNTlbkrk l(`k!jjj<fcb]wtGlIcQ`U^WVNYH@^?_7`*ap"e|g; Qilns1qs8$Zq3ZrB"sNQsK_tot'tt]t]rqtp^ۭpCo)Ego2OAiwV`YZ ZoT`M/bcDdC:ey4i)lHlm:ptxz:zyl${43 ziAt|PQy]{m}~|`|Mzzzx9xuisiYe_]_VdRNeXHSi"AkN8n.)n#r|lsv0ax|}0#02@PGc\Cl{_JҦ҇~|{e^b0`fYg,Pi)LlCmtM Ygnv!]A}D )!`ZtwTyUNzE|Ai8R0<+"lvN2"1K>RLY"e:uA2xR0N^SU:{'PIς,C=X6֊/,~)" Փ ai\<| "K/4>JXe[q:U@orIwDR3? - V  ĉ >"0(H.5=<:=B=IQY~6_~jv|}?/ 0-Vo#Š uk ww ,HX|$,3260@)FPCW}aizhN|uQ{}v~N =IϒRK VyiSX=@ t&<,P23> ]ENL!yT{kavgvr'w[}{{p} |b \,Q2~aVyt}N_N {! .z!`&o0K{5|mAyK v0RsYpcr+pu}uw8wHwH\vx~~vqkKN , @ OA"0|m,y4vn:@99n;8([& !ieI!x3l\AOIw CF=V$=w3O;C5xQ4dC1v01o,.0%c)&#Pry i0;]{tg[M@06D2-x,,[<+Q+Oe&.v#m" ;:̇0 O .~wUocWK< -2j)D!& Q:Nctg4b y , =WW,0-xppi'^6UGg6!kC$V 83M `g s!w,NWWWW6-(zrn0cXNCE1La !5II^$qC0md!~wUn0gB^6UI;*<e2IW]bp; }#o; N1!'W~wn<g`eW K &@^11 - M k \3O0G [0pC b0 Ϥ m=Q^W1YFqxdqUhB ` V0M#A5&w"m1HZnNmtҀ:<Q QG VW vpUgc?]ZUO`"cE"8!)%) ,*)Q+,2*D*Y,l,X}*0(m&&1%%N"z!9^V =xKZo_jb"#W&GOh*QG?->1/J1"45iK6655 7:t18Ce:tV:tj 8Z{,9Ë755.3O4'+1.)$=zArE$aiu%Lb0,X0RQ1Hm4>93;(Z;?O ?BBB CB/eDO8_2!x}-$?q+Oj-b3O\8T=VK<D?O8D@.D FIINNLMM/0M@NjQNbOrPVM뒬ON;LKOJ!I|F=>z,u1l4!c7`^;UAqPDYFjG>I"1L&NlNQWQVTVrX!/Y0mW?YnPmY`YpYÀZ<WW `W,V0UUiRLv!3@n;e=`DDXHrPKvILAQ8RT.T"KUY Z]^_Y`|`"#b1a>bPCc7_OdGn,b5}b,cEAbb,9_Ombڂ]]ZZYpp@hmBbHmY|N0SRN=MUpDaXC<|Y}2$[P(]Q`^axglljikN"j/k?lMil]mxmxky}mxmlll>j id eiHcNZ\SOUV-NYH@^?_85_-,ai#:cg gi-lrNslq@sN"t0Pr(=wtfMxsZ<t|i-uxt]uubu u/rWrpCnerQ`YY[T`JbCDSc`NJ6WcsqK]܍] 1ɷ,K0)YgS~k=MnGq"?u`:Aw`2Nz*{!}e ,-CS!"N0=J10Vaxqt."RN3҅Sg0W=oO^rJLv0Dz;z6%~.A&cOΈ^ }6ijFse!0K/<IlUsabpY}ĊR`i ήPގ  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~mft2  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  9A<EIsBYK(b%Q[4m`_H`vs8[Z؋gyoU/anQbzu+LVyG|iE=uLCm(BEk6G\;jPJ+lV.Kef:;7N>a`B"hN4g\Hyfska_^mXPpvSķ4~gL1$H*G}oGcxIuJNvBPMsP~)n=:wR:?duB$oLW5seTE>iHhbdvD]~kVԉAXOh{J_G=KtONKQ|T`xUsE6[[8in>#xuFn2!}RLH3tcdI,i|_{lat!'YyP&KLTyyRZӌ5(UX!FY}f \ xH4EJb/1rv 6'?.iLGEM\ct rqhAB]QZNH TKY^\`taAb~ON2|g0)A>2u9)EA8_gVabko aO,T驰ެW섪)_PyJdU_evǔ 3hȏih+lUa.p+7!/`1"O?<|⩐KrZa;{~jGZ)1eD\LjTpi0r N΂rz#snur/>^9-xP)& y*t{50MC"SRS 5nJ}p v+{~2Tx~@ڲ({p|{?^w|sN|*+pA{}c% nZA)Bj:DqE}qSS(fDžܹhүm~Ֆ枻xl5청t[qO٘n=/nk#hM8}h! Pϒ.]1+CXnK\Vw ٛTHi?hÃBdii.3񢜣g:Ee jcnjvc zmOx`yT32FFci\%tC^p:ZN쯩^Ƅ:^XL_S^=^d]q~Y|B\/vx,e5k4E3u?2;N=,IH;IB:o@g,<i84"SnH/aP-WG;?ZeASv}HLbgxI F*ZKnA K=4H;rQB(9<7 @5;%?~d==aO7D[MV+]uZk=z_hHN0Y]rU^gkF1P pJ7rCFUw2C`n6C<i'E~Mi?ўHufiŲI eJgc0(=X@ <,SB@[b G=(!f_T;0d dS2H^cXrkR xLeU}0 GbiE=uFs^J3sCƎMEs3NSElPjx+7ES8sZ:Wk C(oQ<>j`&Sdyy3k,]Zy{[ÚN;fH*G}:Kk}bO-{gR!x TSsPU6n24s yJa6{a8%Uq@&y7Kt:Gv]ZSG*jqp|`XhOc{J_MA3 R!և|U;X~Zt{0['s44 BR_30g35y:#F^8y SPSrl~wf9[ 'OӡvNHU2Õ1XU,]f :^)(` ~Nya%w71 X02p!2;3Na>2NCGhpN@mkYr\3omiEuSzKz {xxx%voTtGvR q7v(~I* Ak# x{$s|ѡ555Db9V?C܃jfqylw橵u)V[ԙR 6hjlP$,7kKo2jh . Sg(I5dԑC~ed)x[T{V1(-f?Q3~Wނ~|ƥ[mY[]w_Je]`^V^jz[Qqf4Z0ޙ'G$A\I}Zwpu@wȊ \ OՏP"φRk]T>›}>SzBtSޱV"oTF/kowR (LB_@Z`lTshp[WyM3{Hs>H0nIPݚKjKMiI'aBK=a10N(eZJcB7+vY Neknn\Z3:cJNZgF:BgCe.^@'TAV>B/U\BҢ RCǴ;)A.kWCu_1hLV\\PM XRvEQUAWU>XK<_E#9N=:>?(*X'rEA(eb&kSG6XXb#Sj 484Q R5Zg@8s@D-GvQC `l=fta+a ~xVƶJM߳>I7އM{ۅ$QFQSe|lUwWrnYMn#591 WP3XoO5e;'L@awQa1afхĀoYʐ0M쪘xM"3pS~ XtS\[2̄b])C']+4vƏ^r#3mvA0w a1;u/[7$G?zZYa onZ} x]&>Oӡv؍U[sܺ0\Q-_,ڜ86BR _=xsb#"UeԿ`󧯧ei8Hmjn*kÆg}[lyk x70M/ hO ) n$ %#a~&6'/KI%XlgnhUgd(o_벯Ws- s4}\tKwtCtq*qq y14S+Z6% x~+>zLx*V[Br^궲qzqxuҘso텮nY| Gk{=6w=&8{cemϒmi8584$Mz6)!~p|QGdnr[XdeaLAeEDd)B[ABo*@P2:`]rg>H:U"N L RD3pSmޖoPv4eULQ/QW!E`s9PI w>IxG@CbC\?ipmF6;@A9'6;+5z12B)K|mhI.[G?$QZ.Kl1F~4"B0iY7q=N\7:M8D6m55^23+-Ǵ0u]uZS*< uR@}KtS$Fa(&xErG+>&*3;1,|8ǧ.6(5/V2,K2+B0 ;.< EED:P-L%@WYz7VqGOBPCTJ?_*FDbg!Ac?SXp@WXؚBZY[DbXDSĸPFS@B0=\479 I->lV$F")ZW6ZlyJSYLжfF9vi>BKgoAH"`Dc,FFboG^8H3[۰IW;n 8~44 BN07_B"~c4R6_eALQXN`Nݵ lW-G o9CjmD*8iֺG1kIgJgc0M`Mc[`8#5?4K1U4gw=?kL(3!OdbbQ[fQNsH8zsEsKu}(HtLs'sO^ptPqiߨPPe\R5`k5(3fC11 \s1 o9qvUG2Um$^@Q`l!S{9H𺂠FIPKNBFQFzpRUt/YUbouUiGjVte53-,LY/ c/z2BP0lxXoP f |rV?{J_NISY]VY@zYr[som[i_k./-P{) nM(,e:i*OM^EmUtz>nYߵ9=*N񲗱ʡV"B[{􍛤&^d1^~'` wQar\&ak ,7(]"6|q  Y0"hؠFHg5e*^ӧegt{i8Hmiitj&?jRn|zj^mv#uiVoz7(ME /h&& Ž9P;#݇Xz-Yyѩu'!M~2iz}Ju}Cݐr zeoRx{|m#vuukXt[og##L~qn]A-'pCMUe|bF‣ b:SQc?Rbܜb`|at`E^n`Qi3FhCN()HmMbofu\ySyNQ {Rܾv+RjJmTpjT/wdT)`sL2wƕ'bHL=en]R2eGiSDQ`Eq_G^л^FƃXdHVHKo3R\lSt^&vGN2]/~g,GO N GRA aR=,IH<\T E<E=E{>>̹5D:--(|$U)hhD(V?T`k8YL]7=E@T?VB5;C 9Y-;4/3",2,rҖM0zob]5@3NZo+H2p/PC83=e3A: iP6H64X4E02`).!,h]P Q~:qIQr"pEfl'E@(t';%)7?)4 `)2fʥ*1k*0'pYlADP/ BG+p>Y%O< N:C9C;G D__=pEÛ=9CM>YB E:= 8~98SFB|QQ*Qip9 KZ:EzDMc?CQ=O;pJI=NM'M?`^M^?MHؼI@G@DCB07#5<5gVNO<JVNc*fTf-;kM|H^FhR@AXP>?TM>S?RBQBdNZCLqoDIB035M)33B1r vT7UZH&Y-bC?bQbNH&)XAU^e?tYUAYҨCU>YzÓE-WGUEZS:>GbJP٤HN;.K.o0 G- [B3bF}'Q\\@LUSJ_&BdA ap*D_\acF_4-H\IXKJTnJO:= ,7+Q)w ea-NmF?$wgW@Y~5WM$g4DDn$FmJplѼ9LTg$Nid O~_CPz[COU]5`-?K#Th_qtS'z}4w{O >pbuFaPsaI#.}EOY}-LRxTqrW3lD>:J?6[_3b47/5>34oj07`64@ԓ~~s;AiT^5MW0(6C~.`. s* A)T'4& #,LB07R8c&1B w: QF=Fj!< +57)1a4Zֆ24{K20o(]a0m*0Y*d1,Lų1X)޾R1U(LB02!6-1; '>2NL>hq!u>.:@56d7!5 14(J2)-53EP1P3U0q4a/Q5R1t3,mG:-x'298SBJ{Cei%@#0g;ۯ87f:4C3c4/23Xޅ56qЄ6677n5x6(V1Kz6e0rE7 ,,m-;267\F{FaH@b)%C5=E<8?Z6v87v;A9t>ɸ:^=:;Qy:q7nr;'5B02Nx$=0(^E`0 NAfM[v+HF=7?òBE:~D=:vEk<"GC_=pE9> D贁?u@ά?h><^>:5:,=}QO)O [8LTS.M~AC>!J<2N?S̩BR۾(C;PZDMFxJ\EGDȚEW@5g,k LI$c(Hl+f$J.(U~umH MIxXUCbǛHkFcbJ`M>\MVәM9dPMMoMe"G-;:`z \=;1'kl.aVN]^?nVNxXvZ&r)pyom*pll9ij i`hf[IeefGA;Y0,Pm(2v)RN]2@B"<*9X-;8:9֣;:Qɛ:X;:;+5 :ș0?@.rT9 27(Aq/g[k,)_'Td&P$E T,wP,zaHm/'/9 )X$ %qY &Qb#ck!!Rx@ * YǦ ~iwT<$V~&,%I8!>_`Vq}t AV  ~k DLB0605 1<@PPW&j (z&%"$M%# =!_""Xɖ!'!.LB05-x=$N),gF+:W((S%'At!*9#;$k$T#S#jL:-)6!'Jr .e-q+(8(.T&jYg'+(o(Mt(൨)6E5(m|"&:00IC 1^\r1)~-Э7 9+6!P)|,B"N־,#S-L#Fh-=!, K,B0-sL+)S77V8Z46x!22.b'/\!(w].&m0+,2,'2+2)A2'>1\#8"' ;XD<1+ C,Pj=T{'K6.3079~t9::y9C#;s 7*9`1v]9$.9,-7LY$k T%C-Iq@3AA=Ig ALEC3H֧C6EADyA3DS-T7&RMy~R?,G{8PBvOZ=i("MoMІ$DQ LO=y5wjxJbr{pKRm]pl@pLljeigE^efeVdbOb`+ IR`^`D0DYn%勠^(1>.woKsk6T\#\S]S#VRPR7KQS}64GQyPAQ s <7h0J昧 Òe}Cͪ/ZBX:`}Jc9zHE5wsHr1p:E, DYawvzו:M w|xD,a2npPLythԧV@je'8Xe0cgvQ b`+ IR_S]Bf\[T=^\ X9oXwUX3 AfPB 5qRfb?UēG MrXHKBK- -O2wc(һ s s u  >="  _|Djr۟'n$ۻtQn; /Ӧ 2F F Q ґ$ 6 #**3 -3`ʻ,a*t*gj. $...<n,G}T+/WAM ]Q"n :IL\"SE|M-^B<4g@/d5ņBSk2!|A',vm?(q=#m; h:c':ttQ؏c`pCbv1o;}#eZgW8l`@b\ΏC]\d?ZX7WU`2>SRu,QO'P;K#-=-Z<נq=LyȒ+D\^Y-M@3DÅ^]4BT=0By+X@s'_@mV%@vg"q>b8C`Czs izx^`AܭL4׈-D+̨,y- ,- B-,{W`~^ë1g>e[(]ʌ; ɰ|P 0ո a-~: ! :^m컻t2AX\/S  r g"~?ǻ<Ò~0gN%'4`[ol-ԽʀR~rs*o_D L20(mt}׹{jVPGJ"a*=o+Q8h06ce < 0mFHmLJbr!om G@dD!+<Z`xaN`jjlzC{= O,mXNq%X{%sGmxNӨ0?iOOis@e`~Y~a4mr'.T[Y9uDylVgO bV>#^OJnC b~i"2:~AkUG9X.CHFJ@u?=ؠLy9<7<.'02r5 u./++L'u'dwf5F=@QZPfAbEmi2h7{&JfGjx dIb÷a>YkPJ Ciξ$$D]Si_Oi~jpRPmy*P% Ps\}<tjb[.VnVLN0z<s`"s~tb0N1̞| Ω0NQ~?mvR|mmlW`A'F!L!Ly:F`K}o` N0.9õa! \~njιv̰G̪r̰?tX`hm,W紭Gi4̃~޺ݓBkN!wbًJzmaۨҶ,',.9lړGVƻiu||N˨<Ax"m"R/ӧ<1Pa0eyNmlrQOG%y5qĨI<^t0tv"QQʇ'=HaYno <r}v(!V1MNgv<Z}vuipCNNA.0@!qQAb<˧m)ymc`μ- 4Yyu/_qdVNI(ހF-Y~r R!#rOgx^f?XgEıS &kI >7601J|W!|)t1Y76 mIi6;:4\h f0,2 ' z* /ݹw &/"^,X$Sa% 7BdrJYR6Z \Z*qI[,] XnVCJ SOC<590ERt\x_@cDisNyu/Rrف qmkG]V^P?ZO^\^wh-doOIu3~6 ӇN0yã6,wmfZtMfoyqhy}ViA^&txMJ+t@?pi0n}lM}vn00]M]7Ve@`2Fmpm>"w}rcUD0DkCj*88`wFl\ԠME<)Ǟ̡?|e݋'2Fē%|ob uTXF=8?K$.  Ր ہ;ks_OF0օ԰"D g G*1m@ N_BpU0<Jr 6:lσ+408IY l%mC< #tnN%,=Q$fvN3m@y}vϻK 4L`bzE:GU@xqN.<L~'BXtQDbsNg`(m_65qVRyqQxVu)r?o]@TK,ʺY|&qIkDmc6]Z?KVI=Q60-ʮ6~kNa3@HUi~KjC>zׂ:+Z Xq.<t|f@I '8c)3. )' [a%&\4$U?"Ϩ < u -+(}4e;N0Ab+J PLsMH{F$CAf#7.'+((8K|(z`z̃:xCvto7dyYhb0Z@jKOpa:9w$} 5pR:\u{Oϝ-'Uzlkis`qPy@TQ1t :Z<%zjeR0cxYÂ?Hm9(Βߚ#N9VOt68K-0\dž<MAN3@<#07`CT8r1-i-=55T-L9ImXjn-~x RXD}v 9~m"03KB=R|dRV}* ̓)}vtQԿLYNs Bwu&u2: xLs3_Vrvwyw}odmK|Ny4~rIn0^kvleDm!o]@Tm6Ou FfU$3a=u^WZqZpX6W^֒YDIuAxZ6,l ZTV5iN&P JqFXAMD۵?1)oigSzdKJq.C*<L1Yn*&er"Evyq\B-!Fzmk0/<vhQwb3O T@d >0iq\RETmby e3.Pmm9$p)-C (:$ec#@;B3 ?yhm'T~0@8D'=B0CAi8K@j_??<ݥ;Jǭ9/%uy+iw7XC>GI11L~SOVYW7WZtU|:U]NTQQMiC2}4m@[N0KOV0:["ai Rdilj6"l`WlwcjjܷHe԰b0lZdoNa*XT_Ce.inv!{}z5zT}t{z-ry^Dv00n!c`_rV0hmIl8um*xz9}M5So\(<<YkPv@|Q1u އ i-t4R%n<ݦL '}TzG80.=m3ONjÂGXY;~0 ->u 8.?@}N0tV]t]o:xx:z^<}wN2 RI~+%vc4q EoZijqmqrEwum~~} (u/n!-qjBd Tg]uf<a``jke[J_Oxvjl^ [t8SLVeQ^QR(NR>OIuvyYZSO0EEAiaiBi?=uD<42#h6qYCu>%-5S@2t`*x&.5!By S_}mxU8W)S 9]"$J0y77vv0c[N1p  u6`Vt7Wy3i[FC(60au~1Yn`Kx27#Q4Qk\m? N ,,O}n \iN< 7) 0Z53! 1u1-W2.0zt,)"C"Z,~qxc)RO3@9)B0=EHIzE.EREr+EDDB0>6ta+da6SAD]I1OT01D~KcW~ͳ>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~mft2!TEF~C&}xK-ޤ|ۯۛ;r آԹв+Gzγ\Q#Uѻɺ,[A֓_gĺY7(2ʹe&ڶ:9 9{إ $hۄSEQo/Bwg튠r )}1Vd~בOLKM3Xft+tJlC%}l/˚#u_jaBXOPGA>->oCPe]kJz#𗔧ATƻS_ږ%|2ZxGlaWNAEk<6C124+$ %1?.M[jz[ۛviTZNCC;f2+'&(|09Q8d4d0/T0 3=JZi0xԈU4ܗϦ%t`a#ZRMEHLC>:{9:;B6L[Mk7zҋ,b ދPˍox;b3b`b]DXqRMeIF8D)E?F6IRX^znu}mO_Ϝ۔X7T|fknjGb^[XUQPQRvTsZd%qƂϡQupLw{to;ida}]].]^_dlwׇæǧNAј堲}Ά~byu pnkGijkm ovydV:˕{UKcY"ftqٓ?Bs5f[Q^H?>7/+,6rDQ_~n}SxsLߜ׻˛\ylq^SrI?_6!-%#0=8KqYhxn:;2|gf!r/ugYM:A7.:%>+9PGyV`euqgC E 敛rbVG;2+ $X  )7EUAdXtIU/XR'Y#qMo_UDU=T631-H*()Y-B7KF5USdt,;n.ná9`so^NPUJE~AY4?z@~CLZiz;3=VRO{dTbb[WkR{NkKJ/KLMU\_n}ξ0$Ok`pnfb^xZWV0VXY^esqƔҢ:ȘFKQ_|S{|~2:*2UYP#4qߙ3ˋDyəΡ3`ç ؓ˪ѭߢ|ї(̛ C$ڿ\^\¼bUΨå򦔩/OɓS_ʏcs o!;ʺ׆ cv,&͓Nȇ §3¼˸Ώ@c=;\QU=)$k? P8w>" I]mTFltt1QYbB>TIY߃^խԴP L߼Z?۞ՠ+͕ɶ#xÂM*yjtl2ˏ6PT0籡GuT0eu>J`T硺k߸ӦߡI(5̺åx+UǔwLMǭոvPӯٿ?,18L5؀} v.؅Ѿa#&zw{sponrgz dXTηݧIJ`^׊pzs|lh4ca-__bp}yՃܻɻ(U^~ume^XnTQxO>PXfTtnўԼ0[~5>#}ri`ZXQ6J!FB@:C/P-\IjJxBU֙b֞Ё1u.h^jUM"D<7Q32:hFTDb#p-O(\AG`_lЉWyl@`GTKB!9.0)%&21>LZi]x}c,۠j ZatfZXLB08k/s&9*7ETds˔=:>pwaSF[:0'bd%3B Q{apG<ō#H2p^NAK5T+)#^m #1@P_pA}K-MܴolZ[]L@4.`)&R#O"#'2AZPapˁ[(Хy@qy\hHIKBo==941-,i-/V6ZC7R3brR줏<ׯM>ܦu`HLMGICD+?wr!zvshrsuxr|Nw4 .v֫hp =U7aΚ+R֒ظt˞BoD{TH+ʸÕ߹=ԏ8Ӻԡ/N :أ<6pbHb.ֺؿ>%w&2 Ƽ͟K'/4 ՀvҴ̏\XSƻ=ľΐ֠]aN3x%($H=6KmyۼcpwIX6gJ {s x_k҃ܦ1LRש|ϫ˙ĪAP"ƏSmL-/q.ԛ;UB˳kЮxGӚϾ3Z"! >lPF'ڢXƤ$g?ʜPiN"rPҸzLqES`NB}|{}ۃΞ0X^đ$֒~xBtpmkk9mw㮒˘ڥjތsƯ!#{}=v,oitdP`l]g[g[t_mkz>jw*XeْҘ+xph^aWZ"TPLFKLBURbqR}כ *76*L.Xwmd[TLE@>[;> KaXnftAO҉Y3{VmcEXwԆKao`wRF>;1( JM%E2A P_owu&Աגm{jt[PL@"4() E A :.=L]%mG}oBc6nWʁszfWHx;/#,6A@ :52o0F.s/26T?"M_o)nKt F_Hx_JNeM_JFZ@=;_9:=I?FZRaqID9Cqj}%dXM\WY}U~QK|HF"EFdGKwOWEesMa`eI@rEdVXCfKa^ XSRGPQTSW\n`kwPƗHӹvۻyhq8u6s)lid`s^]^a2cwh~ls~|ּrkA % '~rzuqRoGjjjnpu{Wa>Pju?M *P̃{yxpy{KĈpt=& "[0֧1k2ӌxDl]P ~oʟ § ZGD×ԔW@f{$G\ޮг˺%ʣEȟY٨25ѩiiwشWttїNfV'ˣstHzîwqHIVK؜9NѲk@{>:kZߗ E ߞQفr%6ύ*+zt ԄFA!ˆ&o<ܰ9cc󬋬c׹̑B .XgV@e)ڳ|.ՂIvDZ0މ wֳτL9X%ɦOz)-zʃO|Ayx w6x~.!-cX%2*bysoilKhg.f ht'|Ǟ$6}BȖ~<xBq0je(_r[3XKV8UZ*ivLB̎" {UkS|skRci[UOAKrGF!EO^blcy/ [ pdT=p|0qg^?UMG@u;e7k58nFkSTaroFd́Zt𽲩uzg\QIA191,p'&'.-5,u$&4FBQe`lp"ՐqjV,Q(ӻwziZLY@/4*"T ~-Mz*RkqdlUu?++l,'" '6&FWhxM}[k8#]q X@/X961,('b&(&).L8fGWiaz%9w8Ľ&uO[F;XEAD=74210248k=Jm?|HWjdsxiNB"{VpA[AwZkP`W/NGN@'83/\-1I>L[kix舶Pi FxhpvaUKB:X1*q$v (c&4CRaqߑ*[óAߎ>zjZM'A7.~&.j<9K]Zk{̌ ԭAgb%ueSE:-$`I ) l'6FZV.fw>_/XTk{yeQN?2)&8 s$3ZCSdtr= ṾL_}+eiPd<7t- Z S")1AQbs؄Թ7\lۦa$|gP;>)#   0E 1#AQbs߄Yj[!(jTR:% "'!0 @RcbdtRШ+,@OmT<\)-H,({$0 }x" %0o@R.cXtDC뺭̲=rxXC6#-8!3u/0+)((,06BR?dug ۻΡIGv^{PAEB?Y:743037<9ATHzUd:vS༮U`zl\^NCNKyFC#@?e@|BHaMS[nhwN2Ѹ="Ĵ՘xYin\]_~]WSPMLGNQUZ`g%o|ڊȜ5ѦH'%̅/w9j^aje`1]ZZa[]bvgnu|S$V̰$ҟg2wsDžx){urol$i`hilpu|c `[^p"a$~sǂ={wvwxzQ~]ߋqo櫟qmLM˸𡢔ڎ{j;rcr0'vd\5;QՙJ唉ŗآX7b+- ݾK&Ox?F;= b9\5| EZJ*`TMfn1ժc` JG-Ц͏ɧǫĞ2uީL*ݻ#~ٿӷRdϚ"ׁ`ïC5pQ l6ۊ Vɟ0Ny<~뎧Is )Kp9/^ @0 dY&|)z{]>YKɶV&Ol Ϫ-2 я}wttplih1irގvH&={Ϛsz_smh'b_Q[XVYFetx֞`ʍxԁV{1skd+^/VRMI FDJZhAvϓ=Xѐm)ܽpјzoep\xU N>GUA<8~575@-O1]l?{(6ʒہPrSdZPG?8i1P+'$&6LDSbr Fv P;yj\2$Q_ӿ; ycFL8( #\5Q 2,8;nK]ngHtcتET=JhO8#!B)/  ):L](n푅ti@LkR9%%@#-m5*?:L]o%<Ȥ"7۰oV@p1%.+Q&" Le #S(-;9LU]o,'ֶ^{eީXr\L=1;5k2:.,T+0,.390@%M^oN\ɬJȜry"imYbK*>FlB>_:868N:?F%L;Smao􂂔-_ܹO@ֆOufXLgCOPIH.E9DzFHNSxZ`h;t焹޹*A ,Et0fZD^]XV!SRS#V$Z`gnx\˙DF̋XGUgPttlhtfEc``Zbcinw7|k , jW̝%/E!w=mxu8rnpZqtMws~A s8v5E*.Ɋ] |ۀ^~`톕qfԈ`Ⱦȭau)őΏ#ʓa:Ϫa.YDQ:|Ɂ :4O3dq¹x%_0̱ؗ+qͭMu㼈lJ7٩'VYWRr߻kwȰ͑6V [=t+n:nN t? G&_d kΔ +VJx*:,ؽD 3!rX%(鴁Įk+I#EȄo9UJ F͞`"֘[?C|0`۷LY*s "4Mjȇխ-B7KsW(~=z(utqqv>Π'ۼˊiOdQَփ$|Dv$qlidL`^`ThxHaC ^AEkD֛÷h_zirl4f_]ZVR NLN]zk\y8YLsȫܚ~etlc\UN~IE @C=;CP_nx}oS%K?κ t|i _VcNWF>9r3/,*F7GUdsio¤ϴ "=fRX|k_\SIp@8i1)$YC.=Lt\`k|of)2LνtdVI&>j4+#n_ @e&6EUev 6칟 'AzovQMj@4*I )$pJ */?O`qa磴e)J,n>_YF76,,!j  *R 1*;K\fm~ զy;Whu-_iJ"7"19#Fi5c()8jHwZj| ¡`Kw`cI4h"/-2 !H&6?GX@izqyUTA.|cyn/#wfM6N   $4F`Whz!h{g*߷iP8"e0  ) "f4hEVViz/>Z|뇜n T>.D <!Bm$3ETVi{l9ÛWZ m̌q[_J:-I1-)&c$"$&+#0]7FpVh{͍iIMacgx4gJVG:0Y9621M0!137-=DLYoiyzUĐהɗ-\sjd UHh=GCx@s>=$>@SDKSQZ]amw}} ה"MgxqcVJUQNL K M[NgSYa gox_Ec6 +qeY`_\Z9ZZ\ahYnw~~ᑮ0?%k;tin ntihhNislqt}h' GV|pMײ師KRA{xpy{yxZwdy8{΄Ǖ%5BЇl&{յ踩8Bz ' .5ukZED]ߌ0VYɛl+צ޳ϻ}ĒU"P1V"㧰ܬJ{TĤ޴4Y avźƆu/s'y1MXx)_{Zm12۪;t D^θɿ3&21^t\F44ϝ~)?NU2Zf߿lڮ,>8x)ùԶ2>/TIm;%!vݭFe-<9-̢K]*o[`śȑш3|z{$|ΈѕrmͮOn4TS{gv qmmjgglz‰rʳ2u\5ŧG|zftnjc_[X4U.T_m|~Rǁ&Jzqkd]XSRNI-ECEWSc+q CU\eRoz?mQd\U*MFA<74#1\9=HbXgurŏM%L zW֎}1nbQX@OGe>70+'," 0?N>^ m\|On_'oـ洕ŤVxӰ rlLXGn9-#%7  )9WJ![%k|=oq{jD3/q\1G6M10%Ge.!  $5}EVKg'xx`쾸\7s\wG3"]$GH%!T2BTdvJGl<Ëu^G1BBm 6A0AFRHdBuJªUMGcPVyaI1l pA 1R# /@!QbtBZ,Wː~dfL 4tCq+cQU';h $.h?Pctچ~T[UjŵhPG7p H rv @q-?Pec9s熀Ϊc̺O)mS=+9Yx(3V,\>Pbt Ιϡdmh]ҋ-pZH8.)),$ 9!V(/;=P4cusٽ󩰎tweUE7@+0.+^'((*/r5 N_,oċ՚X}^YYH=I0' 0# C-(9HYizî`VѮ5޴/n== ;=?MCGOW_hr߃vTTN9wԼQYX|Dm_SrHqLMKKALNSQX^fnx`͌̚hȤȊ^vf{obWs[4Z@ZZ[_ag@oyvXw;P.Ϫܫ;dť7l~rgiAfk{jCmosw`ɏX._mq`?:כۂxo+xwy{~15-A5BߺjCNҏ{Eܓ`9;뉺Ŕ AJ0[@[_N/3֯ir"2wz'ܴw`LϤmv P9&Nm.xجٺ- PqB2҄ϭ˝]z_L9R|ۊvԻ]eǝϿK+{̰زQ""ߟ}9Ny 䬐 I)jO>Ӣ]ŏlBuisđz<Dž':ZԘ0Ůb5c ~{FyGw낍\{1{u'˘<э/p}wtok gOe f u;y˴ۍx?ڑł{vDpke`[XTSqYMgvڅT@a6X1Gܝ(zrlye`ZJSOIEC'@CMp][kjz QY=cP̔PqxEmd]_UNHB<7401BQK`ok*1,x3~Ev#|p`jP^A(2&(&&D&4' ).+/<6 >FTdwe{ɶ}m^9O^Ah5+7D46'46#7_;?rEMqU^l z v㚝{al>]PD>fCDBECGJjObU]gFnyJ+ӒF$̧h]zm@`TUOPStUXM[`WfMn=v}Y";h1qʊ'}pddZ`_6aYdHhlMow*; K\ߙ`2ҶÎئ ZTOKHAN`mz|IʙzEڟͱG c}itkd~]X^RTLNFAW=9^6DT]c&r]աHЗ{v)s2<9|pHf\`UNH8A%:/4/+m'J)9DIhX`gwUf٨Zw ?Oq\9BsXdZ}OF?80*# E0?N^njv,m̼LgtcD[/ObDg9v1)!4A Ov(X8GX3hxԽ߄Ԟm ]sLD`8-$ #.B!40AeQjbEr h=g~iUC4.#r>5E/". *v;Ki\muE۴Źl˴CoiT@-09J> 15) %/6FXizE1UӒfѫC`kU?+aR+9%/ 1CeTewӛ3doΝԅnWA+p7N1*& +< .@R0ctʆ3Ҫ9RrY[=Dj. . ;,>PSbsN۩!؋Lv_H1G  )>,: +=HO-asۗΠ ގ u${dL7$4)%!$ :*r*I` #9,LB*<OR`rܘ߼SSoYF(3! < (;*;M`sCg:P})33uy`M;(s&Ypt-);"M`r]XUL 9&j}6iV[C3&#+ 7&:L_q jĈDلq`PO@1#"p!_#&+\2=K]p.Q`}}mX]N@?2'+,=,.&/26a;{CKVcn˃/i;iݚ}azk\OFBD69:;R=@lBG1LT^]ufo{{ *ylG_6RF@HXILOS"W\e,nxMlA!6Ժľ5{o bWOWY ZO^ainw ͜>>}OӜE"si^_ggkn3sQv~1wZt#Š׃i^r:@~Y3zsyx|~ ʘ#womk9$kR8i)2xK%$O+;^7ȸ:+"`zBuxɘ3J)2|aаΝʼǐƤdF~݊ڶOя#,[ :Gx_ݠI%;̹‹źqs˦tɠ&]Uݤ$@1ˑ¯麥q Ǩb}ږ lH CkۺϽ|Zԟop}yew{ٖt!aJCd86|bQ=~yau0qmghmfcl}'(}]AzĠU'}wS9H5/,;L)Ziy.sٛp("{lwj`_VN]F@953-<')"1 AJQHabp)oѡG3i`2%ӣhxg`RI0?80("T1 yR(8GWgxZ&D϶K$q^OBG<3q*!: kV 0@PaXr&53(TϱZs|hVnE8h1,' e-[UO Z)8Jo[k}ÎԸC͟|gS?.#*#>(%G#3VD;VfPwӉuĭЫO̵.~3h;R>.*RGc".35w.@IQEc,t|˗㩏 wx$jVT>)>'m15D2 8*x;M_pͥʓ[)VnW[A(+=~  +:'D8Jb\n6 UʹhIrK[E.O\;9#CV$7fIzZm7GɶUT wf_I4l 6!"0H%x$6uHEZlA~ǢT;C{dO];j(Z:  &# ;#5=GGZk~Sm1d`!j:jVB/_< 7"3 # 3"[5FYbk}Z@ >Aq]J 7$Qv"Cx!4 FXtkK}%h%⺝yeR?-y 1*B;M\!Q2FXj},ɢ3k#[&кg6>Ԩ8UV(&*գ.hwc߄ӄμhƪ\ Xrvnވҡп̗m&6 &zΨ%6?F &3 q=ĭsծ;B]}ƒK"aBAߓ8Kׂɪ=͒Zۀǜ{ԟOԺ 񜉗d 0zus omqjΜǹǟLv܇{Yupwlnhc_y\uZf.vl6ێf¶ ;aK{u[oijdW^YnTQM$IIXYgvѺA#e`BӑR|Ssme_SYLSMGB>[:6;K[mjygi>8E 7eƿ6 zZne\0VANIA;50+�BWRaoX }fl?cUYTNG?941*$xo(>97HsXgqyWJh-Ara.RLA.80)!e ; .0 AMO`{p߁Ғ1g@χDjDW0G=53,D#. C-C1)Y8IYjz򌯝Ư ;ɁzfYR@2>* * MV3%=J="Y2nBS{dduƆ˜G1f`xzeP= *B FI@#B$( _,^=tN`pugɥ~)}1fQ!(7 <#"'%v#+4yEWi{$D4hEXزamW`B ,ht#2#.=3s1\CTfx@f2f,Ar\F\1= : F- 0 N/ASeXx Ղߥwl`L*8%E/'4BA /!@SOe~wj\Q~+r|vgaR?,) ! 7 3 -@XRdwKVcҖcl\4n ZF4|!5 'T -R?Qdw`^7 R|XuIaN<=**1&,N,D9D},>R8dvɭAӎҋl}3jXWD2! I0 /, &\+>BPc.vrdO15ͬars__Mj;);u.@*%*=-O{b3uW~& N7ʠ{hV=E3{!$ c mIY(:ORa3tdЙlqwӯq¼ѩ5hq`]N?R0A!_ "y&+1DNwU[g+yxͯΘ=yk]OB779;=@!EI\OW`iqNtʶmֳczm<`QT`HCTIBIM$PzVk[Tagr{L$00Ҙ  }pep[/S:Z=[8_VbQfochtrB{,_Ϭ%T,E'9=wn9d}bydf;vp:szMAB6.*^]ߪā|ŔCMx ps$wyNG-iIހ{ ܥœp%@҆ډ>`Wو+lݕӓ-`µUŧjND$A$2Ӏƀ4od~Fܘ}eفy׽¯l  qe+)+)Ju+${zxSut“Hü& cJg_Rp̀{euoli dbOfvmђ|-fpH~yrm6hc:]ZaUR7O9Y+jz)z׳K`\g|ntng/a*\VpQTKhFB>840g,p1JDUcqK^>? ͨ~qg6]UNG@Q93-'")4'L:gJRYQhwLjé=%(җ܉tueYPG?R8:0)"H A 0@ePK`0oπ-RIռK~jk{\NA:1)I! $a)48HY hzTmEdžΐz'fSB5b.% P *<4!w2AQbsc*@k+@-ydWPe=D+ B0!5 ' +Q;hK]=oY{ + >ydO^:'5 r6 V+ %5GXfizڍE I-X|feP;&!E D' n 1}BMTifwn9őʕiT>).U"#1 I,  ,>rPbsۗg]!y ڄnGXSC.,(3*h);M_q֖@%,1s{]I4"+-'!`(b9L^pxr-ԛȥxc~O@@(-=a'8K\p`$ǹ>W Q~j`W C1* .D#S%7J\oQmʹ@ߝȯq^MK8&!11/6%7J\En遟˧˔1Ѷ=yPf SA/  0>GBQ.*$6qI[m!o?ʓK8nY[{I`7%F,'N #E#l5Hx[n7ғ:Zsfķ1_'wdsR@.8 +3=V")5eGZ[m+ޥs=e͒XAmU[I8'<|>/S 2EYmLfShb1ivceUSC27!h Pr#,/>bQmvD'.4|co6^lN@1`#&!#%*R,417?CObu˛lB}';zlb]OB 4*n.14745:DDPMT]i|ȏ`RAku,𗷉8{Dm_RG+;::@ECjFdS^OeT^i"q{ @dڼϒFl*}8pdYNIJM@Q?V9Y`g|pxΡg*[bnvlbZ]la d"hCmzsY{(բNΚl ҃շmuo qUtx:|eG(*N˦a/°3s&c6ns?n~}MԜ͜ZP署Q3:ů(Ayr `ɊDP}H~o1nbߵ֤qF}FhK}{x_^֦ŽSEa{*>ӥn܅Vssk(hXe1p~Dėɦuĥ7,KK75X}~xwrmlhd`#XqWTggԜ {Y`Ѷ܌y}tmke_uYTPKGEPap~ك\Gj~5{qUjce`oYxSMHsBz=852DiVAesב« nR#zlc][UO{HB;5|/*%"l%9tLA[#izש095i4!||k`U NF?S8]1*#`g1APi`qMΐ}$$v'ʵo^SG|?8/(  :>2)n8I*Xh y~1ΜsV8zgoUF;2) $ MW5!\1B@P_aqGd*%ExBd:Q?,.$2 @<^)9JPZl }>ٳ~wbN:'\&Y35 7,$#3DpUfylIоDxcN9%2u>c!  <.?bPrbOsږc?ܑ{fVP;&j6+Q B):L8]o7J{wB@`jET@+( "( &%6|Hc[1l~ڐҵg߉j݄o!YE2( #%%G]";3EiWWj!|5;/ rΠLZt` K8%C0'^+ K2 DUi&{~~ zpfcR?-| (A3 MB 1HBVMh,z÷KټmrZ-G5~#F0!#e 0BTg ydűA%zʲ ubNPA=q+?)  7/BUf>y48?wj}JjWE3"T<! (#@@+ -.ASfy S;ù𥉉s`/Nx<*;C8)7 O-@Sfwӟ-ǐl{iUWE4"#D3B^0 4[c-,h>Rje8x̋ş<Tρ\|r`cN>,>"Ej$w,?[R~ebwŌ>P?{iXH.7%S@ | U 5#^5H[kn Q`¨؅etdcS?C(1"H8 $G+;_N4at;ؙlmÁ⏵~)n^P|A3&!"&'*U/29[?iGVh{m;͜B {n-`^RE:1472:>VBGEMiU_#gsUNξ^q}q1cXMlEH#KNRWw\bkft~6hR udC*ߨp恑v k`+Y\9_zbfkqwʀ"ޥy~G)En‡}tnpdsw{<'.ܻToj?QRGҋ=B?+ձȻЍZ/m5DӓcN(wLțzMSђιI?𥵠76厂Kc\z~IZȗ֑ȋ}-yvt=tsY!qh?-[ȭ֣M_~wrzmfheDa_j`p*}p =Nv6ϵjpexFq'kAe ^%YTQ_LJKO.^l{\ ɵvKR&ڳQ󗮋^vne^WS~L/G=B=97?P]lzdQ 6ۺ HAynd[U@MuGA<61,(`%k5F#UhcRq)}_Ģԙxj&]SVLE> 60+%q,=kRybq&xŽ\lRl_yhZLNE<5.)@"&r (&9HWhx&. YȣJl4ZjL@6/(F _ t0?P`;p$v{ƼDgXy1eeRmA4!)!u2 1'H)~8HYRj zIL\vb{N;* " 6% "2IBSd u^(xw)aMD8%%[<&J$:.b {,B&E`C'&7%HZ'l}Pu,J{fQC<(8ZI>)"g2E[Vh{ymүc/֒EcjVMA/Sv 2.B< ".@RdvšF|ާǬPpS\=H5#E )8B)/ M+E=Oat`ռ͒ zpvbO2 TLHyiX2G7$i |bz)a;-&ŭ8=^3vTnȭ*EMEe=c}yvSst4c\ eBހ^Ҧϸ.6Vm䃔}Dwqlh^ea_aPpB}鋷gЋaTXJwp>ic@]XqSP1LJAP`!n{uг1 6B2 xǤd2uldC\UO{I&C>;$75?O^m+Pf2  / +D2byeR@.X b )J2"4*FXj|+`<}m/ZH36<$1 >% !"!U2F3Wj}Fv(ǛgtbO=,P K$4" ,&2=DVjf|/.y{V|jDX,F4#: 0/!-8 C1D+Wj{bxtEM;s@`2N=,^4'""*$ N0BV,j|XcufgK{HiWFJ5 # &&> VJ0BUiz+yλ9BVr}a,O>~-9$D3!#0;( *)/Eh%ʿ9~uj^1Yg\_bTf2jotRz5bOP֞cL}rUmp9s_vzR~J՟h\Ⱦyɉ7ț*<"[⎼-kK¾HW۵ 8Rڇb'wC?꿭ׯ1ڧ̥2ƚ8oCͺ2~@wp6µΥЗ02^}Lzvtu 1Z wNIøE}-wfqmMie^b_cOrٙNJFn 5Vсx\Fwaoic4]XT"QM-JR`n|[.hc7CH#ukld@\UdO6D.H'!G  !T0?N]/l|Č"9ɸudVJ1?b5u, #Bk o!2'ASazrnI1Ƒ$ ,=RN9_q?nʀ'-XydIP"<(:(").b'9I[nH`|s~iU%Bu/wd  2! %8#4BEWjzը–4uo[tH6${^55;03A\TfGxދ#ȋĞSubO=+ ('N" '->^Pc_uG-ΠϣT|iW3E23(!@:%" 5$ +>2Pau+G_XSpy^RL:v)0%'"3**X+!%(-:M}ashu=HZEm\gKi9)O< :T"Oe`r-&Vn}m]N=!+"%/&)l,.14:JZ-k |UDQʡe׊yjrZtK{:2\5D8 ;>AE=HLVeuʆ٘O ͪnġwhMXKFFHKORVyZ]bhqqׁ<ק ɰҢ w%j^gZF\_cfjnsax~񭛾}'ۜx%|t}qRneptw4z/O:e؞羈xF‚4TLRhֱ֤   & ,4=SO$aBs~՟¹Ѕfs_-L:)]%&.(6Y(:.K^pm ͧ2ZI3xf,T)B10' n4,&'$6;H[m/w9/1m[I]6% C  #5G[l[TiuabP?Y- !-*7; J"H4CG0Xkbmɚm|jXGI5$5(7#  %+!e3FXk}EQ҅%r`O>D-%h >4@ 3EY kW}#׌&̞{HiXUG5$IG/,PW !-4[GQ(l'##Ӻ$DVrKahP?.4 A$P ,D&+yB$M]l}q"9ϱp?_PO>:;>%@kDAFoILPZ-hxxΙ#>|%|m^O&HJMPT$Ѵy!Yߏ19+,5zcvPme_$\ZX#VUTtRuQ/OTza*m{ɖZA…җoxlxnd7[\S*OKSJjGvFE(CA?@DP^kyyo#֠ЫetCg\7RJID>;8H7*6'412f10!0(3@Nw\qjyu"r>˺ŬƘT#tdpV!K @83/f,)([%$V"!0 #~1*?iM\l+|až%c}jXG9/(j#+0/"A1@ O_pȞUwcP6=;+sW O Lw=L4C&4E V_fIw˛F=;ʞuM`L8{% N8N*3"cV! 1@Qxbs=nSv|b_Nr;)Q"c3%%" D=o c-e>?N`Zs;xky{gU-B0 '!A#Z .(~8J[\PnDl>cbvn8\I7&? K<!&JL#5JFXUk3}#PkuWbP?K-4 $.)NWvJ 0D Uohfz>(8g|\iX-E4"b 1= : X .AQe?wȈUٽܖ„Dr9_Mg;*0)6( =->Pmbuכp5급n]IKh:m)fto8(#61,d**=JOxbv.epӨHOxw;ePTPB2l!M41!"6W1H$+>RPbv_⛳o۸FxnG\K:)zp1@Z#77R$J5HZ^lˀnлӫ4!vf$UfD3N"jJ*_ 'b9L_r2*2=mWo^N[=+C *  Em0BTg~z&ҟ˽,dxhdWG6$v;p  +7;L^{pZY ꃶrc RcA0v[ t"%W'x)-/7G&WhzYIn/k-֞6}n\M<*e(+-K0025h8?:b>DSIc"sF#j'Yg1y2iPXI8?59 ;=?CEHMQ_oƲ;Ӗx9ufVEC/F GIMsQSWWY^lK{l2Ρ񟷑 2t cU9PbSnUdW[}^aCdin0x臞Φ G|𺩬)΁SrRd#^`cfimq:u'x}K?]-ֈ`ΝOtqtwz}skYyˮ8chiԙ˹ҞT ^ÍSRݢŧ´uٻyۿ_]ͺ޻L1PМ}LDS#UREѦ?&BԳ.gG}y9$ʘݖ'kG1ʹZߧ-cj3h^th]TPLHEB@@@<M]n1~e7،xdQ@-2Q'd!&a y G "$1[AAQbcs{YֳvߊvRb,O<,W t(*FY&$ > (384GxYj|fIwd:Q@a.%6 6=5Q$E%X4DxV gxe76|j`XF5# 3+%)=>+H 1B:SeewhAӼXJrp_eM<'*043'-)&4'[P m,>PJbsȆO>ràB{yg:TC1 .Z# %#r})o:L _pv̌jnm\sJ!8'&,(!;'8&7H\RnaԔ׭Q3u\cfQ@/H/   :a1$7RHl[sn\ncۙznY[|k*YH>6&J6^!1.#030#L5wGZlΥфs6aP?Y.k |1 -'2Ou"~3FZbl;F֡ &{iXG7$&V<2"@R'q .A'S(gxRޥ˹̧ra^P?. ^#,#[&4DB V0BUoh8{KО{jZgI8P'Q@L > Y-&8KA]pi7ܺQStWcxSWB1` j ;}n!1BTfy8_ԟ|~m\L_#1Ͼ&#u}n_N@?B/DGJLwOMR(VZc$qֲW5vs٩J+[zk[NuLOQT\WWZ]`dhp0~{d+iø1ykii]*[B\M^kad[hkPnqu}B8Q*)k(%wkgjlnr4ux|&,.9@\;,͆i{quwz|_IСX|ٲ^ʖ axEóW~ܬNhjJZLg78ź+ҷRYd#j;ܼ)kG˶hY4q٩QW2D Sթ+- :iqWM <Ӝo@uһQFNi#"E<ڶæУWLKߵϧFe؅ƃ6.~a~~||KziwwtxgwlhϝJmE*SL E~zu6r~omonkkjgggSf%eq}|g@Г< X[z{=rnhea^]\[NY XXV5TUUa0nzc!jӚ5LT7u~kc]=XU!QNM KIHGbEDEaDoDQE]jx.6X֙ϚPdwJgx[ZRMtHC@>\<::|77 54w335AfM[j"y:}̦͋:ms\P"DO=9Y41b.,O))("&&T%$#;%u1j>M\]l'|2|!zJfV"F9O0&)%U! C+$/!0#?QN_pms=xf[TzCp2X$l uxfFX"2CSxe)vۊ(4 yhVKD2"N% 5(*.,F= X+;L^ao׀VT n\J95'|MDUD8 )8~I7Zl}еQDv&Q /@S'ewI։rf QypgV?D3"@*#) l->R#dwsO ro6^HL;* 9%##/]3H V,>xQnclv{c1L҈weTC3&"?3@$F1)2% /"*:L_r ٨ǙzXn ]L;^+ <#S1Ct)&=]\Y&8K^Ir χ;r wfUD4r#Q"N+p$ .@SRex͋z{R;Ӣ0'Ho^NC=,7d L Rj<-'9 J\vn[1΀.7xhXHG7/%Q4c"%9&V'3CU fx˝$7ׁŌӒrb`RdA1 `#l%l(+b-/258C?O{_p~Hx𾤮-|m]RM"O@CEH\KBNDQSYegw+GgҲQLdtoeTHHK6MrOSV*Y\s_c-hto =kܾ yT -IqbV|VY[ ]aIdfj[mrvpǐ A6Ȇ:_Lo(ejcevhk mqtWx|WќNhGԐ"!|V}:rrsvLx|geՆk ͸" { Ȧ+ܿݽmcvӷ򷩶[C|Ͱ ovrf˸E}7~mOqQ#4ܽ|e+Ĭɟ^m=>>0C*O \ix[o8Aο!@rcW$KE.@<@97 4D3N210/}/-2.&-3?jL[{jVy܊wfZ_f}m\JMA61t-I)'%#"@!!&b<;#%/>M\ImW~Nɾo|$k"YI9+"I&%CM 0?Oa>s0|owt ~l[J8'^ /0`Y,`+JB#>4DVUh,zÍ-(wr_N=1+& ,F*348&uO/@Pbs#ygV0D3_!q $.$ I#i <-P>qNk`rbhʶ?5n]7K:)'-$!</&[ ):L^o\MrvdS;A0 k)( E$/x1&8 I] nOhٙڏ}*l ZzIq8r'4+$,C#$*6C%X5H[Smh~΁CsbZQU?/8 .-?E ?O$6EWj}M9 J{j*XHV7'@6/;^C:?%%( 1DLVh| +4/ȕ(rRaQ(@t08+ 0 5&H}$6_H[nx_"Fƌ{kDYI}8)%;V  .M?QdGv7-tdrS0B24 az "#*9J\nXeʯz}l]GL<+[,\G $.&(+.1~27EVgxT)+wۺ`|5wgXQG6o(F*+.r0368;n?ADeQar)y=[ЂsbRCB6-6t8;>CA`DGjJtLOASQ^2mp}g, Ŝ1} nn_ NQBCFH~JMQTrWZ]cajyCJ.7ڷ4yj^ZPVRhSV-Yu\_qb}elikpw+LU x(c%whc_r^acf@izmpswuz~ׅ? LEܳ-6ʓtl8lortw{~^OeWQE*H]2+zzg}~v ҋ]s٤qwɝכJ2i_e:šCl1ժ⺿Ș֪N ԏZEږȝd_]FǛn:e:`ދlڻٴru#ԦW|үч/ց:Juwl"ʉtŎŤŰøL YqkϮVjttγā\h6xծM ­-g:T `Dž@vn\tɡA>kɛwЮRJE-Ї—(gt4aÑď1e=ž=H}ɘ)0-j`}u~|||Mzyw~ywтUl޳'փhubMe})wuspBpnllkjgi&hgi4h;q}OZqе޸W8wrkgca_2]=[s\W[[[WY/W#W*WZaWly&pñՊœrtOib\XSPOMFM[LLCJaJGHGFFFP]j;w҆@ʩotx*j_>SM&HEA9?=;:y98876l766y@MZiKx7u{祻Ѓ!rcpU9Ib>9520.-,**1(''&&%&1=KZj{9a?Ăgp`P?AM4f+%"1eRs4% .J=M]ou§ɔۃq`CO?z.   X:5/ &0S@Qcu KFudS A0 z %^6 ,C5"&$7JGYl~buqF}lZH7&w =* 2 #4DVDhy{=raQP@?E- +#4)=,#!822CZUgwӺ‚}?ziMWF5C$Osd$" # M5 .A5Sf3w?}ܣʟIbp7_2MY<,} > BV7N$D** .l?Rse w4RxyfUD3#+B8 84 {*=Qcv^Pww'fU E4%9r S ) L5#5)G,Yl*~G֢Vő oc^'NB=-a 9 ^ 7$!!/1?QLcBu;߻͑ IxBh4WH>7& W!$o&V(+-.;Ku\:m:oֶı)RrbSQBO1K"$N' )/,4.247O9ADPGJmMjPSWY[aUox{4]?7scTqLMORqU9WZ]a{dzgkp/|b!f3R3qb%XY\^bdhl#nqux~^ȧ"U;׷|ofhLjlorvyyl|ՀtgwXYè0}!uYuNx{|\醱]yI-ޒ`ޭMݳgٔUGUɾD?Uo~蓨_iܲȷU˸٥-a%wn 𦯩ı' ^ΰTe'M'?;޶5ީ'"5MPr8^p ݴ"#KΪm{ !0 (ݾM 6Х?uμŻHp?x}DKCYi;xr滒lvdeWI;23,* 'X$#""0 U\Y "i.I<KLZj|LzDGvf}UEq5(2~Vf{,l1sb$Q@0o P397ZRQ N)<5NyatAQҞ\c{&jtZ?I,9b)g^U & A+3o$4F?X*j}+f*e!ercZSC-3U"w9Q!u$ $h(*1&@Qybtſ5|l\PM.=\,"_$B&*=,/m2Q47#9>+L]mN8[#ڽVKvgWG6<*-$/624;8J:e= ?DF)HKYiy]F( `쁆qob}RKB489;?CB;DGJrMQeTAVZ f3uuI[_xˤ $|fmW_NF_GILOS4UY'Z_ceiΚL3l vqops+ux8{~ d#KԾ$צȈ׬~N>~Fa ZЌujߣөҴ+5kWH(d!Ϟi(ybҷqƥĪ =֠@¥bԯ²ƛӽƢ֩JRaai֭ 4=P_;N}c)>BmxڴIջ՟;8FkјϋLϚЏ߉1oލ9BΝkoƳoOôɿU΅٩0,f7Vƪ͸³Jmjʱ뭣J2G$CST8ζޱ_{РljH\G@TћZ^;Agn͎ARS|/ґIݰ;Y98978977?KY;g]vP,Fe|l^]OD'941/b-Z**{*q)'(='_''(&/J;IwWhx|Ӥ۟'|k[Kw=03%t!j/i4:%Y+7:PJZ=mG}&_On]L;,J 2Us l,=JN`Mr5ʆ dsfbQ@06sm+BM"& Q:K"V2EWjYPь{0jtYPH6&^% (*(~ R0AFT9eqEȰ; p`RO1>.$Q#/*?! O.?QvdkJމxgVFG5%3zA7 "2" +,>iP/a".\zBon^Ma=z-*P 9M&L&,5=p 0CUi0QZwfxV!E5%#Y   ` ^0d);L_jq5~֭g=Z2o^Nd>P. ? "&5F%Wib{Z߮͒)0iwgWVHG8(#y!$8'K)5+._/34BgR'bt/^BmöqGa(QB1%'),.14_7e:vqLҌ=V-+?I\I?7(̕;%L,yτ]%ϐ9P0Ouߊ̅}l탨т!wP~հh}ŖȎR~x|xwnu ts2strrsq^rpqty~olڿ4H犬y_s6o lhTf{dd6dWcqb`makbbbbvacmyA󢋱fĹϪ)uCj=ca_>[WVTTR>R3QRYPQ%PPtQ3OR)R]aiNv-n?ˇOzZk`WDOJHG CuBAAA)Ae@4A0@|@?AXBvLXqfbtt׆ŕn sdYXeJA;8Q632 100~/./00"//1<HuW?euÅ2ZrraR6DE7-O'%#"!  * +I8;HVhy.$asxcQBm2$&b::Spz/C(8I[n$Lwyg!UE4i#v^\"&-'  ,?PcAw`㲗G;~n]Lh;*8 s7>+9/!H-),WJ'8HZݕЩudSB2"^8>"$5-? & 6vGuX2<çO}kkZJu:\)A )$D"T\f%86K]RABIsc?RB%1"^QeO*lw: g M/&A+Sf;V {ksZzJ_:j* T Z oT z'x*T:K]oژȄ>sbSCC3#0a!$;&'{*,C/7cFWuhz#y"V,[{l\YL= - #!5#&}),6/]14A58<4?DSct&m5:ufiVG7-_/1=469=F@gBEyH-KN|R`UoI俙sqaRBW:/:I3N(CN?/3Yx[7c)T܇qj%Lѐuс.ϣP06 ۲Ύva1(?`ٿXX!NHO 4-o۶|<:987q7787778X76={HIU[cMsp`́eOxhY>J=4!.,+'(('<&&&S&&&'$'&,7oETduΗsoR yh4X]G8* } -B<'&57DGV>hui˰d|k[mJ9){  k#R 5'8J}]oǙ*rasP@S/N)$H$ .(g:).AR)FKEyhX;H 7|'/M.I5 ,?Q;]_o_|O?E.@**0Q  5q#,5H]YΒw{gW/F6'aV&Y^ q 3 .@'Qc֋ֳ5oC_Of?/{ 2T(%!"%&,:;&K\nlW$ɉ-xhWH8)!K p#&(+/-0Z15}7:HJWhy/ðp-aQCV3'G)[+&.04<8 :ADGJ"MPR+WwZPbqcrZaGPufX)IACFSIKNRVY]w`cogRkvo~H^Frb]VAP~RTqWY],`dg\k)oqv{J~T؉a♥|oXc^\`c dngfjJn;suUyo|߁Jĉ>ИӶrE܍"8Qp()i̮8a!`rP-jKwWXigj4-SBsqbҧۣΤ~2 U*î¹_ ޔ1ݩoƘs1r,Ö֔O$g͓ .;oZ۴?f.\䉨f'*ʂ܄|Oa[0?ޘ}$z]wdusryoopIpqoppr"rqr|Ӈ۠خ.־䷱ۚ˃yqkig+dbLa/``&_m__^`+`u`a`bkvۃ(H®jzHnd_]WkUSQP{OiO]NOLNONPOPPQZf>s)^.ٱ3_d:s_eXOSHEBAPA @>==>>&>>>>?>1@AIUbpIk "5~Wn_Q|Eb;5!3H00 /{---0-x-Q--/.*.d./8WD PS`qKk=~0n]M>2'#@ |+'&!2yA\QcjƮ1Nq;`O??W/!j d X O _ 6 yWW#3[E'V<κWvxeTD4-$9-1 *" %B\';9(M`TNse~~m\vLT;,I >32'+G(;NG^d򧞖tdSC3$812P j 7"3TEW&tXf|`lb[J;,r ] C K(!g/#?dPb9ٓV8~tKcTD<5%b]7!%&)u*-=/>1AwJxY2hyBpFKu`fW GX9.a0259:;m>ACG7J LN9RWfAuم cnqp`QB<>R@BG"ILfPSvVOY[`bf$smQp Pޟ̡౴JRȏ΂ך?9I ܆ή߷İ&UO}fR (оŭzHiYH8)4 ,jp<$a8L>N4qa9PT@-0! @,2,m t& 9MޜK嚜.yBh|WG8).u W+ Y 6kn?"2fCvUG<=푌p'_O@1"# T "#%() 0?PC`ĻᙻwhgXSI;9*>q!%#&w)m,a.12n5D88>MA\mN fRqa4QC3(w*Y,2.2K4(7[;=@vBEHMILZjz ^gXlkkzj[tL<57:9ܠ9׏$}$Th@i< I#^0/rtn.Ie&41b# ݀~Kח %ԯ2@ܝmlHOچχMɵOMµ]ŗŮƦȠȅ'sX߿~b!ɷZ}wQ/\ӕ471`VZ1,ZGʥ CN G/KȓՎ;ϒ$l2kn#v&]]?p”Ў!؆L$;}~d~#~F 7ʃޕOH|Ҩݜh~xusp(onmmmlnUnooppkpgrFpy~؝OWTf%.{riddb`f^~]]\^[]][]i\C]:_^]^s`Khr};ܢ`Gz4?s4f]UaQQMLL=KKiLFKfJK:KLMGN4M5MV+`mz.UˡC7\{l^ERkHB>>=V;;Q:;;9:O:;;<_;2F=K[/"f~zjZK,;.r#fjMLOrW -?Q?vIIn^hM=.t~ , E y V F V #v%8M4W2ĸQuhd{TGD5&K !3;$ a % &:N#cՍ|l\M =3.J '| po'<< `"Y%V&)+-013@P"`>d}l].Nl>d/"e"}$(h*.033k68;=?BUD5N]mUFǩĥޕ6ufLWH 9/03M59h;t>BhD{GJcMNPTw] kIz7З*؞^p~o`RDC=>B@CjG JLPxU0WY\`b)f^k xh=tYyk\wPJM$NfQ+U W[j_btejl0osvx#N\a2ᄓuh,]3YFZ\^cYeimr%t槎خn鷴lF6..ؘ2= c,c湨%ʧΨӎݥmR]=ulͷ񾕼;cݝUvoڱm*֡٫ޢ$zr e۹q;mLy!H1n(%pEKXG z LL6Km71FY8ߩd"<(%ʗ7&8уз078ζgʿջ$ƶٷܻI~0߾$4g1<څ4ʵSR3ަ,II ;uUڗiUxQ:fp).ݚ $ƞP <Ҡv7ԋׇԆSʅو7߉k()@ٜl'ã zywttrpss{tu^vtuux2w8vwa{ɅA 3kD6 "xTpj_h gwe d"b1abbccc$cJcf#fefjs)~)v3Ry{mc[XUvSRzRAQQPR\QR?RWS_RTTUZSXaXlNx'"qr;eY*OHKD;CBAB A@i?@@@ABBABFLOh[h0gp`RFK[0VDm pm``PBG4)"!A wRPt . #Q/@Sp, :^s|bBR{C,3% {  HaiCd':Nq_4gry5h}XH9*$  | , >v"r).;O@u!þ\LqS`QQB{3W% sd6M !z#q%I'&)*3CmU" %wmyhYuJ;?,d!4#&),e/*1U2[57-9D;BP`siԭ]}qWbSYDi4)){+.147U9=k@mCDGJ.MQ7_mhlr6syj[MG?7 79;?gBEHKwNjRV YCZy]a+m{Z¢ᒐ%teuW^ID(DGmJMuPjSW[]adPgk/mp{;fM}ob2UQSUX[_!bfin/p'sy;{~ږO إ։&zmc`acIeilp|tWxP{ˀ݇kґz\ΊԢJxqnoqsw6z~Z0ey4{$FtAbd}{~,~⁢9![|on㹛H{]I%5Kn6͞Ǣ]'oaQAWƸxHܚʙwО*CۭSm-RûƷ;߹hݶ҇čvF0- 8p|Cҍ9\ϰœ$Wy8,HWHqGnҳ,y5cZ>ā ġYȘ=ؘ9%Bu^cҪԓ]Tc @L ~--AAUUii}}textBEGIN_DATA_FORMAT CMYK_C CMYK_M CMYK_Y CMYK_K LAB_L LAB_A LAB_B END_DATA_FORMAT BEGIN_DATA 0.000 0.000 0.000 0.000 95.000 0.000 -2.000 0.000 10.000 0.000 0.000 90.670 5.900 -3.860 0.000 20.000 0.000 0.000 86.180 12.010 -5.210 0.000 30.000 0.000 0.000 81.390 18.700 -6.190 0.000 40.000 0.000 0.000 76.420 25.780 -6.910 0.000 55.000 0.000 0.000 68.620 37.720 -7.370 0.000 70.000 0.000 0.000 60.840 50.590 -6.740 0.000 85.000 0.000 0.000 53.500 63.840 -5.370 0.000 100.000 0.000 0.000 48.000 74.000 -3.000 10.000 0.000 0.000 0.000 91.480 -2.970 -6.960 10.000 10.000 0.000 0.000 87.170 2.620 -8.140 10.000 20.000 0.000 0.000 82.840 8.510 -9.420 10.000 30.000 0.000 0.000 78.290 15.130 -10.240 10.000 40.000 0.000 0.000 73.360 22.100 -11.010 10.000 55.000 0.000 0.000 65.790 33.970 -11.220 10.000 70.000 0.000 0.000 58.190 46.540 -10.820 10.000 85.000 0.000 0.000 51.080 59.430 -9.480 10.000 100.000 0.000 0.000 45.660 69.640 -7.400 20.000 0.000 0.000 0.000 87.680 -5.780 -11.800 20.000 10.000 0.000 0.000 83.610 -0.610 -12.700 20.000 20.000 0.000 0.000 79.430 5.100 -13.640 20.000 30.000 0.000 0.000 75.090 11.450 -14.350 20.000 40.000 0.000 0.000 70.280 18.360 -14.990 20.000 55.000 0.000 0.000 62.920 29.850 -15.360 20.000 70.000 0.000 0.000 55.500 42.340 -14.950 20.000 85.000 0.000 0.000 48.410 55.130 -13.930 20.000 100.000 0.000 0.000 43.060 65.240 -11.940 30.000 0.000 0.000 0.000 83.890 -9.150 -16.550 30.000 10.000 0.000 0.000 79.900 -3.850 -17.500 30.000 20.000 0.000 0.000 75.820 1.750 -18.240 30.000 30.000 0.000 0.000 71.610 7.760 -18.700 30.000 40.000 0.000 0.000 67.150 14.550 -19.220 30.000 55.000 0.000 0.000 59.970 25.680 -19.350 30.000 70.000 0.000 0.000 52.730 37.950 -19.000 30.000 85.000 0.000 0.000 45.900 50.570 -17.920 30.000 100.000 0.000 0.000 40.610 60.610 -16.250 40.000 0.000 0.000 0.000 79.720 -12.530 -21.750 40.000 10.000 0.000 0.000 76.050 -7.610 -22.370 40.000 20.000 0.000 0.000 72.150 -2.020 -22.710 40.000 30.000 0.000 0.000 68.010 3.800 -23.260 40.000 40.000 0.000 0.000 63.690 10.330 -23.760 40.000 55.000 0.000 0.000 56.770 21.280 -23.660 40.000 70.000 0.000 0.000 49.760 33.370 -23.320 40.000 85.000 0.000 0.000 43.260 45.520 -22.410 40.000 100.000 0.000 0.000 38.010 55.380 -20.940 55.000 0.000 0.000 0.000 73.500 -18.470 -29.250 55.000 10.000 0.000 0.000 69.980 -13.540 -29.790 55.000 20.000 0.000 0.000 66.320 -8.200 -30.200 55.000 30.000 0.000 0.000 62.570 -2.520 -30.250 55.000 40.000 0.000 0.000 58.340 3.880 -30.480 55.000 55.000 0.000 0.000 52.000 14.430 -30.270 55.000 70.000 0.000 0.000 45.460 25.790 -29.880 55.000 85.000 0.000 0.000 39.130 37.650 -29.130 55.000 100.000 0.000 0.000 34.110 47.460 -28.040 70.000 0.000 0.000 0.000 66.860 -24.730 -37.100 70.000 10.000 0.000 0.000 63.700 -19.960 -37.300 70.000 20.000 0.000 0.000 60.390 -14.980 -37.340 70.000 30.000 0.000 0.000 56.810 -9.420 -37.580 70.000 40.000 0.000 0.000 52.940 -3.280 -37.520 70.000 55.000 0.000 0.000 47.070 6.800 -37.290 70.000 70.000 0.000 0.000 40.880 17.880 -36.580 70.000 85.000 0.000 0.000 34.960 29.330 -35.880 70.000 100.000 0.000 0.000 30.360 38.740 -34.920 85.000 0.000 0.000 0.000 60.550 -31.320 -44.340 85.000 10.000 0.000 0.000 57.680 -26.570 -44.250 85.000 20.000 0.000 0.000 54.710 -21.830 -44.150 85.000 30.000 0.000 0.000 51.370 -16.350 -44.060 85.000 40.000 0.000 0.000 47.950 -10.470 -43.790 85.000 55.000 0.000 0.000 42.310 -0.590 -43.410 85.000 70.000 0.000 0.000 36.730 9.830 -42.530 85.000 85.000 0.000 0.000 31.130 20.930 -41.890 85.000 100.000 0.000 0.000 26.780 30.180 -41.200 100.000 0.000 0.000 0.000 55.000 -37.000 -50.000 100.000 10.000 0.000 0.000 52.340 -32.470 -49.780 100.000 20.000 0.000 0.000 49.450 -27.870 -49.460 100.000 30.000 0.000 0.000 46.470 -22.460 -49.160 100.000 40.000 0.000 0.000 43.340 -17.030 -48.620 100.000 55.000 0.000 0.000 38.260 -7.660 -47.840 100.000 70.000 0.000 0.000 33.100 2.710 -47.180 100.000 85.000 0.000 0.000 27.970 13.120 -46.450 100.000 100.000 0.000 0.000 24.000 22.000 -46.000 0.000 0.000 10.000 0.000 94.340 -0.940 5.420 0.000 10.000 10.000 0.000 89.990 4.750 3.710 0.000 20.000 10.000 0.000 85.500 10.900 2.010 0.000 30.000 10.000 0.000 80.840 17.530 0.590 0.000 40.000 10.000 0.000 75.770 24.990 -0.510 0.000 55.000 10.000 0.000 68.110 37.000 -1.480 0.000 70.000 10.000 0.000 60.480 50.030 -1.610 0.000 85.000 10.000 0.000 53.400 63.020 -0.590 0.000 100.000 10.000 0.000 47.890 73.340 1.380 10.000 0.000 10.000 0.000 90.790 -4.150 0.890 10.000 10.000 10.000 0.000 86.560 1.460 -0.670 10.000 20.000 10.000 0.000 82.270 7.560 -2.190 10.000 30.000 10.000 0.000 77.780 14.010 -3.380 10.000 40.000 10.000 0.000 72.770 21.290 -4.480 10.000 55.000 10.000 0.000 65.380 33.140 -5.260 10.000 70.000 10.000 0.000 57.910 45.740 -5.520 10.000 85.000 10.000 0.000 50.910 58.600 -4.760 10.000 100.000 10.000 0.000 45.630 68.890 -2.970 20.000 0.000 10.000 0.000 87.100 -7.210 -4.030 20.000 10.000 10.000 0.000 82.950 -1.740 -5.370 20.000 20.000 10.000 0.000 78.740 4.020 -6.630 20.000 30.000 10.000 0.000 74.440 10.370 -7.650 20.000 40.000 10.000 0.000 69.740 17.540 -8.630 20.000 55.000 10.000 0.000 62.540 28.970 -9.290 20.000 70.000 10.000 0.000 55.220 41.480 -9.710 20.000 85.000 10.000 0.000 48.310 54.210 -8.970 20.000 100.000 10.000 0.000 43.090 64.430 -7.450 30.000 0.000 10.000 0.000 83.190 -10.640 -9.000 30.000 10.000 10.000 0.000 79.350 -5.380 -10.160 30.000 20.000 10.000 0.000 75.280 0.400 -11.160 30.000 30.000 10.000 0.000 71.100 6.510 -12.040 30.000 40.000 10.000 0.000 66.540 13.350 -12.810 30.000 55.000 10.000 0.000 59.570 24.620 -13.480 30.000 70.000 10.000 0.000 52.430 36.950 -13.790 30.000 85.000 10.000 0.000 45.740 49.510 -13.260 30.000 100.000 10.000 0.000 40.570 59.630 -11.880 40.000 0.000 10.000 0.000 79.140 -14.430 -14.100 40.000 10.000 10.000 0.000 75.430 -9.190 -15.070 40.000 20.000 10.000 0.000 71.590 -3.750 -15.830 40.000 30.000 10.000 0.000 67.500 2.360 -16.780 40.000 40.000 10.000 0.000 63.230 8.950 -17.260 40.000 55.000 10.000 0.000 56.330 20.030 -17.940 40.000 70.000 10.000 0.000 49.520 32.110 -18.120 40.000 85.000 10.000 0.000 42.990 44.390 -17.710 40.000 100.000 10.000 0.000 37.940 54.430 -16.590 55.000 0.000 10.000 0.000 72.760 -20.560 -21.890 55.000 10.000 10.000 0.000 69.310 -15.600 -22.650 55.000 20.000 10.000 0.000 65.810 -10.260 -23.280 55.000 30.000 10.000 0.000 62.090 -4.460 -23.850 55.000 40.000 10.000 0.000 57.900 1.990 -24.270 55.000 55.000 10.000 0.000 51.580 12.650 -24.710 55.000 70.000 10.000 0.000 45.130 24.440 -24.790 55.000 85.000 10.000 0.000 38.950 36.370 -24.560 55.000 100.000 10.000 0.000 34.080 46.090 -23.710 70.000 0.000 10.000 0.000 66.170 -27.530 -29.890 70.000 10.000 10.000 0.000 63.080 -22.740 -30.410 70.000 20.000 10.000 0.000 59.840 -17.530 -30.800 70.000 30.000 10.000 0.000 56.320 -11.910 -31.110 70.000 40.000 10.000 0.000 52.460 -5.400 -31.520 70.000 55.000 10.000 0.000 46.600 4.690 -31.670 70.000 70.000 10.000 0.000 40.640 15.900 -31.640 70.000 85.000 10.000 0.000 34.840 27.320 -31.330 70.000 100.000 10.000 0.000 30.280 36.870 -30.740 85.000 0.000 10.000 0.000 59.890 -34.670 -37.130 85.000 10.000 10.000 0.000 57.080 -29.910 -37.380 85.000 20.000 10.000 0.000 54.050 -24.750 -37.620 85.000 30.000 10.000 0.000 50.860 -19.380 -37.770 85.000 40.000 10.000 0.000 47.330 -13.090 -37.970 85.000 55.000 10.000 0.000 41.960 -3.310 -38.010 85.000 70.000 10.000 0.000 36.400 7.590 -37.900 85.000 85.000 10.000 0.000 31.030 18.560 -37.550 85.000 100.000 10.000 0.000 26.680 27.930 -36.990 100.000 0.000 10.000 0.000 54.300 -41.100 -42.910 100.000 10.000 10.000 0.000 51.690 -36.360 -43.130 100.000 20.000 10.000 0.000 48.920 -31.480 -43.000 100.000 30.000 10.000 0.000 46.010 -26.150 -43.090 100.000 40.000 10.000 0.000 42.820 -20.260 -42.960 100.000 55.000 10.000 0.000 37.820 -10.580 -42.610 100.000 70.000 10.000 0.000 32.760 -0.190 -42.550 100.000 85.000 10.000 0.000 27.760 10.330 -42.160 100.000 100.000 10.000 0.000 23.670 19.510 -41.920 0.000 0.000 20.000 0.000 93.620 -1.620 13.270 0.000 10.000 20.000 0.000 89.350 3.860 11.480 0.000 20.000 20.000 0.000 85.000 10.000 9.790 0.000 30.000 20.000 0.000 80.230 16.780 7.940 0.000 40.000 20.000 0.000 75.280 24.360 6.410 0.000 55.000 20.000 0.000 67.740 36.210 4.980 0.000 70.000 20.000 0.000 60.260 49.360 4.260 0.000 85.000 20.000 0.000 53.140 62.250 4.490 0.000 100.000 20.000 0.000 47.790 72.520 5.950 10.000 0.000 20.000 0.000 90.010 -5.130 8.800 10.000 10.000 20.000 0.000 85.830 0.510 6.960 10.000 20.000 20.000 0.000 81.630 6.380 5.380 10.000 30.000 20.000 0.000 77.180 12.960 3.700 10.000 40.000 20.000 0.000 72.250 20.460 2.190 10.000 55.000 20.000 0.000 65.000 32.260 0.870 10.000 70.000 20.000 0.000 57.600 45.010 0.200 10.000 85.000 20.000 0.000 50.670 57.880 0.460 10.000 100.000 20.000 0.000 45.500 68.140 1.680 20.000 0.000 20.000 0.000 86.340 -8.380 4.180 20.000 10.000 20.000 0.000 82.200 -3.090 2.490 20.000 20.000 20.000 0.000 78.140 2.870 0.930 20.000 30.000 20.000 0.000 73.930 9.270 -0.460 20.000 40.000 20.000 0.000 69.140 16.470 -2.010 20.000 55.000 20.000 0.000 61.990 28.140 -3.240 20.000 70.000 20.000 0.000 54.830 40.710 -3.950 20.000 85.000 20.000 0.000 48.220 53.330 -3.740 20.000 100.000 20.000 0.000 43.040 63.530 -2.800 30.000 0.000 20.000 0.000 82.550 -12.120 -0.800 30.000 10.000 20.000 0.000 78.660 -6.790 -2.360 30.000 20.000 20.000 0.000 74.650 -0.920 -3.690 30.000 30.000 20.000 0.000 70.530 5.330 -5.020 30.000 40.000 20.000 0.000 66.030 12.170 -6.330 30.000 55.000 20.000 0.000 59.030 23.670 -7.590 30.000 70.000 20.000 0.000 52.100 35.980 -8.160 30.000 85.000 20.000 0.000 45.550 48.520 -8.100 30.000 100.000 20.000 0.000 40.510 58.630 -7.290 40.000 0.000 20.000 0.000 78.360 -15.990 -6.110 40.000 10.000 20.000 0.000 74.780 -10.850 -7.320 40.000 20.000 20.000 0.000 70.850 -5.180 -8.830 40.000 30.000 20.000 0.000 66.900 1.030 -9.860 40.000 40.000 20.000 0.000 62.690 7.720 -10.840 40.000 55.000 20.000 0.000 55.920 18.790 -11.960 40.000 70.000 20.000 0.000 49.200 31.120 -12.530 40.000 85.000 20.000 0.000 42.900 43.240 -12.610 40.000 100.000 20.000 0.000 37.880 53.280 -12.040 55.000 0.000 20.000 0.000 72.040 -22.640 -14.010 55.000 10.000 20.000 0.000 68.680 -17.640 -15.190 55.000 20.000 20.000 0.000 65.040 -12.120 -16.150 55.000 30.000 20.000 0.000 61.420 -6.310 -17.070 55.000 40.000 20.000 0.000 57.300 0.420 -17.830 55.000 55.000 20.000 0.000 51.050 11.270 -18.820 55.000 70.000 20.000 0.000 44.730 23.060 -19.450 55.000 85.000 20.000 0.000 38.660 34.930 -19.620 55.000 100.000 20.000 0.000 34.000 44.670 -19.200 70.000 0.000 20.000 0.000 65.460 -30.110 -22.170 70.000 10.000 20.000 0.000 62.360 -25.060 -22.990 70.000 20.000 20.000 0.000 59.080 -19.800 -23.850 70.000 30.000 20.000 0.000 55.750 -14.160 -24.390 70.000 40.000 20.000 0.000 51.870 -7.550 -25.230 70.000 55.000 20.000 0.000 46.170 2.700 -25.840 70.000 70.000 20.000 0.000 40.260 14.330 -26.430 70.000 85.000 20.000 0.000 34.580 25.640 -26.530 70.000 100.000 20.000 0.000 30.070 35.130 -26.370 85.000 0.000 20.000 0.000 59.260 -37.930 -29.800 85.000 10.000 20.000 0.000 56.350 -32.900 -30.410 85.000 20.000 20.000 0.000 53.420 -27.620 -30.890 85.000 30.000 20.000 0.000 50.220 -22.070 -31.390 85.000 40.000 20.000 0.000 46.780 -15.730 -31.750 85.000 55.000 20.000 0.000 41.540 -5.910 -32.190 85.000 70.000 20.000 0.000 36.110 5.140 -32.670 85.000 85.000 20.000 0.000 30.780 16.370 -32.820 85.000 100.000 20.000 0.000 26.620 25.460 -32.450 100.000 0.000 20.000 0.000 53.580 -44.930 -35.700 100.000 10.000 20.000 0.000 51.040 -40.180 -35.960 100.000 20.000 20.000 0.000 48.350 -35.210 -36.300 100.000 30.000 20.000 0.000 45.410 -29.780 -36.620 100.000 40.000 20.000 0.000 42.180 -23.560 -36.830 100.000 55.000 20.000 0.000 37.420 -13.880 -37.020 100.000 70.000 20.000 0.000 32.430 -3.200 -37.320 100.000 85.000 20.000 0.000 27.580 7.660 -37.590 100.000 100.000 20.000 0.000 23.610 16.650 -37.530 0.000 0.000 30.000 0.000 92.970 -2.610 22.020 0.000 10.000 30.000 0.000 88.670 3.410 19.570 0.000 20.000 30.000 0.000 84.270 9.490 17.480 0.000 30.000 30.000 0.000 79.640 16.140 15.370 0.000 40.000 30.000 0.000 74.690 23.570 13.520 0.000 55.000 30.000 0.000 67.310 35.810 11.510 0.000 70.000 30.000 0.000 59.850 48.840 10.300 0.000 85.000 30.000 0.000 52.920 61.680 10.140 0.000 100.000 30.000 0.000 47.820 71.890 11.000 10.000 0.000 30.000 0.000 89.340 -6.090 17.230 10.000 10.000 30.000 0.000 85.220 -0.290 15.030 10.000 20.000 30.000 0.000 81.010 5.670 13.050 10.000 30.000 30.000 0.000 76.570 12.150 11.240 10.000 40.000 30.000 0.000 71.760 19.710 9.470 10.000 55.000 30.000 0.000 64.500 31.550 7.400 10.000 70.000 30.000 0.000 57.260 44.330 6.090 10.000 85.000 30.000 0.000 50.490 57.060 5.800 10.000 100.000 30.000 0.000 45.440 67.230 6.630 20.000 0.000 30.000 0.000 85.690 -9.610 12.600 20.000 10.000 30.000 0.000 81.780 -4.010 10.570 20.000 20.000 30.000 0.000 77.700 2.010 8.630 20.000 30.000 30.000 0.000 73.330 8.400 6.940 20.000 40.000 30.000 0.000 68.600 15.570 5.240 20.000 55.000 30.000 0.000 61.650 27.320 3.250 20.000 70.000 30.000 0.000 54.590 39.930 1.910 20.000 85.000 30.000 0.000 47.910 52.530 1.450 20.000 100.000 30.000 0.000 42.920 62.520 2.120 30.000 0.000 30.000 0.000 81.840 -13.400 7.580 30.000 10.000 30.000 0.000 77.900 -7.900 5.720 30.000 20.000 30.000 0.000 74.010 -2.170 4.020 30.000 30.000 30.000 0.000 69.920 4.080 2.410 30.000 40.000 30.000 0.000 65.480 11.220 0.840 30.000 55.000 30.000 0.000 58.620 22.680 -1.100 30.000 70.000 30.000 0.000 51.760 35.130 -2.380 30.000 85.000 30.000 0.000 45.330 47.660 -2.780 30.000 100.000 30.000 0.000 40.430 57.560 -2.340 40.000 0.000 30.000 0.000 77.730 -17.560 2.130 40.000 10.000 30.000 0.000 74.040 -12.120 0.570 40.000 20.000 30.000 0.000 70.270 -6.600 -1.050 40.000 30.000 30.000 0.000 66.280 -0.400 -2.490 40.000 40.000 30.000 0.000 62.130 6.520 -3.810 40.000 55.000 30.000 0.000 55.420 17.830 -5.760 40.000 70.000 30.000 0.000 48.830 29.980 -6.940 40.000 85.000 30.000 0.000 42.610 42.190 -7.540 40.000 100.000 30.000 0.000 37.740 52.070 -7.140 55.000 0.000 30.000 0.000 71.390 -24.580 -5.880 55.000 10.000 30.000 0.000 67.950 -19.300 -7.400 55.000 20.000 30.000 0.000 64.490 -13.710 -8.620 55.000 30.000 30.000 0.000 60.840 -7.900 -9.830 55.000 40.000 30.000 0.000 56.830 -1.200 -10.950 55.000 55.000 30.000 0.000 50.620 9.870 -12.550 55.000 70.000 30.000 0.000 44.500 21.700 -13.810 55.000 85.000 30.000 0.000 38.510 33.580 -14.550 55.000 100.000 30.000 0.000 33.890 43.150 -14.240 70.000 0.000 30.000 0.000 64.710 -32.520 -14.320 70.000 10.000 30.000 0.000 61.700 -27.460 -15.430 70.000 20.000 30.000 0.000 58.560 -22.140 -16.330 70.000 30.000 30.000 0.000 55.180 -16.370 -17.450 70.000 40.000 30.000 0.000 51.370 -9.690 -18.410 70.000 55.000 30.000 0.000 45.650 1.000 -19.850 70.000 70.000 30.000 0.000 39.880 12.430 -20.850 70.000 85.000 30.000 0.000 34.370 23.880 -21.520 70.000 100.000 30.000 0.000 29.990 33.330 -21.510 85.000 0.000 30.000 0.000 58.490 -40.820 -22.050 85.000 10.000 30.000 0.000 55.740 -35.900 -22.890 85.000 20.000 30.000 0.000 52.850 -30.540 -23.570 85.000 30.000 30.000 0.000 49.730 -24.910 -24.470 85.000 40.000 30.000 0.000 46.250 -18.490 -25.230 85.000 55.000 30.000 0.000 41.090 -8.200 -26.320 85.000 70.000 30.000 0.000 35.690 3.020 -27.220 85.000 85.000 30.000 0.000 30.580 14.050 -27.670 85.000 100.000 30.000 0.000 26.430 23.320 -27.980 100.000 0.000 30.000 0.000 52.950 -48.750 -28.150 100.000 10.000 30.000 0.000 50.340 -43.690 -28.710 100.000 20.000 30.000 0.000 47.810 -38.700 -29.220 100.000 30.000 30.000 0.000 44.920 -33.040 -30.000 100.000 40.000 30.000 0.000 41.810 -26.850 -30.350 100.000 55.000 30.000 0.000 37.080 -16.850 -31.140 100.000 70.000 30.000 0.000 32.070 -5.870 -32.070 100.000 85.000 30.000 0.000 27.240 4.920 -32.530 100.000 100.000 30.000 0.000 23.380 14.000 -32.750 0.000 0.000 40.000 0.000 92.190 -3.470 31.150 0.000 10.000 40.000 0.000 87.970 2.690 28.510 0.000 20.000 40.000 0.000 83.600 8.790 25.980 0.000 30.000 40.000 0.000 79.090 15.580 23.690 0.000 40.000 40.000 0.000 74.180 22.920 21.370 0.000 55.000 40.000 0.000 66.930 35.100 18.800 0.000 70.000 40.000 0.000 59.520 48.130 16.620 0.000 85.000 40.000 0.000 52.820 61.030 16.020 0.000 100.000 40.000 0.000 47.740 71.200 16.240 10.000 0.000 40.000 0.000 88.610 -6.760 26.340 10.000 10.000 40.000 0.000 84.570 -1.070 23.940 10.000 20.000 40.000 0.000 80.340 5.100 21.560 10.000 30.000 40.000 0.000 75.970 11.550 19.350 10.000 40.000 40.000 0.000 71.220 18.850 17.250 10.000 55.000 40.000 0.000 63.990 31.020 14.440 10.000 70.000 40.000 0.000 56.860 43.800 12.410 10.000 85.000 40.000 0.000 50.300 56.380 11.540 10.000 100.000 40.000 0.000 45.260 66.420 11.930 20.000 0.000 40.000 0.000 84.920 -10.630 21.540 20.000 10.000 40.000 0.000 80.960 -4.820 19.330 20.000 20.000 40.000 0.000 76.920 0.950 17.020 20.000 30.000 40.000 0.000 72.730 7.520 15.070 20.000 40.000 40.000 0.000 68.120 14.680 12.820 20.000 55.000 40.000 0.000 61.040 26.610 10.260 20.000 70.000 40.000 0.000 54.120 39.220 8.370 20.000 85.000 40.000 0.000 47.690 51.720 7.380 20.000 100.000 40.000 0.000 42.920 61.600 7.580 30.000 0.000 40.000 0.000 81.050 -14.450 16.510 30.000 10.000 40.000 0.000 77.270 -9.010 14.280 30.000 20.000 40.000 0.000 73.240 -3.020 12.220 30.000 30.000 40.000 0.000 69.230 3.210 10.350 30.000 40.000 40.000 0.000 64.820 10.320 8.420 30.000 55.000 40.000 0.000 58.080 21.900 5.760 30.000 70.000 40.000 0.000 51.410 34.250 3.930 30.000 85.000 40.000 0.000 45.100 46.780 2.960 30.000 100.000 40.000 0.000 40.360 56.560 3.030 40.000 0.000 40.000 0.000 76.970 -19.060 11.030 40.000 10.000 40.000 0.000 73.310 -13.530 8.990 40.000 20.000 40.000 0.000 69.580 -7.590 7.310 40.000 30.000 40.000 0.000 65.720 -1.590 5.360 40.000 40.000 40.000 0.000 61.530 5.420 3.750 40.000 55.000 40.000 0.000 54.980 16.700 1.200 40.000 70.000 40.000 0.000 48.420 29.080 -0.840 40.000 85.000 40.000 0.000 42.310 41.310 -1.780 40.000 100.000 40.000 0.000 37.620 51.130 -1.820 55.000 0.000 40.000 0.000 70.610 -26.190 2.560 55.000 10.000 40.000 0.000 67.250 -20.930 0.940 55.000 20.000 40.000 0.000 63.830 -15.280 -0.470 55.000 30.000 40.000 0.000 60.220 -9.200 -2.180 55.000 40.000 40.000 0.000 56.240 -2.420 -3.680 55.000 55.000 40.000 0.000 50.190 8.660 -5.760 55.000 70.000 40.000 0.000 44.070 20.540 -7.620 55.000 85.000 40.000 0.000 38.290 32.380 -8.940 55.000 100.000 40.000 0.000 33.610 41.940 -9.300 70.000 0.000 40.000 0.000 64.060 -34.810 -6.050 70.000 10.000 40.000 0.000 61.010 -29.500 -7.230 70.000 20.000 40.000 0.000 57.830 -24.130 -8.660 70.000 30.000 40.000 0.000 54.560 -18.280 -9.930 70.000 40.000 40.000 0.000 50.960 -11.550 -11.330 70.000 55.000 40.000 0.000 45.200 -0.750 -13.200 70.000 70.000 40.000 0.000 39.510 10.780 -14.960 70.000 85.000 40.000 0.000 34.060 22.310 -16.070 70.000 100.000 40.000 0.000 29.710 31.890 -16.790 85.000 0.000 40.000 0.000 57.820 -43.620 -13.830 85.000 10.000 40.000 0.000 55.060 -38.570 -14.930 85.000 20.000 40.000 0.000 52.300 -33.340 -15.830 85.000 30.000 40.000 0.000 49.170 -27.390 -17.020 85.000 40.000 40.000 0.000 45.820 -21.080 -18.190 85.000 55.000 40.000 0.000 40.600 -10.430 -19.950 85.000 70.000 40.000 0.000 35.330 0.870 -21.370 85.000 85.000 40.000 0.000 30.300 12.210 -22.430 85.000 100.000 40.000 0.000 26.300 21.350 -23.040 100.000 0.000 40.000 0.000 52.330 -52.310 -20.150 100.000 10.000 40.000 0.000 49.770 -47.080 -20.970 100.000 20.000 40.000 0.000 47.180 -42.090 -21.880 100.000 30.000 40.000 0.000 44.360 -36.330 -22.780 100.000 40.000 40.000 0.000 41.230 -29.870 -23.690 100.000 55.000 40.000 0.000 36.640 -19.850 -24.930 100.000 70.000 40.000 0.000 31.800 -8.730 -26.200 100.000 85.000 40.000 0.000 26.980 2.420 -27.290 100.000 100.000 40.000 0.000 23.190 11.420 -28.010 0.000 0.000 55.000 0.000 91.230 -4.260 46.110 0.000 10.000 55.000 0.000 87.070 1.650 42.870 0.000 20.000 55.000 0.000 82.830 8.000 39.840 0.000 30.000 55.000 0.000 78.270 14.700 36.780 0.000 40.000 55.000 0.000 73.340 22.340 33.920 0.000 55.000 55.000 0.000 66.180 34.450 29.890 0.000 70.000 55.000 0.000 59.040 47.460 26.930 0.000 85.000 55.000 0.000 52.490 60.200 24.780 0.000 100.000 55.000 0.000 47.540 70.120 24.290 10.000 0.000 55.000 0.000 87.700 -7.850 41.220 10.000 10.000 55.000 0.000 83.590 -1.960 38.190 10.000 20.000 55.000 0.000 79.370 4.120 35.100 10.000 30.000 55.000 0.000 75.030 10.780 32.310 10.000 40.000 55.000 0.000 70.390 18.120 29.430 10.000 55.000 55.000 0.000 63.360 30.380 25.630 10.000 70.000 55.000 0.000 56.430 43.000 22.580 10.000 85.000 55.000 0.000 50.010 55.630 20.710 10.000 100.000 55.000 0.000 45.210 65.410 20.040 20.000 0.000 55.000 0.000 83.930 -11.510 35.960 20.000 10.000 55.000 0.000 80.010 -6.060 33.100 20.000 20.000 55.000 0.000 75.980 -0.100 30.420 20.000 30.000 55.000 0.000 71.800 6.670 27.750 20.000 40.000 55.000 0.000 67.290 13.800 25.050 20.000 55.000 55.000 0.000 60.370 25.770 21.150 20.000 70.000 55.000 0.000 53.680 38.440 18.350 20.000 85.000 55.000 0.000 47.430 50.640 16.250 20.000 100.000 55.000 0.000 42.700 60.600 15.500 30.000 0.000 55.000 0.000 80.070 -15.830 30.660 30.000 10.000 55.000 0.000 76.240 -10.210 27.990 30.000 20.000 55.000 0.000 72.410 -4.240 25.360 30.000 30.000 55.000 0.000 68.420 1.990 22.880 30.000 40.000 55.000 0.000 64.040 9.330 20.260 30.000 55.000 55.000 0.000 57.440 20.800 16.690 30.000 70.000 55.000 0.000 50.900 33.160 13.760 30.000 85.000 55.000 0.000 44.780 45.500 11.490 30.000 100.000 55.000 0.000 40.130 55.450 10.800 40.000 0.000 55.000 0.000 75.930 -20.310 24.880 40.000 10.000 55.000 0.000 72.320 -14.860 22.510 40.000 20.000 55.000 0.000 68.600 -9.170 19.950 40.000 30.000 55.000 0.000 64.830 -2.940 17.740 40.000 40.000 55.000 0.000 60.550 4.170 15.120 40.000 55.000 55.000 0.000 54.230 15.560 11.700 40.000 70.000 55.000 0.000 47.950 27.680 8.920 40.000 85.000 55.000 0.000 42.040 39.980 6.890 40.000 100.000 55.000 0.000 37.400 49.820 5.840 55.000 0.000 55.000 0.000 69.670 -28.440 16.250 55.000 10.000 55.000 0.000 66.290 -23.010 13.920 55.000 20.000 55.000 0.000 62.830 -17.380 11.880 55.000 30.000 55.000 0.000 59.260 -11.190 9.580 55.000 40.000 55.000 0.000 55.420 -4.480 7.380 55.000 55.000 55.000 0.000 49.440 6.830 4.420 55.000 70.000 55.000 0.000 43.470 18.710 1.700 55.000 85.000 55.000 0.000 37.760 30.740 -0.540 55.000 100.000 55.000 0.000 33.520 40.290 -1.540 70.000 0.000 55.000 0.000 63.050 -37.390 7.080 70.000 10.000 55.000 0.000 60.160 -32.250 5.280 70.000 20.000 55.000 0.000 56.910 -26.720 3.420 70.000 30.000 55.000 0.000 53.680 -20.600 1.490 70.000 40.000 55.000 0.000 50.070 -13.940 -0.490 70.000 55.000 55.000 0.000 44.710 -3.140 -3.200 70.000 70.000 55.000 0.000 39.050 8.450 -5.690 70.000 85.000 55.000 0.000 33.720 20.060 -7.930 70.000 100.000 55.000 0.000 29.570 29.680 -8.990 85.000 0.000 55.000 0.000 56.900 -47.300 -1.050 85.000 10.000 55.000 0.000 54.240 -42.190 -2.790 85.000 20.000 55.000 0.000 51.380 -36.550 -4.370 85.000 30.000 55.000 0.000 48.410 -30.720 -5.990 85.000 40.000 55.000 0.000 45.080 -23.990 -7.680 85.000 55.000 55.000 0.000 40.020 -13.370 -10.210 85.000 70.000 55.000 0.000 34.990 -2.160 -12.340 85.000 85.000 55.000 0.000 29.910 9.210 -14.350 85.000 100.000 55.000 0.000 25.960 18.540 -15.620 100.000 0.000 55.000 0.000 51.530 -56.830 -7.940 100.000 10.000 55.000 0.000 48.970 -51.490 -9.310 100.000 20.000 55.000 0.000 46.420 -46.390 -10.590 100.000 30.000 55.000 0.000 43.650 -40.300 -11.950 100.000 40.000 55.000 0.000 40.760 -33.990 -13.310 100.000 55.000 55.000 0.000 36.100 -23.620 -15.580 100.000 70.000 55.000 0.000 31.270 -12.360 -17.690 100.000 85.000 55.000 0.000 26.730 -1.380 -19.500 100.000 100.000 55.000 0.000 23.070 7.960 -20.680 0.000 0.000 70.000 0.000 90.340 -4.700 62.560 0.000 10.000 70.000 0.000 86.130 1.430 58.780 0.000 20.000 70.000 0.000 81.780 7.600 54.800 0.000 30.000 70.000 0.000 77.380 14.560 51.200 0.000 40.000 70.000 0.000 72.620 21.850 47.730 0.000 55.000 70.000 0.000 65.450 34.170 42.520 0.000 70.000 70.000 0.000 58.500 47.100 37.880 0.000 85.000 70.000 0.000 52.220 59.700 34.610 0.000 100.000 70.000 0.000 47.330 69.290 32.940 10.000 0.000 70.000 0.000 86.730 -8.580 57.340 10.000 10.000 70.000 0.000 82.620 -2.590 53.550 10.000 20.000 70.000 0.000 78.480 3.700 49.980 10.000 30.000 70.000 0.000 74.200 10.260 46.380 10.000 40.000 70.000 0.000 69.500 17.750 42.830 10.000 55.000 70.000 0.000 62.590 29.850 37.900 10.000 70.000 70.000 0.000 55.880 42.600 33.650 10.000 85.000 70.000 0.000 49.640 54.840 30.200 10.000 100.000 70.000 0.000 44.960 64.600 28.540 20.000 0.000 70.000 0.000 83.030 -12.430 51.970 20.000 10.000 70.000 0.000 79.120 -6.700 48.450 20.000 20.000 70.000 0.000 74.990 -0.630 44.870 20.000 30.000 70.000 0.000 70.940 5.980 41.680 20.000 40.000 70.000 0.000 66.380 13.290 38.080 20.000 55.000 70.000 0.000 59.640 25.200 33.320 20.000 70.000 70.000 0.000 53.080 37.720 28.910 20.000 85.000 70.000 0.000 47.050 50.000 25.720 20.000 100.000 70.000 0.000 42.410 59.460 23.780 30.000 0.000 70.000 0.000 79.010 -16.810 46.000 30.000 10.000 70.000 0.000 75.340 -11.080 42.830 30.000 20.000 70.000 0.000 71.490 -5.120 39.740 30.000 30.000 70.000 0.000 67.530 1.150 36.420 30.000 40.000 70.000 0.000 63.210 8.340 33.220 30.000 55.000 70.000 0.000 56.740 20.210 28.500 30.000 70.000 70.000 0.000 50.320 32.410 24.250 30.000 85.000 70.000 0.000 44.340 44.630 20.740 30.000 100.000 70.000 0.000 39.970 54.190 19.100 40.000 0.000 70.000 0.000 74.940 -21.520 40.130 40.000 10.000 70.000 0.000 71.340 -16.060 36.920 40.000 20.000 70.000 0.000 67.730 -10.180 33.940 40.000 30.000 70.000 0.000 63.860 -3.890 31.000 40.000 40.000 70.000 0.000 59.860 3.110 28.060 40.000 55.000 70.000 0.000 53.580 14.520 23.300 40.000 70.000 70.000 0.000 47.360 26.910 19.280 40.000 85.000 70.000 0.000 41.530 38.940 15.750 40.000 100.000 70.000 0.000 37.240 48.480 14.050 55.000 0.000 70.000 0.000 68.630 -29.860 30.680 55.000 10.000 70.000 0.000 65.390 -24.440 27.950 55.000 20.000 70.000 0.000 62.020 -18.950 25.240 55.000 30.000 70.000 0.000 58.420 -12.660 22.440 55.000 40.000 70.000 0.000 54.560 -5.720 19.750 55.000 55.000 70.000 0.000 48.760 5.470 15.450 55.000 70.000 70.000 0.000 42.930 17.380 11.630 55.000 85.000 70.000 0.000 37.430 29.200 8.250 55.000 100.000 70.000 0.000 33.370 38.580 6.430 70.000 0.000 70.000 0.000 62.130 -39.820 21.010 70.000 10.000 70.000 0.000 59.220 -34.330 18.750 70.000 20.000 70.000 0.000 56.090 -28.840 16.230 70.000 30.000 70.000 0.000 52.900 -22.810 13.770 70.000 40.000 70.000 0.000 49.300 -15.930 11.220 70.000 55.000 70.000 0.000 43.990 -5.120 7.570 70.000 70.000 70.000 0.000 38.530 6.580 3.870 70.000 85.000 70.000 0.000 33.350 18.230 0.730 70.000 100.000 70.000 0.000 29.390 27.690 -1.300 85.000 0.000 70.000 0.000 56.050 -50.280 12.360 85.000 10.000 70.000 0.000 53.390 -44.940 10.140 85.000 20.000 70.000 0.000 50.620 -39.610 8.050 85.000 30.000 70.000 0.000 47.630 -33.400 5.920 85.000 40.000 70.000 0.000 44.360 -26.800 3.500 85.000 55.000 70.000 0.000 39.430 -16.030 0.210 85.000 70.000 70.000 0.000 34.390 -4.540 -3.050 85.000 85.000 70.000 0.000 29.610 6.800 -6.030 85.000 100.000 70.000 0.000 25.890 16.160 -8.050 100.000 0.000 70.000 0.000 50.690 -60.630 4.810 100.000 10.000 70.000 0.000 48.350 -55.460 3.110 100.000 20.000 70.000 0.000 45.740 -49.840 1.350 100.000 30.000 70.000 0.000 43.030 -43.910 -0.570 100.000 40.000 70.000 0.000 40.120 -37.560 -2.540 100.000 55.000 70.000 0.000 35.610 -26.720 -5.500 100.000 70.000 70.000 0.000 31.050 -15.650 -8.470 100.000 85.000 70.000 0.000 26.510 -4.520 -11.260 100.000 100.000 70.000 0.000 22.980 4.680 -13.090 0.000 0.000 85.000 0.000 89.520 -4.950 78.840 0.000 10.000 85.000 0.000 85.350 1.220 74.220 0.000 20.000 85.000 0.000 81.060 7.320 69.760 0.000 30.000 85.000 0.000 76.620 14.450 65.500 0.000 40.000 85.000 0.000 71.960 21.990 61.310 0.000 55.000 85.000 0.000 64.740 34.210 54.640 0.000 70.000 85.000 0.000 57.900 46.980 48.820 0.000 85.000 85.000 0.000 51.830 59.240 44.250 0.000 100.000 85.000 0.000 47.210 68.550 41.210 10.000 0.000 85.000 0.000 85.860 -8.630 73.170 10.000 10.000 85.000 0.000 81.820 -2.840 68.800 10.000 20.000 85.000 0.000 77.680 3.440 64.730 10.000 30.000 85.000 0.000 73.370 10.110 60.400 10.000 40.000 85.000 0.000 68.790 17.580 56.130 10.000 55.000 85.000 0.000 61.980 29.750 50.090 10.000 70.000 85.000 0.000 55.380 42.250 44.450 10.000 85.000 85.000 0.000 49.250 54.360 39.420 10.000 100.000 85.000 0.000 44.770 63.850 36.540 20.000 0.000 85.000 0.000 82.140 -12.650 67.500 20.000 10.000 85.000 0.000 78.310 -7.100 63.280 20.000 20.000 85.000 0.000 74.200 -1.000 59.180 20.000 30.000 85.000 0.000 70.110 5.710 55.390 20.000 40.000 85.000 0.000 65.770 12.950 51.270 20.000 55.000 85.000 0.000 59.020 24.840 45.030 20.000 70.000 85.000 0.000 52.580 37.390 39.640 20.000 85.000 85.000 0.000 46.690 49.420 34.740 20.000 100.000 85.000 0.000 42.290 58.680 31.880 30.000 0.000 85.000 0.000 78.320 -17.340 61.370 30.000 10.000 85.000 0.000 74.520 -11.690 57.470 30.000 20.000 85.000 0.000 70.740 -5.750 53.730 30.000 30.000 85.000 0.000 66.740 0.710 49.830 30.000 40.000 85.000 0.000 62.480 8.070 45.780 30.000 55.000 85.000 0.000 56.120 19.670 40.130 30.000 70.000 85.000 0.000 49.790 31.970 34.580 30.000 85.000 85.000 0.000 44.000 43.890 29.710 30.000 100.000 85.000 0.000 39.690 53.350 27.000 40.000 0.000 85.000 0.000 74.160 -22.230 54.740 40.000 10.000 85.000 0.000 70.590 -16.710 51.130 40.000 20.000 85.000 0.000 66.910 -10.950 47.500 40.000 30.000 85.000 0.000 63.130 -4.460 43.880 40.000 40.000 85.000 0.000 59.090 2.370 40.060 40.000 55.000 85.000 0.000 52.900 14.090 34.720 40.000 70.000 85.000 0.000 46.800 26.100 29.120 40.000 85.000 85.000 0.000 41.270 38.030 24.590 40.000 100.000 85.000 0.000 37.030 47.380 21.830 55.000 0.000 85.000 0.000 67.860 -30.890 44.980 55.000 10.000 85.000 0.000 64.590 -25.480 41.710 55.000 20.000 85.000 0.000 61.280 -19.690 38.520 55.000 30.000 85.000 0.000 57.760 -13.440 34.990 55.000 40.000 85.000 0.000 53.940 -6.740 31.350 55.000 55.000 85.000 0.000 48.150 4.560 26.160 55.000 70.000 85.000 0.000 42.510 16.450 21.200 55.000 85.000 85.000 0.000 37.120 28.020 16.890 55.000 100.000 85.000 0.000 33.100 37.360 13.970 70.000 0.000 85.000 0.000 61.490 -41.410 34.480 70.000 10.000 85.000 0.000 58.480 -35.840 31.600 70.000 20.000 85.000 0.000 55.520 -30.230 28.790 70.000 30.000 85.000 0.000 52.260 -24.190 25.780 70.000 40.000 85.000 0.000 48.740 -17.120 22.530 70.000 55.000 85.000 0.000 43.400 -6.340 17.720 70.000 70.000 85.000 0.000 38.150 5.160 13.110 70.000 85.000 85.000 0.000 33.140 16.780 9.020 70.000 100.000 85.000 0.000 29.140 26.000 5.930 85.000 0.000 85.000 0.000 55.460 -52.270 25.010 85.000 10.000 85.000 0.000 52.740 -47.060 22.350 85.000 20.000 85.000 0.000 49.980 -41.460 19.780 85.000 30.000 85.000 0.000 47.030 -35.450 17.070 85.000 40.000 85.000 0.000 43.850 -28.690 14.240 85.000 55.000 85.000 0.000 39.050 -18.050 10.140 85.000 70.000 85.000 0.000 34.080 -6.490 5.810 85.000 85.000 85.000 0.000 29.420 4.970 1.970 85.000 100.000 85.000 0.000 25.870 14.140 -0.770 100.000 0.000 85.000 0.000 50.200 -63.380 16.970 100.000 10.000 85.000 0.000 47.830 -58.350 14.830 100.000 20.000 85.000 0.000 45.380 -52.790 12.670 100.000 30.000 85.000 0.000 42.630 -46.750 10.230 100.000 40.000 85.000 0.000 39.700 -39.860 7.770 100.000 55.000 85.000 0.000 35.250 -29.210 3.950 100.000 70.000 85.000 0.000 30.720 -18.140 0.050 100.000 85.000 85.000 0.000 26.290 -6.810 -3.410 100.000 100.000 85.000 0.000 22.870 1.890 -6.010 0.000 0.000 100.000 0.000 89.000 -5.000 93.000 0.000 10.000 100.000 0.000 84.880 1.060 87.950 0.000 20.000 100.000 0.000 80.550 7.390 83.190 0.000 30.000 100.000 0.000 76.070 14.360 78.260 0.000 40.000 100.000 0.000 71.240 22.150 73.080 0.000 55.000 100.000 0.000 64.260 34.040 65.750 0.000 70.000 100.000 0.000 57.580 46.860 58.770 0.000 85.000 100.000 0.000 51.430 58.820 52.280 0.000 100.000 100.000 0.000 47.000 68.000 48.000 10.000 0.000 100.000 0.000 85.310 -9.070 86.870 10.000 10.000 100.000 0.000 81.320 -3.070 82.310 10.000 20.000 100.000 0.000 77.040 3.190 77.560 10.000 30.000 100.000 0.000 72.790 9.840 72.810 10.000 40.000 100.000 0.000 68.220 17.380 67.820 10.000 55.000 100.000 0.000 61.530 29.490 60.570 10.000 70.000 100.000 0.000 54.820 42.010 53.650 10.000 85.000 100.000 0.000 48.910 53.870 47.760 10.000 100.000 100.000 0.000 44.600 63.230 43.520 20.000 0.000 100.000 0.000 81.540 -12.900 81.010 20.000 10.000 100.000 0.000 77.650 -7.410 76.200 20.000 20.000 100.000 0.000 73.720 -1.210 71.920 20.000 30.000 100.000 0.000 69.510 5.260 67.260 20.000 40.000 100.000 0.000 65.090 12.660 62.450 20.000 55.000 100.000 0.000 58.630 24.580 55.460 20.000 70.000 100.000 0.000 52.160 37.070 48.660 20.000 85.000 100.000 0.000 46.310 48.980 42.950 20.000 100.000 100.000 0.000 42.060 58.110 38.620 30.000 0.000 100.000 0.000 77.720 -17.640 74.540 30.000 10.000 100.000 0.000 73.940 -12.140 70.000 30.000 20.000 100.000 0.000 70.110 -5.910 65.720 30.000 30.000 100.000 0.000 66.120 0.430 61.310 30.000 40.000 100.000 0.000 61.820 7.720 56.800 30.000 55.000 100.000 0.000 55.610 19.280 49.960 30.000 70.000 100.000 0.000 49.350 31.410 43.460 30.000 85.000 100.000 0.000 43.720 43.340 37.590 30.000 100.000 100.000 0.000 39.400 52.730 33.530 40.000 0.000 100.000 0.000 73.660 -22.810 67.570 40.000 10.000 100.000 0.000 70.020 -17.370 63.530 40.000 20.000 100.000 0.000 66.360 -11.510 59.430 40.000 30.000 100.000 0.000 62.510 -4.940 55.090 40.000 40.000 100.000 0.000 58.560 2.110 50.910 40.000 55.000 100.000 0.000 52.380 13.400 44.310 40.000 70.000 100.000 0.000 46.320 25.560 37.920 40.000 85.000 100.000 0.000 40.860 37.280 32.120 40.000 100.000 100.000 0.000 36.850 46.450 28.210 55.000 0.000 100.000 0.000 67.350 -31.810 56.890 55.000 10.000 100.000 0.000 64.130 -26.150 53.430 55.000 20.000 100.000 0.000 60.740 -20.520 49.470 55.000 30.000 100.000 0.000 57.270 -14.160 45.660 55.000 40.000 100.000 0.000 53.340 -7.180 41.460 55.000 55.000 100.000 0.000 47.650 3.950 35.350 55.000 70.000 100.000 0.000 42.100 15.500 29.430 55.000 85.000 100.000 0.000 36.810 27.160 24.060 55.000 100.000 100.000 0.000 32.880 36.380 20.250 70.000 0.000 100.000 0.000 61.000 -42.220 46.000 70.000 10.000 100.000 0.000 57.990 -36.660 42.790 70.000 20.000 100.000 0.000 54.980 -31.130 39.330 70.000 30.000 100.000 0.000 51.800 -25.030 35.850 70.000 40.000 100.000 0.000 48.290 -18.170 32.240 70.000 55.000 100.000 0.000 43.040 -7.340 26.490 70.000 70.000 100.000 0.000 37.830 4.120 21.010 70.000 85.000 100.000 0.000 32.850 15.460 15.940 70.000 100.000 100.000 0.000 29.010 24.570 12.230 85.000 0.000 100.000 0.000 55.060 -53.370 35.920 85.000 10.000 100.000 0.000 52.400 -48.260 32.910 85.000 20.000 100.000 0.000 49.530 -42.540 29.820 85.000 30.000 100.000 0.000 46.710 -36.630 26.740 85.000 40.000 100.000 0.000 43.560 -29.970 23.460 85.000 55.000 100.000 0.000 38.640 -19.100 18.290 85.000 70.000 100.000 0.000 33.870 -7.910 13.310 85.000 85.000 100.000 0.000 29.100 3.390 8.640 85.000 100.000 100.000 0.000 25.630 12.440 5.370 100.000 0.000 100.000 0.000 50.000 -65.000 27.000 100.000 10.000 100.000 0.000 47.510 -60.000 24.680 100.000 20.000 100.000 0.000 45.000 -54.460 22.090 100.000 30.000 100.000 0.000 42.400 -48.290 19.400 100.000 40.000 100.000 0.000 39.450 -41.770 16.350 100.000 55.000 100.000 0.000 35.060 -31.200 11.860 100.000 70.000 100.000 0.000 30.580 -19.730 7.390 100.000 85.000 100.000 0.000 26.030 -8.510 3.130 100.000 100.000 100.000 0.000 23.000 0.000 0.000 0.000 0.000 0.000 20.000 82.790 0.000 -1.690 0.000 10.000 0.000 20.000 79.110 4.610 -3.600 0.000 20.000 0.000 20.000 75.290 9.720 -4.750 0.000 40.000 0.000 20.000 66.710 21.680 -6.250 0.000 70.000 0.000 20.000 52.840 43.520 -6.280 0.000 100.000 0.000 20.000 41.310 64.410 -3.350 10.000 0.000 0.000 20.000 79.770 -2.680 -6.120 10.000 10.000 0.000 20.000 76.160 1.890 -7.120 10.000 20.000 0.000 20.000 72.480 6.820 -8.210 10.000 40.000 0.000 20.000 64.160 18.650 -9.610 10.000 70.000 0.000 20.000 50.700 40.150 -9.620 10.000 100.000 0.000 20.000 39.390 60.780 -6.950 20.000 0.000 0.000 20.000 76.530 -5.090 -10.150 20.000 10.000 0.000 20.000 73.120 -0.850 -10.910 20.000 20.000 0.000 20.000 69.570 3.930 -11.720 20.000 40.000 0.000 20.000 61.570 15.520 -12.920 20.000 70.000 0.000 20.000 48.500 36.590 -13.060 20.000 100.000 0.000 20.000 37.190 57.090 -10.770 40.000 0.000 0.000 20.000 69.550 -11.070 -18.620 40.000 10.000 0.000 20.000 66.490 -7.020 -19.160 40.000 20.000 0.000 20.000 63.220 -2.300 -19.460 40.000 40.000 0.000 20.000 55.900 8.540 -20.390 40.000 70.000 0.000 20.000 43.670 28.720 -20.200 40.000 100.000 0.000 20.000 32.830 48.480 -18.580 70.000 0.000 0.000 20.000 58.190 -22.090 -31.930 70.000 10.000 0.000 20.000 55.600 -18.040 -32.120 70.000 20.000 0.000 20.000 52.850 -13.740 -32.160 70.000 40.000 0.000 20.000 46.440 -3.190 -32.360 70.000 70.000 0.000 20.000 36.000 15.370 -31.650 70.000 100.000 0.000 20.000 26.450 33.810 -30.540 100.000 0.000 0.000 20.000 47.760 -33.560 -43.400 100.000 10.000 0.000 20.000 45.490 -29.580 -43.270 100.000 20.000 0.000 20.000 43.000 -25.450 -43.090 100.000 40.000 0.000 20.000 37.540 -15.310 -42.610 100.000 70.000 0.000 20.000 28.860 2.250 -41.290 100.000 100.000 0.000 20.000 21.330 18.810 -39.650 0.000 0.000 10.000 20.000 82.100 -1.020 4.140 0.000 10.000 10.000 20.000 78.470 3.630 2.740 0.000 20.000 10.000 20.000 74.660 8.770 1.320 0.000 40.000 10.000 20.000 66.120 21.030 -0.840 0.000 70.000 10.000 20.000 52.530 43.010 -1.890 0.000 100.000 10.000 20.000 41.220 63.730 0.440 10.000 0.000 10.000 20.000 79.130 -3.690 0.420 10.000 10.000 10.000 20.000 75.590 0.910 -0.860 10.000 20.000 10.000 20.000 71.950 6.020 -2.140 10.000 40.000 10.000 20.000 63.630 17.990 -4.100 10.000 70.000 10.000 20.000 50.460 39.440 -5.090 10.000 100.000 10.000 20.000 39.360 60.040 -3.150 20.000 0.000 10.000 20.000 75.990 -6.280 -3.670 20.000 10.000 10.000 20.000 72.520 -1.790 -4.780 20.000 20.000 10.000 20.000 68.950 3.040 -5.830 20.000 40.000 10.000 20.000 61.080 14.850 -7.550 20.000 70.000 10.000 20.000 48.260 35.820 -8.570 20.000 100.000 10.000 20.000 37.220 56.310 -6.900 40.000 0.000 10.000 20.000 69.030 -12.630 -12.240 40.000 10.000 10.000 20.000 65.960 -8.310 -13.050 40.000 20.000 10.000 20.000 62.730 -3.740 -13.680 40.000 40.000 10.000 20.000 55.490 7.390 -14.900 40.000 70.000 10.000 20.000 43.450 27.600 -15.770 40.000 100.000 10.000 20.000 32.780 47.610 -14.820 70.000 0.000 10.000 20.000 57.640 -24.350 -25.880 70.000 10.000 10.000 20.000 55.100 -20.300 -26.310 70.000 20.000 10.000 20.000 52.410 -15.820 -26.630 70.000 40.000 10.000 20.000 46.040 -4.940 -27.270 70.000 70.000 10.000 20.000 35.800 13.660 -27.420 70.000 100.000 10.000 20.000 26.410 32.180 -26.900 100.000 0.000 10.000 20.000 47.200 -36.930 -37.320 100.000 10.000 10.000 20.000 44.990 -32.780 -37.580 100.000 20.000 10.000 20.000 42.600 -28.430 -37.540 100.000 40.000 10.000 20.000 37.150 -17.990 -37.750 100.000 70.000 10.000 20.000 28.630 -0.260 -37.260 100.000 100.000 10.000 20.000 21.240 16.570 -36.110 0.000 0.000 20.000 20.000 81.420 -1.650 10.730 0.000 10.000 20.000 20.000 77.870 2.840 9.290 0.000 20.000 20.000 20.000 74.180 7.970 7.900 0.000 40.000 20.000 20.000 65.670 20.490 5.050 0.000 70.000 20.000 20.000 52.350 42.400 3.160 0.000 100.000 20.000 20.000 41.130 62.910 4.390 10.000 0.000 20.000 20.000 78.400 -4.560 7.080 10.000 10.000 20.000 20.000 74.910 0.080 5.570 10.000 20.000 20.000 20.000 71.360 4.990 4.270 10.000 40.000 20.000 20.000 63.150 17.290 1.590 10.000 70.000 20.000 20.000 50.200 38.770 -0.160 10.000 100.000 20.000 20.000 39.250 59.300 0.870 20.000 0.000 20.000 20.000 75.290 -7.290 3.250 20.000 10.000 20.000 20.000 71.840 -2.950 1.870 20.000 20.000 20.000 20.000 68.400 2.060 0.570 20.000 40.000 20.000 20.000 60.540 13.950 -1.900 20.000 70.000 20.000 20.000 47.920 35.120 -3.610 20.000 100.000 20.000 20.000 37.180 55.440 -2.880 40.000 0.000 20.000 20.000 68.350 -13.940 -5.510 40.000 10.000 20.000 20.000 65.380 -9.710 -6.500 40.000 20.000 20.000 20.000 62.070 -4.950 -7.760 40.000 40.000 20.000 20.000 55.010 6.350 -9.430 40.000 70.000 20.000 20.000 43.170 26.690 -10.950 40.000 100.000 20.000 20.000 32.740 46.530 -10.860 70.000 0.000 20.000 20.000 57.060 -26.470 -19.330 70.000 10.000 20.000 20.000 54.510 -22.220 -20.000 70.000 20.000 20.000 20.000 51.780 -17.700 -20.710 70.000 40.000 20.000 20.000 45.530 -6.730 -21.900 70.000 70.000 20.000 20.000 35.490 12.250 -22.920 70.000 100.000 20.000 20.000 26.260 30.640 -23.060 100.000 0.000 20.000 20.000 46.650 -40.100 -31.090 100.000 10.000 20.000 20.000 44.500 -35.960 -31.380 100.000 20.000 20.000 20.000 42.170 -31.550 -31.750 100.000 40.000 20.000 20.000 36.630 -20.760 -32.450 100.000 70.000 20.000 20.000 28.420 -2.900 -32.680 100.000 100.000 20.000 20.000 21.220 14.120 -32.230 0.000 0.000 40.000 20.000 80.020 -3.370 26.000 0.000 10.000 40.000 20.000 76.530 1.710 23.880 0.000 20.000 40.000 20.000 72.860 6.850 21.830 0.000 40.000 40.000 20.000 64.660 19.220 18.030 0.000 70.000 40.000 20.000 51.730 41.230 14.010 0.000 100.000 40.000 20.000 41.090 61.500 13.450 10.000 0.000 40.000 20.000 77.060 -6.060 22.080 10.000 10.000 40.000 20.000 73.700 -1.380 20.150 10.000 20.000 40.000 20.000 70.150 3.820 18.220 10.000 40.000 40.000 20.000 62.210 15.850 14.650 10.000 70.000 40.000 20.000 49.570 37.600 10.550 10.000 100.000 40.000 20.000 39.040 57.560 9.930 20.000 0.000 40.000 20.000 73.950 -9.290 18.110 20.000 10.000 40.000 20.000 70.670 -4.510 16.330 20.000 20.000 40.000 20.000 67.270 0.340 14.450 20.000 40.000 40.000 20.000 59.610 12.360 10.960 20.000 70.000 40.000 20.000 47.310 33.690 7.190 20.000 100.000 40.000 20.000 37.090 53.510 6.290 40.000 0.000 40.000 20.000 67.100 -16.590 9.170 40.000 10.000 40.000 20.000 64.070 -12.050 7.520 40.000 20.000 40.000 20.000 60.930 -7.060 6.160 40.000 40.000 40.000 20.000 53.960 4.300 3.210 40.000 70.000 40.000 20.000 42.480 24.750 -0.710 40.000 100.000 40.000 20.000 32.540 44.450 -1.800 70.000 0.000 40.000 20.000 55.920 -30.440 -5.410 70.000 10.000 40.000 20.000 53.400 -25.980 -6.350 70.000 20.000 40.000 20.000 50.730 -21.400 -7.520 70.000 40.000 40.000 20.000 44.760 -10.220 -9.790 70.000 70.000 40.000 20.000 34.870 9.030 -12.800 70.000 100.000 40.000 20.000 26.000 27.700 -14.490 100.000 0.000 40.000 20.000 45.710 -46.310 -17.410 100.000 10.000 40.000 20.000 43.530 -41.790 -18.180 100.000 20.000 40.000 20.000 41.290 -37.410 -19.030 100.000 40.000 40.000 20.000 35.950 -26.190 -20.860 100.000 70.000 40.000 20.000 28.030 -7.820 -22.750 100.000 100.000 40.000 20.000 20.920 9.580 -23.650 0.000 0.000 70.000 20.000 78.210 -4.730 53.190 0.000 10.000 70.000 20.000 74.740 0.330 50.130 0.000 20.000 70.000 20.000 71.110 5.550 46.870 0.000 40.000 70.000 20.000 63.170 18.070 40.910 0.000 70.000 70.000 20.000 50.770 40.120 32.400 0.000 100.000 70.000 20.000 40.710 59.540 27.780 10.000 0.000 70.000 20.000 75.240 -7.910 48.910 10.000 10.000 70.000 20.000 71.850 -2.950 45.830 10.000 20.000 70.000 20.000 68.380 2.370 42.900 10.000 40.000 70.000 20.000 60.600 14.730 36.860 10.000 70.000 70.000 20.000 48.650 36.360 28.940 10.000 100.000 70.000 20.000 38.770 55.680 24.170 20.000 0.000 70.000 20.000 72.160 -11.110 44.460 20.000 10.000 70.000 20.000 68.930 -6.380 41.590 20.000 20.000 70.000 20.000 65.470 -1.250 38.640 20.000 40.000 70.000 20.000 57.990 11.000 32.900 20.000 70.000 70.000 20.000 46.350 32.210 25.000 20.000 100.000 70.000 20.000 36.650 51.390 20.200 40.000 0.000 70.000 20.000 65.260 -18.940 34.360 40.000 10.000 70.000 20.000 62.290 -14.430 31.720 40.000 20.000 70.000 20.000 59.260 -9.470 29.280 40.000 40.000 70.000 20.000 52.450 2.210 24.310 40.000 70.000 70.000 20.000 41.550 22.730 16.760 40.000 100.000 70.000 20.000 32.240 41.920 11.840 70.000 0.000 70.000 20.000 54.280 -34.790 18.020 70.000 10.000 70.000 20.000 51.880 -30.170 16.170 70.000 20.000 70.000 20.000 49.260 -25.470 14.090 70.000 40.000 70.000 20.000 43.350 -13.960 9.760 70.000 70.000 70.000 20.000 34.080 5.350 3.580 70.000 100.000 70.000 20.000 25.800 23.930 -1.070 100.000 0.000 70.000 20.000 44.410 -53.130 4.160 100.000 10.000 70.000 20.000 42.420 -48.720 2.660 100.000 20.000 70.000 20.000 40.150 -43.810 1.060 100.000 40.000 70.000 20.000 35.090 -32.690 -2.620 100.000 70.000 70.000 20.000 27.500 -13.810 -7.370 100.000 100.000 70.000 20.000 20.810 3.740 -10.660 0.000 0.000 100.000 20.000 76.890 -5.180 80.220 0.000 10.000 100.000 20.000 73.480 -0.200 76.040 0.000 20.000 100.000 20.000 69.860 5.110 72.070 0.000 40.000 100.000 20.000 61.800 18.060 63.320 0.000 70.000 100.000 20.000 49.810 39.670 50.590 0.000 100.000 100.000 20.000 40.400 58.230 40.700 10.000 0.000 100.000 20.000 73.840 -8.530 75.120 10.000 10.000 100.000 20.000 70.560 -3.590 71.360 10.000 20.000 100.000 20.000 66.980 1.680 67.390 10.000 40.000 100.000 20.000 59.350 14.160 58.980 10.000 70.000 100.000 20.000 47.620 35.660 46.410 10.000 100.000 100.000 20.000 38.450 54.270 36.910 20.000 0.000 100.000 20.000 70.700 -11.760 70.200 20.000 10.000 100.000 20.000 67.510 -7.220 66.210 20.000 20.000 100.000 20.000 64.230 -1.980 62.640 20.000 40.000 100.000 20.000 56.760 10.270 54.460 20.000 70.000 100.000 20.000 45.470 31.510 42.270 20.000 100.000 100.000 20.000 36.340 50.010 32.860 40.000 0.000 100.000 20.000 64.000 -20.320 58.560 40.000 10.000 100.000 20.000 61.010 -15.810 55.230 40.000 20.000 100.000 20.000 57.970 -10.830 51.810 40.000 40.000 100.000 20.000 51.280 1.270 44.500 40.000 70.000 100.000 20.000 40.630 21.510 33.110 40.000 100.000 100.000 20.000 31.920 39.950 23.980 70.000 0.000 100.000 20.000 53.260 -37.020 39.940 70.000 10.000 100.000 20.000 50.790 -32.320 37.300 70.000 20.000 100.000 20.000 48.290 -27.540 34.410 70.000 40.000 100.000 20.000 42.500 -15.880 28.260 70.000 70.000 100.000 20.000 33.510 3.210 18.660 70.000 100.000 100.000 20.000 25.540 21.050 10.810 100.000 0.000 100.000 20.000 43.770 -56.930 23.650 100.000 10.000 100.000 20.000 41.710 -52.560 21.440 100.000 20.000 100.000 20.000 39.540 -47.800 19.150 100.000 40.000 100.000 20.000 34.560 -36.440 13.860 100.000 70.000 100.000 20.000 27.130 -17.520 6.610 100.000 100.000 100.000 20.000 20.720 -0.430 0.960 0.000 0.000 0.000 40.000 69.280 0.000 -1.350 0.000 20.000 0.000 40.000 63.020 7.970 -4.250 0.000 40.000 0.000 40.000 55.810 17.930 -5.420 0.000 70.000 0.000 40.000 44.120 36.220 -5.370 0.000 100.000 0.000 40.000 34.350 54.000 -2.980 20.000 0.000 0.000 40.000 64.070 -4.400 -8.640 20.000 20.000 0.000 40.000 58.280 3.100 -9.900 20.000 40.000 0.000 40.000 51.590 12.840 -10.880 20.000 70.000 0.000 40.000 40.610 30.570 -10.960 20.000 100.000 0.000 40.000 30.990 48.020 -9.080 40.000 0.000 0.000 40.000 58.240 -9.590 -15.490 40.000 20.000 0.000 40.000 52.990 -2.200 -16.210 40.000 40.000 0.000 40.000 46.910 7.030 -17.030 40.000 70.000 0.000 40.000 36.680 24.100 -16.880 40.000 100.000 0.000 40.000 27.430 40.910 -15.530 70.000 0.000 0.000 40.000 48.680 -19.090 -26.430 70.000 20.000 0.000 40.000 44.300 -11.910 -26.730 70.000 40.000 0.000 40.000 39.030 -2.810 -27.040 70.000 70.000 0.000 40.000 30.370 13.000 -26.420 70.000 100.000 0.000 40.000 22.280 28.650 -25.320 100.000 0.000 0.000 40.000 39.760 -29.040 -36.240 100.000 20.000 0.000 40.000 35.890 -22.150 -36.110 100.000 40.000 0.000 40.000 31.440 -13.430 -35.820 100.000 70.000 0.000 40.000 24.340 1.680 -34.590 100.000 100.000 0.000 40.000 18.220 15.780 -32.730 0.000 0.000 20.000 40.000 68.060 -1.550 8.450 0.000 20.000 20.000 40.000 62.040 6.410 6.200 0.000 40.000 20.000 40.000 54.920 16.860 3.910 0.000 70.000 20.000 40.000 43.720 35.210 2.440 0.000 100.000 20.000 40.000 34.210 52.650 3.430 20.000 0.000 20.000 40.000 63.000 -6.300 2.410 20.000 20.000 20.000 40.000 57.280 1.480 0.250 20.000 40.000 20.000 40.000 50.720 11.460 -1.760 20.000 70.000 20.000 40.000 40.150 29.230 -3.110 20.000 100.000 20.000 40.000 30.990 46.510 -2.500 40.000 0.000 20.000 40.000 57.250 -11.950 -4.680 40.000 20.000 20.000 40.000 52.050 -4.400 -6.530 40.000 40.000 20.000 40.000 46.200 5.170 -7.950 40.000 70.000 20.000 40.000 36.290 22.280 -9.150 40.000 100.000 20.000 40.000 27.370 39.110 -9.020 70.000 0.000 20.000 40.000 47.810 -22.580 -16.060 70.000 20.000 20.000 40.000 43.470 -15.100 -17.260 70.000 40.000 20.000 40.000 38.310 -5.690 -18.320 70.000 70.000 20.000 40.000 29.980 10.340 -19.060 70.000 100.000 20.000 40.000 22.170 25.840 -18.980 100.000 0.000 20.000 40.000 38.950 -34.310 -26.080 100.000 20.000 20.000 40.000 35.270 -27.000 -26.670 100.000 40.000 20.000 40.000 30.690 -17.700 -27.300 100.000 70.000 20.000 40.000 23.980 -2.430 -27.310 100.000 100.000 20.000 40.000 18.220 11.850 -26.530 0.000 0.000 40.000 40.000 66.780 -3.160 21.140 0.000 20.000 40.000 40.000 60.860 5.310 17.780 0.000 40.000 40.000 40.000 54.050 15.660 14.700 0.000 70.000 40.000 40.000 43.220 34.110 11.470 0.000 100.000 40.000 40.000 34.180 51.310 10.990 20.000 0.000 40.000 40.000 61.830 -8.100 14.760 20.000 20.000 40.000 40.000 56.310 -0.080 11.790 20.000 40.000 40.000 40.000 49.950 10.010 8.940 20.000 70.000 40.000 40.000 39.660 27.880 5.930 20.000 100.000 40.000 40.000 30.940 44.700 5.230 40.000 0.000 40.000 40.000 56.210 -14.200 7.510 40.000 20.000 40.000 40.000 51.130 -6.230 5.040 40.000 40.000 40.000 40.000 45.360 3.340 2.600 40.000 70.000 40.000 40.000 35.760 20.460 -0.520 40.000 100.000 40.000 40.000 27.230 37.140 -1.310 70.000 0.000 40.000 40.000 46.940 -25.810 -4.500 70.000 20.000 40.000 40.000 42.660 -18.160 -6.250 70.000 40.000 40.000 40.000 37.710 -8.620 -8.150 70.000 70.000 40.000 40.000 29.500 7.520 -10.480 70.000 100.000 40.000 40.000 22.010 23.140 -11.660 100.000 0.000 40.000 40.000 38.280 -39.390 -14.670 100.000 20.000 40.000 40.000 34.590 -31.720 -16.000 100.000 40.000 40.000 40.000 30.110 -21.990 -17.490 100.000 70.000 40.000 40.000 23.680 -6.430 -18.860 100.000 100.000 40.000 40.000 18.090 7.940 -19.300 0.000 0.000 70.000 40.000 65.120 -4.570 43.720 0.000 20.000 70.000 40.000 59.290 3.940 38.570 0.000 40.000 70.000 40.000 52.730 14.420 33.690 0.000 70.000 70.000 40.000 42.390 32.870 26.660 0.000 100.000 70.000 40.000 33.840 49.300 22.720 20.000 0.000 70.000 40.000 60.220 -9.850 36.650 20.000 20.000 70.000 40.000 54.740 -1.650 31.910 20.000 40.000 70.000 40.000 48.580 8.620 27.210 20.000 70.000 70.000 40.000 38.860 26.340 20.720 20.000 100.000 70.000 40.000 30.570 42.580 16.670 40.000 0.000 70.000 40.000 54.630 -16.340 28.460 40.000 20.000 70.000 40.000 49.730 -8.440 24.310 40.000 40.000 70.000 40.000 44.130 1.390 20.240 40.000 70.000 70.000 40.000 35.020 18.500 14.070 40.000 100.000 70.000 40.000 27.000 34.710 9.960 70.000 0.000 70.000 40.000 45.620 -29.460 15.000 70.000 20.000 70.000 40.000 41.500 -21.600 11.780 70.000 40.000 70.000 40.000 36.620 -11.800 8.220 70.000 70.000 70.000 40.000 28.930 4.250 3.270 70.000 100.000 70.000 40.000 21.920 19.730 -0.460 100.000 0.000 70.000 40.000 37.310 -44.830 3.280 100.000 20.000 70.000 40.000 33.760 -36.840 0.740 100.000 40.000 70.000 40.000 29.520 -27.250 -2.320 100.000 70.000 70.000 40.000 23.380 -11.490 -5.990 100.000 100.000 70.000 40.000 18.090 2.830 -8.250 0.000 0.000 100.000 40.000 63.890 -5.160 66.350 0.000 20.000 100.000 40.000 58.140 3.310 59.650 0.000 40.000 100.000 40.000 51.500 14.080 52.410 0.000 70.000 100.000 40.000 41.520 32.100 41.680 0.000 100.000 100.000 40.000 33.530 47.770 33.080 20.000 0.000 100.000 40.000 58.890 -10.630 58.240 20.000 20.000 100.000 40.000 53.620 -2.530 52.030 20.000 40.000 100.000 40.000 47.500 7.730 45.290 20.000 70.000 100.000 40.000 38.110 25.440 35.060 20.000 100.000 100.000 40.000 30.300 41.040 26.890 40.000 0.000 100.000 40.000 53.490 -17.740 48.760 40.000 20.000 100.000 40.000 48.600 -9.800 43.250 40.000 40.000 100.000 40.000 43.160 0.400 37.260 40.000 70.000 100.000 40.000 34.300 17.270 27.750 40.000 100.000 100.000 40.000 26.760 32.760 19.840 70.000 0.000 100.000 40.000 44.730 -31.490 33.380 70.000 20.000 100.000 40.000 40.710 -23.490 28.830 70.000 40.000 100.000 40.000 35.990 -13.600 23.760 70.000 70.000 100.000 40.000 28.550 2.250 15.930 70.000 100.000 100.000 40.000 21.780 17.060 9.460 100.000 0.000 100.000 40.000 36.840 -47.900 19.560 100.000 20.000 100.000 40.000 33.370 -40.200 15.800 100.000 40.000 100.000 40.000 29.260 -30.570 11.360 100.000 70.000 100.000 40.000 23.230 -14.920 5.740 100.000 100.000 100.000 40.000 18.100 -1.000 1.780 0.000 0.000 0.000 60.000 54.140 0.000 -0.970 0.000 20.000 0.000 60.000 49.370 6.120 -2.980 0.000 40.000 0.000 60.000 43.810 14.030 -4.030 0.000 70.000 0.000 60.000 34.690 28.550 -4.010 0.000 100.000 0.000 60.000 26.980 42.590 -1.880 20.000 0.000 0.000 60.000 50.140 -3.610 -6.460 20.000 20.000 0.000 60.000 45.720 2.270 -7.510 20.000 40.000 0.000 60.000 40.560 10.050 -8.340 20.000 70.000 0.000 60.000 32.030 24.140 -8.320 20.000 100.000 0.000 60.000 24.470 37.900 -6.570 40.000 0.000 0.000 60.000 45.610 -7.840 -12.040 40.000 20.000 0.000 60.000 41.610 -2.000 -12.610 40.000 40.000 0.000 60.000 36.930 5.440 -13.250 40.000 70.000 0.000 60.000 29.030 18.980 -12.940 40.000 100.000 0.000 60.000 21.810 32.170 -11.590 70.000 0.000 0.000 60.000 38.100 -15.500 -20.860 70.000 20.000 0.000 60.000 34.800 -9.720 -21.010 70.000 40.000 0.000 60.000 30.780 -2.270 -21.160 70.000 70.000 0.000 60.000 24.180 10.340 -20.380 70.000 100.000 0.000 60.000 17.970 22.440 -19.160 100.000 0.000 0.000 60.000 30.990 -23.680 -28.430 100.000 20.000 0.000 60.000 28.090 -18.070 -28.330 100.000 40.000 0.000 60.000 24.740 -10.860 -28.130 100.000 70.000 0.000 60.000 19.420 1.570 -26.900 100.000 100.000 0.000 60.000 14.960 12.690 -24.750 0.000 0.000 20.000 60.000 53.170 -1.340 6.780 0.000 20.000 20.000 60.000 48.590 4.860 5.020 0.000 40.000 20.000 60.000 43.110 13.160 3.220 0.000 70.000 20.000 60.000 34.390 27.650 2.130 0.000 100.000 20.000 60.000 26.870 41.290 3.020 20.000 0.000 20.000 60.000 49.310 -5.130 2.020 20.000 20.000 20.000 60.000 44.950 0.960 0.350 20.000 40.000 20.000 60.000 39.890 8.930 -1.220 20.000 70.000 20.000 60.000 31.690 22.940 -2.150 20.000 100.000 20.000 60.000 24.500 36.460 -1.520 40.000 0.000 20.000 60.000 44.880 -9.700 -3.630 40.000 20.000 20.000 60.000 40.910 -3.750 -5.050 40.000 40.000 20.000 60.000 36.390 3.920 -6.110 40.000 70.000 20.000 60.000 28.760 17.380 -6.860 40.000 100.000 20.000 60.000 21.810 30.500 -6.570 70.000 0.000 20.000 60.000 37.490 -18.180 -12.690 70.000 20.000 20.000 60.000 34.210 -12.180 -13.550 70.000 40.000 20.000 60.000 30.260 -4.530 -14.290 70.000 70.000 20.000 60.000 23.930 8.120 -14.590 70.000 100.000 20.000 60.000 17.960 20.010 -14.200 100.000 0.000 20.000 60.000 30.420 -27.540 -20.550 100.000 20.000 20.000 60.000 27.670 -21.640 -21.000 100.000 40.000 20.000 60.000 24.230 -14.000 -21.460 100.000 70.000 20.000 60.000 19.230 -1.600 -21.140 100.000 100.000 20.000 60.000 15.050 9.560 -19.860 0.000 0.000 40.000 60.000 52.160 -2.740 16.440 0.000 20.000 40.000 60.000 47.660 3.880 13.940 0.000 40.000 40.000 60.000 42.430 12.120 11.650 0.000 70.000 40.000 60.000 34.010 26.630 9.270 0.000 100.000 40.000 60.000 26.850 39.940 8.840 20.000 0.000 40.000 60.000 48.400 -6.640 11.530 20.000 20.000 40.000 60.000 44.200 -0.360 9.320 20.000 40.000 40.000 60.000 39.300 7.680 7.190 20.000 70.000 40.000 60.000 31.330 21.680 4.990 20.000 100.000 40.000 60.000 24.490 34.690 4.450 40.000 0.000 40.000 60.000 44.110 -11.550 5.890 40.000 20.000 40.000 60.000 40.230 -5.290 4.050 40.000 40.000 40.000 60.000 35.770 2.360 2.220 40.000 70.000 40.000 60.000 28.380 15.710 -0.040 40.000 100.000 40.000 60.000 21.750 28.580 -0.590 70.000 0.000 40.000 60.000 36.890 -20.730 -3.560 70.000 20.000 40.000 60.000 33.640 -14.630 -4.840 70.000 40.000 40.000 60.000 29.850 -6.930 -6.240 70.000 70.000 40.000 60.000 23.620 5.680 -7.800 70.000 100.000 40.000 60.000 17.930 17.560 -8.450 100.000 0.000 40.000 60.000 29.950 -31.340 -11.680 100.000 20.000 40.000 60.000 27.220 -25.190 -12.650 100.000 40.000 40.000 60.000 23.870 -17.250 -13.740 100.000 70.000 40.000 60.000 19.110 -4.760 -14.420 100.000 100.000 40.000 60.000 15.060 6.360 -14.130 0.000 0.000 70.000 60.000 50.830 -4.070 33.690 0.000 20.000 70.000 60.000 46.430 2.580 29.860 0.000 40.000 70.000 60.000 41.400 10.900 26.200 0.000 70.000 70.000 60.000 33.390 25.350 20.880 0.000 100.000 70.000 60.000 26.670 37.920 17.690 20.000 0.000 70.000 60.000 47.150 -8.220 28.330 20.000 20.000 70.000 60.000 42.990 -1.800 24.790 20.000 40.000 70.000 60.000 38.250 6.380 21.240 20.000 70.000 70.000 60.000 30.750 20.190 16.330 20.000 100.000 70.000 60.000 24.280 32.650 13.120 40.000 0.000 70.000 60.000 42.910 -13.380 22.040 40.000 20.000 70.000 60.000 39.170 -7.190 18.930 40.000 40.000 70.000 60.000 34.850 0.640 15.840 40.000 70.000 70.000 60.000 27.860 13.930 11.190 40.000 100.000 70.000 60.000 21.650 26.390 8.020 70.000 0.000 70.000 60.000 35.970 -23.620 11.590 70.000 20.000 70.000 60.000 32.840 -17.370 9.180 70.000 40.000 70.000 60.000 29.090 -9.470 6.490 70.000 70.000 70.000 60.000 23.270 3.010 2.880 70.000 100.000 70.000 60.000 17.950 14.780 0.220 100.000 0.000 70.000 60.000 29.420 -35.370 2.290 100.000 20.000 70.000 60.000 26.770 -28.940 0.410 100.000 40.000 70.000 60.000 23.560 -21.110 -1.860 100.000 70.000 70.000 60.000 19.010 -8.610 -4.320 100.000 100.000 70.000 60.000 15.240 2.300 -5.480 0.000 0.000 100.000 60.000 49.860 -4.740 51.160 0.000 20.000 100.000 60.000 45.520 1.790 46.020 0.000 40.000 100.000 60.000 40.430 10.280 40.390 0.000 70.000 100.000 60.000 32.740 24.380 32.030 0.000 100.000 100.000 60.000 26.550 36.390 25.310 20.000 0.000 100.000 60.000 46.100 -9.050 44.940 20.000 20.000 100.000 60.000 42.110 -2.770 40.190 20.000 40.000 100.000 60.000 37.430 5.360 34.970 20.000 70.000 100.000 60.000 30.210 19.210 27.080 20.000 100.000 100.000 60.000 24.160 31.220 20.740 40.000 0.000 100.000 60.000 41.990 -14.690 37.610 40.000 20.000 100.000 60.000 38.300 -8.510 33.410 40.000 40.000 100.000 60.000 34.130 -0.390 28.780 40.000 70.000 100.000 60.000 27.350 12.800 21.540 40.000 100.000 100.000 60.000 21.510 24.790 15.480 70.000 0.000 100.000 60.000 35.330 -25.350 25.690 70.000 20.000 100.000 60.000 32.290 -18.970 22.290 70.000 40.000 100.000 60.000 28.690 -10.950 18.450 70.000 70.000 100.000 60.000 23.060 1.440 12.640 70.000 100.000 100.000 60.000 17.900 12.730 7.860 100.000 0.000 100.000 60.000 29.320 -37.730 14.980 100.000 20.000 100.000 60.000 26.660 -31.490 12.200 100.000 40.000 100.000 60.000 23.480 -23.600 8.870 100.000 70.000 100.000 60.000 19.010 -11.280 4.890 100.000 100.000 100.000 60.000 15.410 -0.880 2.400 0.000 0.000 0.000 80.000 36.620 0.000 -0.520 0.000 40.000 0.000 80.000 29.760 9.940 -2.490 0.000 70.000 0.000 80.000 23.850 19.770 -2.140 0.000 100.000 0.000 80.000 18.880 29.400 -0.220 40.000 0.000 0.000 80.000 30.950 -5.660 -8.170 40.000 40.000 0.000 80.000 25.040 3.890 -9.020 40.000 70.000 0.000 80.000 19.890 13.340 -8.450 40.000 100.000 0.000 80.000 15.500 22.250 -6.860 70.000 0.000 0.000 80.000 25.840 -11.040 -14.370 70.000 40.000 0.000 80.000 20.820 -1.420 -14.620 70.000 70.000 0.000 80.000 16.660 7.530 -13.640 70.000 100.000 0.000 80.000 13.150 15.630 -11.940 100.000 0.000 0.000 80.000 20.920 -16.710 -19.640 100.000 40.000 0.000 80.000 16.860 -7.000 -19.310 100.000 70.000 0.000 80.000 13.810 1.810 -17.830 100.000 100.000 0.000 80.000 11.530 9.070 -15.470 0.000 0.000 40.000 80.000 35.230 -2.010 11.240 0.000 40.000 40.000 80.000 28.910 8.220 8.040 0.000 70.000 40.000 80.000 23.490 17.920 6.630 0.000 100.000 40.000 80.000 18.850 26.880 6.660 40.000 0.000 40.000 80.000 29.960 -8.490 4.030 40.000 40.000 40.000 80.000 24.530 1.520 1.650 40.000 70.000 40.000 80.000 19.780 10.790 0.400 40.000 100.000 40.000 80.000 15.700 19.100 0.420 70.000 0.000 40.000 80.000 25.120 -14.890 -2.540 70.000 40.000 40.000 80.000 20.600 -4.820 -4.170 70.000 70.000 40.000 80.000 16.710 4.060 -4.840 70.000 100.000 40.000 80.000 13.400 11.570 -4.690 100.000 0.000 40.000 80.000 20.380 -21.820 -8.300 100.000 40.000 40.000 80.000 16.720 -11.450 -9.250 100.000 70.000 40.000 80.000 13.970 -2.750 -9.090 100.000 100.000 40.000 80.000 11.800 4.080 -8.080 0.000 0.000 70.000 80.000 34.440 -3.270 22.320 0.000 40.000 70.000 80.000 28.330 7.040 17.460 0.000 70.000 70.000 80.000 23.200 16.620 14.110 0.000 100.000 70.000 80.000 18.890 24.990 12.160 40.000 0.000 70.000 80.000 29.270 -9.980 14.590 40.000 40.000 70.000 80.000 24.050 0.030 10.570 40.000 70.000 70.000 80.000 19.600 9.130 7.700 40.000 100.000 70.000 80.000 15.840 16.960 5.900 70.000 0.000 70.000 80.000 24.670 -17.000 7.510 70.000 40.000 70.000 80.000 20.290 -6.810 4.330 70.000 70.000 70.000 80.000 16.670 1.830 2.260 70.000 100.000 70.000 80.000 13.630 9.010 1.000 100.000 0.000 70.000 80.000 20.320 -24.470 1.170 100.000 40.000 70.000 80.000 16.820 -14.100 -1.030 100.000 70.000 70.000 80.000 14.160 -5.490 -2.100 100.000 100.000 70.000 80.000 12.140 1.070 -2.210 0.000 0.000 100.000 80.000 34.020 -4.280 32.910 0.000 40.000 100.000 80.000 27.820 6.290 26.120 0.000 70.000 100.000 80.000 22.920 15.650 20.880 0.000 100.000 100.000 80.000 19.010 23.580 16.630 40.000 0.000 100.000 80.000 28.860 -11.190 24.270 40.000 40.000 100.000 80.000 23.590 -1.050 18.490 40.000 70.000 100.000 80.000 19.290 7.850 13.880 40.000 100.000 100.000 80.000 15.910 15.350 10.270 70.000 0.000 100.000 80.000 24.450 -18.330 16.500 70.000 40.000 100.000 80.000 20.050 -8.130 11.800 70.000 70.000 100.000 80.000 16.560 0.340 8.210 70.000 100.000 100.000 80.000 13.740 7.170 5.560 100.000 0.000 100.000 80.000 20.590 -25.970 9.540 100.000 40.000 100.000 80.000 16.980 -15.630 6.020 100.000 70.000 100.000 80.000 14.330 -7.150 3.840 100.000 100.000 100.000 80.000 12.460 -0.860 2.710 0.000 0.000 0.000 100.000 16.000 0.000 0.000 0.000 40.000 0.000 100.000 13.480 5.340 -0.360 0.000 100.000 0.000 100.000 10.420 13.930 1.350 40.000 0.000 0.000 100.000 13.710 -2.860 -4.020 40.000 40.000 0.000 100.000 11.660 2.500 -3.900 40.000 100.000 0.000 100.000 9.020 10.920 -1.890 100.000 0.000 0.000 100.000 10.410 -8.200 -10.250 100.000 40.000 0.000 100.000 9.080 -2.400 -9.230 100.000 100.000 0.000 100.000 7.880 5.790 -5.940 0.000 0.000 40.000 100.000 15.590 -1.380 4.820 0.000 40.000 40.000 100.000 13.530 3.830 3.990 0.000 100.000 40.000 100.000 10.730 11.610 4.240 40.000 0.000 40.000 100.000 13.700 -4.830 1.390 40.000 40.000 40.000 100.000 12.030 0.550 0.870 40.000 100.000 40.000 100.000 9.560 8.140 1.370 100.000 0.000 40.000 100.000 10.650 -11.290 -4.220 100.000 40.000 40.000 100.000 9.570 -5.160 -4.000 100.000 100.000 40.000 100.000 8.230 2.660 -2.380 0.000 0.000 100.000 100.000 15.710 -3.080 11.680 0.000 40.000 100.000 100.000 13.740 1.760 9.870 0.000 100.000 100.000 100.000 11.330 8.610 7.280 40.000 0.000 100.000 100.000 13.910 -6.910 8.610 40.000 40.000 100.000 100.000 12.310 -1.920 7.030 40.000 100.000 100.000 100.000 10.130 4.960 4.970 100.000 0.000 100.000 100.000 11.320 -12.880 3.560 100.000 40.000 100.000 100.000 10.160 -7.440 2.670 100.000 100.000 100.000 100.000 8.710 -0.070 2.060 100.000 0.000 0.000 0.000 55.000 -37.000 -50.000 98.000 0.000 0.000 0.000 55.730 -36.300 -49.330 95.000 0.000 0.000 0.000 56.810 -35.250 -48.280 90.000 0.000 0.000 0.000 58.650 -33.370 -46.400 85.000 0.000 0.000 0.000 60.550 -31.320 -44.340 80.000 0.000 0.000 0.000 62.550 -29.070 -42.070 75.000 0.000 0.000 0.000 64.680 -26.830 -39.610 70.000 0.000 0.000 0.000 66.860 -24.730 -37.100 60.000 0.000 0.000 0.000 71.220 -20.440 -31.880 50.000 0.000 0.000 0.000 75.620 -16.480 -26.700 40.000 0.000 0.000 0.000 79.720 -12.530 -21.750 30.000 0.000 0.000 0.000 83.890 -9.150 -16.550 25.000 0.000 0.000 0.000 85.850 -7.490 -14.130 20.000 0.000 0.000 0.000 87.680 -5.780 -11.800 15.000 0.000 0.000 0.000 89.630 -4.400 -9.360 10.000 0.000 0.000 0.000 91.480 -2.970 -6.960 7.000 0.000 0.000 0.000 92.570 -2.090 -5.520 5.000 0.000 0.000 0.000 93.280 -1.500 -4.520 3.000 0.000 0.000 0.000 94.000 -0.900 -3.500 2.000 0.000 0.000 0.000 94.350 -0.610 -2.990 0.000 100.000 0.000 0.000 48.000 74.000 -3.000 0.000 98.000 0.000 0.000 48.670 72.800 -3.400 0.000 95.000 0.000 0.000 49.690 70.970 -3.940 0.000 90.000 0.000 0.000 51.500 67.620 -4.700 0.000 85.000 0.000 0.000 53.500 63.840 -5.370 0.000 80.000 0.000 0.000 55.800 59.520 -5.940 0.000 75.000 0.000 0.000 58.320 54.990 -6.390 0.000 70.000 0.000 0.000 60.840 50.590 -6.740 0.000 60.000 0.000 0.000 66.010 41.960 -7.280 0.000 50.000 0.000 0.000 71.230 33.640 -7.300 0.000 40.000 0.000 0.000 76.420 25.780 -6.910 0.000 30.000 0.000 0.000 81.390 18.700 -6.190 0.000 25.000 0.000 0.000 83.800 15.340 -5.770 0.000 20.000 0.000 0.000 86.180 12.010 -5.210 0.000 15.000 0.000 0.000 88.450 8.910 -4.570 0.000 10.000 0.000 0.000 90.670 5.900 -3.860 0.000 7.000 0.000 0.000 91.980 4.110 -3.420 0.000 5.000 0.000 0.000 92.860 2.930 -3.020 0.000 3.000 0.000 0.000 93.740 1.750 -2.610 0.000 2.000 0.000 0.000 94.180 1.170 -2.390 0.000 0.000 100.000 0.000 89.000 -5.000 93.000 0.000 0.000 98.000 0.000 89.090 -5.010 91.180 0.000 0.000 95.000 0.000 89.180 -5.000 88.440 0.000 0.000 90.000 0.000 89.340 -5.000 83.760 0.000 0.000 85.000 0.000 89.520 -4.950 78.840 0.000 0.000 80.000 0.000 89.730 -4.820 73.520 0.000 0.000 75.000 0.000 90.020 -4.750 67.990 0.000 0.000 70.000 0.000 90.340 -4.700 62.560 0.000 0.000 60.000 0.000 90.920 -4.390 51.510 0.000 0.000 50.000 0.000 91.550 -4.000 41.000 0.000 0.000 40.000 0.000 92.190 -3.470 31.150 0.000 0.000 30.000 0.000 92.970 -2.610 22.020 0.000 0.000 25.000 0.000 93.320 -2.130 17.610 0.000 0.000 20.000 0.000 93.620 -1.620 13.270 0.000 0.000 15.000 0.000 93.980 -1.310 9.310 0.000 0.000 10.000 0.000 94.340 -0.940 5.420 0.000 0.000 7.000 0.000 94.550 -0.690 3.100 0.000 0.000 5.000 0.000 94.690 -0.500 1.640 0.000 0.000 3.000 0.000 94.840 -0.310 0.190 0.000 0.000 2.000 0.000 94.920 -0.210 -0.530 0.000 0.000 0.000 100.000 16.000 0.000 0.000 0.000 0.000 0.000 98.000 18.180 0.000 -0.060 0.000 0.000 0.000 95.000 21.460 0.000 -0.140 0.000 0.000 0.000 90.000 26.690 0.000 -0.270 0.000 0.000 0.000 85.000 31.720 0.000 -0.400 0.000 0.000 0.000 80.000 36.620 0.000 -0.520 0.000 0.000 0.000 75.000 41.110 0.000 -0.640 0.000 0.000 0.000 70.000 45.530 0.000 -0.750 0.000 0.000 0.000 60.000 54.140 0.000 -0.970 0.000 0.000 0.000 50.000 61.820 0.000 -1.160 0.000 0.000 0.000 40.000 69.280 0.000 -1.350 0.000 0.000 0.000 30.000 76.120 0.000 -1.520 0.000 0.000 0.000 25.000 79.470 0.000 -1.610 0.000 0.000 0.000 20.000 82.790 0.000 -1.690 0.000 0.000 0.000 15.000 85.890 0.000 -1.770 0.000 0.000 0.000 10.000 88.970 0.000 -1.850 0.000 0.000 0.000 7.000 90.810 0.000 -1.890 0.000 0.000 0.000 5.000 92.030 0.000 -1.920 0.000 0.000 0.000 3.000 93.240 0.000 -1.960 0.000 0.000 0.000 2.000 93.850 0.000 -1.970 0.000 0.000 0.000 0.000 95.000 0.000 -2.000 100.000 85.000 85.000 0.000 26.290 -6.810 -3.410 80.000 65.000 65.000 0.000 37.470 -3.860 -3.150 60.000 45.000 45.000 0.000 52.220 -2.480 -3.530 40.000 27.000 27.000 0.000 67.670 -1.960 -4.370 20.000 12.000 12.000 0.000 81.950 -0.890 -4.100 10.000 6.000 6.000 0.000 88.540 -0.380 -3.130 5.000 3.000 3.000 0.000 91.800 -0.180 -2.620 40.000 27.000 27.000 10.000 63.530 -2.080 -4.150 20.000 12.000 12.000 10.000 76.860 -1.010 -3.930 10.000 6.000 6.000 10.000 82.990 -0.470 -3.000 60.000 45.000 45.000 20.000 45.790 -2.560 -3.060 40.000 27.000 27.000 20.000 59.330 -2.180 -3.910 20.000 12.000 12.000 20.000 71.670 -1.100 -3.710 10.000 6.000 6.000 20.000 77.280 -0.590 -2.930 80.000 65.000 65.000 40.000 28.070 -2.810 -1.890 60.000 45.000 45.000 40.000 38.650 -2.300 -2.510 40.000 27.000 27.000 40.000 49.800 -2.080 -3.340 20.000 12.000 12.000 40.000 60.000 -1.130 -3.310 10.000 6.000 6.000 40.000 64.680 -0.640 -2.750 100.000 85.000 85.000 60.000 16.970 -4.320 -0.420 80.000 65.000 65.000 60.000 22.580 -2.140 -1.150 60.000 45.000 45.000 60.000 30.560 -2.150 -1.790 40.000 27.000 27.000 60.000 39.180 -1.930 -2.520 20.000 12.000 12.000 60.000 47.030 -1.060 -2.390 10.000 6.000 6.000 60.000 50.620 -0.630 -1.850 100.000 85.000 85.000 80.000 13.110 -2.730 0.710 80.000 65.000 65.000 80.000 16.240 -1.670 -0.410 60.000 45.000 45.000 80.000 21.180 -1.420 -1.050 40.000 27.000 27.000 80.000 26.660 -1.360 -1.570 20.000 12.000 12.000 80.000 31.850 -0.790 -1.470 10.000 6.000 6.000 80.000 34.250 -0.440 -1.070 100.000 85.000 85.000 100.000 8.880 -0.950 1.180 80.000 65.000 65.000 100.000 9.740 -1.010 0.310 60.000 45.000 45.000 100.000 11.020 -0.850 -0.350 40.000 27.000 27.000 100.000 12.520 -0.700 -0.680 20.000 12.000 12.000 100.000 14.190 -0.380 -0.550 10.000 6.000 6.000 100.000 15.070 -0.210 -0.300 100.000 0.000 0.000 70.000 26.040 -20.280 -24.040 0.000 100.000 0.000 70.000 22.980 36.070 -1.110 0.000 0.000 100.000 70.000 42.070 -4.540 42.120 100.000 100.000 0.000 70.000 13.270 10.790 -20.160 100.000 0.000 100.000 70.000 25.030 -31.880 12.280 0.000 100.000 100.000 70.000 22.820 30.080 20.980 40.000 40.000 0.000 70.000 31.100 4.660 -11.150 40.000 0.000 40.000 70.000 37.160 -10.020 4.960 0.000 40.000 40.000 70.000 35.780 10.180 9.840 3.000 3.000 0.000 0.000 92.700 0.800 -3.900 3.000 0.000 3.000 0.000 93.800 -1.260 -1.150 0.000 3.000 3.000 0.000 93.540 1.430 -0.320 3.000 3.000 3.000 0.000 92.510 0.420 -1.600 3.000 0.000 0.000 3.000 92.230 -0.890 -3.420 0.000 3.000 0.000 3.000 91.990 1.620 -2.580 3.000 3.000 0.000 3.000 91.010 0.710 -3.800 0.000 0.000 3.000 3.000 93.020 -0.320 0.060 3.000 0.000 3.000 3.000 92.040 -1.230 -1.200 0.000 3.000 3.000 3.000 91.790 1.300 -0.410 3.000 3.000 3.000 3.000 90.820 0.350 -1.620 7.000 7.000 0.000 0.000 89.540 1.870 -6.440 7.000 0.000 7.000 0.000 92.110 -2.930 -0.030 0.000 7.000 7.000 0.000 91.500 3.340 1.920 7.000 7.000 7.000 0.000 89.100 1.000 -1.100 7.000 0.000 0.000 7.000 88.460 -2.030 -5.310 0.000 7.000 0.000 7.000 87.910 3.760 -3.350 7.000 7.000 0.000 7.000 85.620 1.660 -6.180 0.000 0.000 7.000 7.000 90.300 -0.730 2.770 7.000 0.000 7.000 7.000 88.010 -2.830 -0.150 0.000 7.000 7.000 7.000 87.440 3.030 1.660 7.000 7.000 7.000 7.000 85.180 0.840 -1.150 40.000 3.000 0.000 0.000 78.630 -11.110 -21.960 3.000 40.000 0.000 0.000 75.500 24.680 -8.150 40.000 0.000 3.000 0.000 79.570 -13.130 -19.490 40.000 3.000 3.000 0.000 78.450 -11.570 -19.750 0.000 40.000 3.000 0.000 76.200 25.520 -5.040 3.000 40.000 3.000 0.000 75.310 24.440 -6.190 40.000 40.000 3.000 0.000 63.550 9.900 -21.790 3.000 0.000 40.000 0.000 91.130 -4.410 29.700 0.000 3.000 40.000 0.000 90.940 -1.610 30.350 3.000 3.000 40.000 0.000 89.910 -2.730 28.950 40.000 3.000 40.000 0.000 75.880 -17.440 10.390 3.000 40.000 40.000 0.000 73.300 21.710 20.160 40.000 0.000 0.000 3.000 78.210 -12.310 -21.280 40.000 3.000 0.000 3.000 77.190 -10.980 -21.480 0.000 40.000 0.000 3.000 74.980 25.170 -6.820 3.000 40.000 0.000 3.000 74.110 24.130 -7.980 40.000 40.000 0.000 3.000 62.530 10.060 -23.260 40.000 0.000 3.000 3.000 78.060 -12.880 -19.150 40.000 3.000 3.000 3.000 77.010 -11.410 -19.400 0.000 40.000 3.000 3.000 74.770 24.930 -5.040 3.000 40.000 3.000 3.000 73.930 23.910 -6.120 40.000 40.000 3.000 3.000 62.410 9.660 -21.390 0.000 0.000 40.000 3.000 90.380 -3.460 30.380 3.000 0.000 40.000 3.000 89.380 -4.340 29.030 40.000 0.000 40.000 3.000 75.500 -18.690 10.750 0.000 3.000 40.000 3.000 89.200 -1.710 29.630 3.000 3.000 40.000 3.000 88.230 -2.760 28.320 40.000 3.000 40.000 3.000 74.470 -17.170 10.150 0.000 40.000 40.000 3.000 72.760 22.370 20.870 3.000 40.000 40.000 3.000 71.940 21.220 19.740 40.000 40.000 40.000 3.000 60.410 5.250 3.670 3.000 0.000 0.000 40.000 68.540 -0.850 -3.100 0.000 3.000 0.000 40.000 68.360 0.960 -2.510 3.000 3.000 0.000 40.000 67.640 0.290 -3.370 40.000 3.000 0.000 40.000 57.490 -8.620 -15.640 3.000 40.000 0.000 40.000 55.190 17.180 -6.250 0.000 0.000 3.000 40.000 69.100 -0.450 -0.600 3.000 0.000 3.000 40.000 68.370 -1.120 -1.490 40.000 0.000 3.000 40.000 58.130 -10.000 -13.940 0.000 3.000 3.000 40.000 68.190 0.710 -0.930 3.000 3.000 3.000 40.000 67.480 0.010 -1.780 40.000 3.000 3.000 40.000 57.360 -8.920 -14.120 0.000 40.000 3.000 40.000 55.650 17.740 -4.120 3.000 40.000 3.000 40.000 55.040 17.010 -4.890 40.000 40.000 3.000 40.000 46.820 6.730 -15.660 3.000 0.000 40.000 40.000 66.070 -3.800 20.190 0.000 3.000 40.000 40.000 65.930 -1.900 20.620 3.000 3.000 40.000 40.000 65.240 -2.660 19.700 40.000 3.000 40.000 40.000 55.470 -13.100 7.080 3.000 40.000 40.000 40.000 53.460 14.840 13.890 0.000 0.000 0.000 10.000 88.970 0.000 -1.850 0.000 0.000 10.000 10.000 88.270 -0.950 4.760 0.000 0.000 20.000 10.000 87.560 -1.670 12.050 0.000 0.000 40.000 10.000 86.140 -3.420 28.590 0.000 0.000 70.000 10.000 84.320 -4.720 57.790 0.000 10.000 0.000 10.000 84.960 5.240 -3.660 0.000 10.000 10.000 10.000 84.320 4.180 3.200 0.000 10.000 20.000 10.000 83.690 3.320 10.460 0.000 10.000 40.000 10.000 82.320 2.120 26.280 0.000 10.000 70.000 10.000 80.510 0.790 54.350 0.000 20.000 0.000 10.000 80.740 10.920 -4.970 0.000 20.000 10.000 10.000 80.130 9.880 1.650 0.000 20.000 20.000 10.000 79.620 9.010 8.870 0.000 20.000 40.000 10.000 78.260 7.830 23.930 0.000 20.000 70.000 10.000 76.500 6.590 50.790 0.000 40.000 0.000 10.000 71.600 23.730 -6.580 0.000 40.000 10.000 10.000 71.000 23.040 -0.700 0.000 40.000 20.000 10.000 70.500 22.420 5.720 0.000 40.000 40.000 10.000 69.440 21.090 19.740 0.000 40.000 70.000 10.000 67.930 20.000 44.250 0.000 70.000 0.000 10.000 56.920 47.020 -6.510 0.000 70.000 10.000 10.000 56.600 46.490 -1.780 0.000 70.000 20.000 10.000 56.370 45.850 3.710 0.000 70.000 40.000 10.000 55.710 44.660 15.390 0.000 70.000 70.000 10.000 54.710 43.570 35.080 10.000 0.000 0.000 10.000 85.690 -2.790 -6.450 10.000 0.000 10.000 10.000 85.020 -3.870 0.630 10.000 0.000 20.000 10.000 84.260 -4.810 7.960 10.000 0.000 40.000 10.000 82.900 -6.430 24.280 10.000 0.000 70.000 10.000 81.040 -8.210 53.050 10.000 10.000 0.000 10.000 81.800 2.210 -7.550 10.000 10.000 10.000 10.000 81.180 1.190 -0.790 10.000 10.000 20.000 10.000 80.480 0.270 6.360 10.000 10.000 40.000 10.000 79.220 -1.250 22.130 10.000 10.000 70.000 10.000 77.360 -2.790 49.690 10.000 20.000 0.000 10.000 77.720 7.720 -8.750 10.000 20.000 10.000 10.000 77.130 6.810 -2.190 10.000 20.000 20.000 10.000 76.540 5.760 4.870 10.000 20.000 40.000 10.000 75.300 4.450 19.960 10.000 20.000 70.000 10.000 73.480 3.050 46.360 10.000 40.000 0.000 10.000 68.820 20.410 -10.280 10.000 40.000 10.000 10.000 68.260 19.670 -4.340 10.000 40.000 20.000 10.000 67.750 18.920 1.930 10.000 40.000 40.000 10.000 66.760 17.380 15.980 10.000 40.000 70.000 10.000 65.120 16.250 39.830 10.000 70.000 0.000 10.000 54.550 43.340 -10.200 10.000 70.000 10.000 10.000 54.280 42.590 -5.340 10.000 70.000 20.000 10.000 54.000 41.880 0.060 10.000 70.000 40.000 10.000 53.310 40.670 11.580 10.000 70.000 70.000 10.000 52.350 39.420 31.200 20.000 0.000 0.000 10.000 82.150 -5.430 -10.940 20.000 0.000 10.000 10.000 81.550 -6.720 -3.910 20.000 0.000 20.000 10.000 80.840 -7.870 3.670 20.000 0.000 40.000 10.000 79.470 -9.960 19.850 20.000 0.000 70.000 10.000 77.620 -11.780 48.100 20.000 10.000 0.000 10.000 78.430 -0.740 -11.780 20.000 10.000 10.000 10.000 77.810 -1.790 -5.120 20.000 10.000 20.000 10.000 77.130 -2.970 2.190 20.000 10.000 40.000 10.000 75.890 -4.730 17.860 20.000 10.000 70.000 10.000 74.080 -6.590 44.930 20.000 20.000 0.000 10.000 74.530 4.520 -12.670 20.000 20.000 10.000 10.000 73.910 3.540 -6.210 20.000 20.000 20.000 10.000 73.310 2.480 0.760 20.000 20.000 40.000 10.000 72.140 0.690 15.750 20.000 20.000 70.000 10.000 70.280 -0.940 41.700 20.000 40.000 0.000 10.000 65.960 16.940 -13.960 20.000 40.000 10.000 10.000 65.440 16.170 -8.140 20.000 40.000 20.000 10.000 64.880 15.230 -1.940 20.000 40.000 40.000 10.000 63.880 13.510 11.920 20.000 40.000 70.000 10.000 62.230 12.150 35.440 20.000 70.000 0.000 10.000 52.060 39.450 -14.020 20.000 70.000 10.000 10.000 51.810 38.620 -9.160 20.000 70.000 20.000 10.000 51.450 37.870 -3.760 20.000 70.000 40.000 10.000 50.780 36.410 7.780 20.000 70.000 70.000 10.000 49.790 34.930 26.930 40.000 0.000 0.000 10.000 74.670 -11.820 -20.170 40.000 0.000 10.000 10.000 74.110 -13.500 -13.250 40.000 0.000 20.000 10.000 73.400 -15.000 -5.780 40.000 0.000 40.000 10.000 72.060 -17.840 10.090 40.000 0.000 70.000 10.000 70.130 -20.220 37.130 40.000 10.000 0.000 10.000 71.320 -7.330 -20.750 40.000 10.000 10.000 10.000 70.750 -8.780 -14.140 40.000 10.000 20.000 10.000 70.130 -10.320 -6.930 40.000 10.000 40.000 10.000 68.770 -12.840 8.330 40.000 10.000 70.000 10.000 66.880 -15.240 34.250 40.000 20.000 0.000 10.000 67.700 -2.160 -21.130 40.000 20.000 10.000 10.000 67.190 -3.690 -14.840 40.000 20.000 20.000 10.000 66.510 -5.050 -8.230 40.000 20.000 40.000 10.000 65.280 -7.350 6.730 40.000 20.000 70.000 10.000 63.510 -9.830 31.540 40.000 40.000 0.000 10.000 59.820 9.450 -22.080 40.000 40.000 10.000 10.000 59.390 8.210 -16.170 40.000 40.000 20.000 10.000 58.880 7.030 -10.140 40.000 40.000 40.000 10.000 57.780 4.870 3.490 40.000 40.000 70.000 10.000 56.180 2.640 26.060 40.000 70.000 0.000 10.000 46.780 31.030 -21.790 40.000 70.000 10.000 10.000 46.550 29.860 -16.980 40.000 70.000 20.000 10.000 46.250 28.840 -11.730 40.000 70.000 40.000 10.000 45.520 26.860 -0.710 40.000 70.000 70.000 10.000 44.520 24.730 17.930 70.000 0.000 0.000 10.000 62.590 -23.390 -34.490 70.000 0.000 10.000 10.000 61.980 -25.950 -27.890 70.000 0.000 20.000 10.000 61.330 -28.340 -20.700 70.000 0.000 40.000 10.000 60.070 -32.660 -5.640 70.000 0.000 70.000 10.000 58.300 -37.280 19.490 70.000 10.000 0.000 10.000 59.770 -19.050 -34.640 70.000 10.000 10.000 10.000 59.200 -21.500 -28.350 70.000 10.000 20.000 10.000 58.540 -23.700 -21.420 70.000 10.000 40.000 10.000 57.320 -27.800 -6.700 70.000 10.000 70.000 10.000 55.670 -32.290 17.390 70.000 20.000 0.000 10.000 56.680 -14.330 -34.740 70.000 20.000 10.000 10.000 56.180 -16.650 -28.710 70.000 20.000 20.000 10.000 55.510 -18.750 -22.200 70.000 20.000 40.000 10.000 54.360 -22.730 -7.980 70.000 20.000 70.000 10.000 52.770 -27.160 15.160 70.000 40.000 0.000 10.000 49.740 -3.190 -34.890 70.000 40.000 10.000 10.000 49.310 -5.190 -29.380 70.000 40.000 20.000 10.000 48.770 -7.170 -23.470 70.000 40.000 40.000 10.000 47.900 -10.840 -10.480 70.000 40.000 70.000 10.000 46.390 -14.920 10.450 70.000 70.000 0.000 10.000 38.560 16.600 -34.100 70.000 70.000 10.000 10.000 38.320 14.780 -29.530 70.000 70.000 20.000 10.000 37.980 13.220 -24.590 70.000 70.000 40.000 10.000 37.300 9.900 -13.760 70.000 70.000 70.000 10.000 36.410 5.950 3.710 50.000 40.000 40.000 0.000 58.040 0.260 -1.130 100.000 0.000 0.000 10.000 51.390 -35.310 -46.690 0.000 100.000 0.000 10.000 44.660 69.240 -3.110 0.000 0.000 100.000 10.000 82.980 -5.080 86.630 0.000 100.000 100.000 10.000 43.700 63.120 44.290 100.000 0.000 100.000 10.000 46.870 -61.120 25.460 100.000 100.000 0.000 10.000 22.640 20.480 -42.960 END_DATA sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/images/000077500000000000000000000000001320103431700252405ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/images/sample.png000066400000000000000000001362321320103431700272360ustar00rootroot00000000000000PNG  IHDRA IDATx}w|3w$! H.S[qK9w]'N%S\ $n4c *0E $Եݙ3٢+>hfggfg_~ ti24Fn -!Ԁ @MdIP_{>`͑}_C@`Q៊Hak-Aܒ6ʿ8HgVUO>iiil'n8>466=Hb -s!|7¦䘣ӧOg}&#\4'M'Oov 2A dBcߎh aҤIxaZȍ;v ַ  HIʕ+_%8~xC1B6#Cnb ºu9e+EdFihu]&&!A+G}C%zJ-=0!G`x"?+V 2dn +MyǷ<>ؓ~aQ$2rrrގdN[fI$AG@uu5͛GaߎO?48&q" H6s҆%$@0ŋydN+<˫" HOaa”)S ;nd@ $;e˖ deeaѢEزe`&7[AP" $4$}. h4(ĵ^;"@ٮ" 0#eYvDor8z(vu)S ''SLAii)JKKS()))SxO5xbd8;kA" d!\eH zaNl۶ }?m4Qﷷ=R~kF#fϞ b)I1w}ABJS4:xj$fcǰm6l߾]}Ʉjaԩ(//r!ףGAGGƶm۰m6cҥ7AY8(E( 8Upq8~x{٘3g-ZǏ`ttt/ѣGwިj*B)} Q H!6 B_~9AՅ/Q`4qc…())9ى{?2Meee;̙3St:PJ)!O2~$-n7>#ۢX|9f̘2!'|'NWVVCVVy.$ڸF w#DpaٰǏ?n~_FF.]K.$eĉxD VXq1׋;w\ |AL8$<@a娬Lį%9BlHKKCII 9 p7cI;x饗{^Ef60x[AݻwOXr%l6[B|8s N:3gΠo̹Ũq㐝y>7oѣG}7MčfK$]΋edd@+ڽ{7׿'M+@ش 8vއ^Gii)1~x#==%?Eoo/ڊFx^477`Z1ydL4 %=,Zyyy|ݻweYB#mhDHXf{_|6l^`A\ѣGq 8ΘǦaɘ1c @Qh9"(JDV,˂aѺDZcǢLMF^^^1ƦM1y a 5\k&j={+4 fϞ СC8}t_ji)/^24/* EA妹"G~|"P(P(zl޼9lQVV{zzuVQEEӃ񠥥% `0 ## G}===qV@hIbʕX`hߗ_~o< ,\P1*ԩS"z<(//JJMӰl(//GQQ2pBgg'yaȑA(ӧe !-- 5551~[n}s~AӧOԩS8uZZZEzz: QXX ٢#? M"V@hì < џ>}< @VK/Ut ={GA kxP0 sѱj7n쵂 v؁`0X|9.ݻGMH+-BX[ gFUUF~㏇rV@ )!!VO<x<~ &:VQ__ X3oP05%JN_~N:W^y%ʬb0qDkq>P(|ۋ{bǎqM>UUUFNNrss1u!kRhhh#G [ .==_~9f͚5ZO>d8_A"CE~+ցQ!}`gΜIL^^^fh4cQO!Vlٲv@N?؈/CrDe.Bǟa} D0V0,-(ca׮]شibD ߏ555kaX`2xbQA"$m~ݻ`ӦM8p@̌ɓ]vڅ͛7+yjjjRkWW+l޼Yݣq饗+2ttt j5l6 ~?_իWݻwc֭"-^z Dz=V\I&%E/Hq"9PTZ@ -))whZh4~QʂbANN233yl,BN@(刀T~y2&Y2:n7|>`ɒ%?=z^[g8/ZhX'L?"QKK l?;͓ZW"^Fc hŰ4O& 0 0 l6+-jLLAkArD eY࣏>B?})`(OСC裏XV̛7/nP0Dww7Fٰli{A0N7MY;&Qa4IūCt:/ }>_$-W^y%鐑Jax/$i(tA2r_X^GVV`4RD}9,˂(]{{;N NCaƍCO QDss3;CٳJ@}}}hooGcc#Ӄ`0F#{EѠ}}}p\8z(L&rrrdǿG"RF@BrLsPNhCee%.t:C׃(^ș@XqihZdeeMלÇ㫯B Ev}!Z-_  R0qDzMP]]]Q4vzq94npg&333 |kV6 zǎعsH><4RV2#Ch0|D5UVAVl6CAV@,PռG àZ)w@zIL2hii 7܀'|RDEǕt(..M"gmǏ%dVG}}=v;А09B!ttt7edd"ʂJBkk+{=,f<"ˆG q—'%KL&/9sEQ iB6 EQZ01$jj?D?oߎ> rFA^^n7z{{iʖbʔ)}ա.*!X,l6Q2NJkDdy%6[,"h'Ig"77> oO<|>~/$XI 4PSHOOE%jsZ'N`|B'wghZv'uq33f`̙FHR"fsiGOO pHzA^/o[d __F$Y(M\s OjE3H~,$ rb ގԹ<҂V,]4fZsa@ qNKKŋ1sLM8nC@cirގ >7ꫯW\}a rPTBlD /H`-F`QRR+z gP(I,[~?B4MBdOI{ZhlljE}}lnҒ!K⦛nBQQ.0Oa2r7nj5?χfC[[[фmC^kc߾}1n!1F^LI5eJ>ss‰s& (pf+A~ņ6rZNnj-?1,Zx?~HˑG^hllbi>GzDFF^/GMM j|HĉI0&D0 究`5| J0ZH-(4}>_T΁<$3j!i !o}[`>gvinrA&7^mՊR! tǏGee%Μ9pz"dpprB1}+V ;;[ H2 "DFQH3ZHJǃ>X,(9j5^/Z[[$cVZZ3fTTTUFER"n"H'O擗L>CI nAE;f4M'Wxɒ%Vɘ| 9GtaɻeAAOꄻ)11g)0)AIFdIͦr\zdhD @nnnCFu D+ƫ1$ԶbB!tvv";;[ri"peA7!3 t(4)p2K3#XQYY>l6VZ%It. }s+ip\/X=ciEEE(,,gժU=Ȥ2JRJJRJi ɚc WXXŋcѢE%'(cjS劂;V2QWJd,(/a/q"ZJBee%݋P(rTVVc E\W-.Q+Q3yQv}'vS<[ֆP(L̝;{!9Biv9DA4`Y'uN:YfEhvZTVV둝 eH*9ghMMW_}n^ҥKa4cO { ժYPk*,,ĉ'@4ϟDPҤ!em"EqۜYnHN;wbݺu?P^^|rtuu??σeYôiڊgyfy'scc#^z%=܃;C? %9*r=%~? %b̄`ൂiӦСCN` 777ap?7L 8f޹?!(뭷믿UVRJӧO ={`x_:?я~z<#ڵkծh)I@ <T*jjjRJ/}x@c4 h<(9/4P^{ ]]]Qxr /҅=m6Ə=6l؀VG-V?Oر>hX'۸'Y/wDl2qKzz:Z^!BjqCNdlco~tYA?)&XV\veرc_(f򽽽ذa?6+W]JJJ/cƍxWα0#Vux_p[&" Hz~"JR m۶Kьֆwy'&|? eygr2iƫ J\رco+?}Y_St4ʭ" E(Jg"V/?bعsgL"0B|{F+--EQGcc#t:]ܰaN<y0x'dx&#DKo*dllb"D@"HKK X~)) մO?%f /]]]DŮ]x?CZZoNi;v@ZZhF͡"8N~ؼy3N/eH3uvcY4M>K`D Arū#4]pQ9‰'0a_|-[pa̙3,E4|>=RŦo&zϟ:u*N'>Sp tZ/J N ^VB)d5qL&TkPPP^|8r^zY򬫫dFpτ۷&ٳg'h4Bj0FQs^_J?%!z>">.,gC<:rΞ=Ǐ+)f99rqQlݺ---{NB!Q:%m@(0( LCR $nZ\T2 W9R)u෹!`BSSN<ݻw.JӉ^p"?JB!~oNPTZC6XN[8R+JŌjYeBզ4i\.?[.++˲8w,"//n!wee!Rrx"#%pD ʁkwy1w\8NQ I.w(3unv˙JKKa(**4b\#D4yHHuxD'D@cSS #MFIPҒ|><4n8XA0 x3ڹsP\\NL2eP1k,Z>QLTP,>w\B.fm1 :::o]Ɣ4T VX+(pb!yeYƍK3jkkcbF$|Drǧ!BiP{{;Bȵ&  /^cƌI]rDpB!Ԡj_f͚?#??L%fJRD'D@0Zp`r*t8{,of͚5+ᱹx0󷣣;w.:;;|sI/q}é$3ET"H555! XfI>'lZ;p1VT?~<.䒸Ip(--0hllzðX,?>93gDmmm̊XtiP>9p"aСC4t jzw*ޘhDEEz=F#x6nʤhI,"??Fss3DfCyy9 ^/h`Zzq뭷.݋Vga(++сbX,`ԩEYY*++ `_ /)k:/O .\ `0M6S|D"X T1˅9s 3) p9A$-S-|@̾#I,bxga6aDV &`ԩ:u*Lsaƌ3g/(`XJlݺ%%%bЀG7o^L **I<" FVG(pT ~"B7PQQш4꘳ $JB0*EA-Gj䎉WG(2r#ZfP}}}vo6|AD! Gb|Rh882iQ,Aj+ʂ^۷of`;|0 eYjbƌjؽ{7݋3gĉؽ{7Μ9ٳgGԩScu㍋pDWi  ƛmۆSNo8(<(2CF/R8j__`61ah4j~-\ H)*7C0H~O<: $FOOT*/^%K 1{t:}ΆFNCQQNFn%$u KM\'j\#^ TVVيu7oޜP%\7Nd2eP2 #F\.5CB(rn nM6s ( t ,$fY,%:ǡןNoz*@Q%Zarj3F#;~rc'Grih" v vܨj<NI&h46n%?A,g)I$TlrZGgð^O (ZSD_R9z֠_U./]):K!5"DgJp9M$ x "oL<|K.̙3b3"H ʝb pkkL]0vCMó~z@SW2A6EAc Jc@h?6DY 10AKu=rm H7JDt:.F) yF!SG ZAyyyTG r@ٺn#`8R{EaL\rPj#SM ڴEC&JůlEqǨ@A!:z?w';HH|L6phDHW+PHd $Oiz)vwww sQL3"OpyQEK.bĉi.44V^Ty^XD q't쟢Ye΅T> )Z,9?5Tǁ=7"i f>Օpi.բ[#FCgg'E"h"Dp>҉dJtXz5, /"F Vi R웬D:`;} .GMg_ mL 9Yp_G }J(i~ hd0@*պwr5Ȁe?.WK[Dc&L*Er"EBPOOz{{E?.[si W:UUUX|9Z-&N $'%P(˅`0H||*V@4@kf͆>kԦqaLӠh贊'Z`@#\J-!ky{wp]/"0@5Znr@eՈgԤ&1p8'Oo߾L'H$lpXQ\EEEHKK+*,fEAՂeYPF0vn@ƹ_R Иsa(۠U֚#QAlx t5E^ށ}bmȱYkBLPK`ʹ҇2#L/l¢0Cn!V-kOL}}}8wxp0DCCxT4X5QΗ-2o4fTUUiB!XVY/@#ܖ`j5Z-?#e8`ڀGa~ɛiiu0/ʘ'k(xaSDSRΉLBBO@tZԦ`C}y ߇M)<+@J<|>ߏ?[lM/"D0ҰaE 󡿿&LM0qJBBV##a -!Jz+tSAk- x%bڅd fB-rŸku0}ZMz@Q#s,XuRaxoсnQC~޽7nĹs3 JGBH_M$HΕ8UnC{p5 9*ή! eYx<^+ b׮]8uXq'"D0ao} h4(--EfffReų, : ?@}$AcX y2hIDUQQPDh\TPKCMeCSC7 p;ke3 q$`܌}pСnI)!Ũ Nn dLY*辽0:cXh@5/ Zm4c(QBRDed2H?L @L Q2HWӵ-~@"pBLss3Μ9'O^_F$ 9@6 BZ[[3 N^ J-"*"(WrBTfftn@2g(;GNj TvD.!ZgP!Ý"m@J\XłK.Fv@CYh(B8~8 0 v;\.L& k!sNjp7@iy4;@Tt Q=fBP5H 4f@dA4 \yOopP\ "N+AZJDy IDATy9ACYhpdp)L:,+~?z}!) gE;3hi3H {J{@]{ Cн#TVGjqڀ/2~XkX~Q+rΔ5(U/^ ǃӧO!bJxQVVV ׋`0'3q91i:8( 0Bn^kx2XyHV+Dv #X3zݻAѦ0!HJ?G,Xm/5@5}`S+bzCHip[Ϙ18x 1"$0b5557deej5L&un7zzz7HA ˲𫋐x;\fAk>RJi"_G -6 K B:@kLJD׈6PikpZ^o@ h@6P>j/Jd [/ѣGa/D"*FM7݄|;h4%Z-F#\=np}|>h`}0aBANj3JF;!OJ=6dGлTPi!V8(/)/ jyM,w`kr1?JA" MӰZXp!v;.Gzg(#D0tIɄիWcٲev⒘Z-f3̈́ P^p8FWW.g!_s !w L׋9% a|)ġ@1CRlJ;!e X~XA )z,=oz[Jځ`e]ٌ{^ _}DD*"4 _/x1k֬(/%kV N; LX% p&u넱j?rP4@ǀ q CR ؉ TRPIR:4З@#5 `sۏ/@\c9LE՘0a?歝ND!da*X]!)3 ???& (i\hF^hu*+̗JuhW`J}|7"i !$,N<$"=q$牓ʀg/+ N XH g?<]; d`?E@<@lO.Ǐ'\ d0 KD"H`U+[F")pIܢVR²**J6 =bjKheЦUE of4y(?yH";Je <֬ yLonkr⚂bĥdggcɒ%ؿ?^p1:ҽA(\i>rp |>\4x {n342hd@ L) TLi$t"2{!v,J[>`LEpYOpyw=?*i$}_(/pl6,]vd0?&cB E,믿?J%JMh9PXsn3 6dj π(IAN'lYB=Pk+E&",2`"dvm%Ap]3ӢG(v~9hTTS^{-v1 VG t+\#;.>HZ=E0Qvdn« pf"}I)D#v!7hm Re~ov86݀s1|b p9{q˖-5V5H!؊1 ^j~_ t9"儾 $Jrۍ jaQwWAˑ*ؠ,_T ǩhuVG$5%bꉥ:G /_m۶Վg cB Qq 7رcctMo~#k ee&Rrsđh{!Vl=: oNo i4&L:Nc8%(ZP*AAIh=-pߋ^w>  |N;PHdZ4م#>lL} H1!| oժUG}}R7|3|ɸFI6 ,"Ī(F8@|;a*-iM&`Q>E |( &j԰g~`8/ [[P[Bjk4l891X23I`֭c .\ߏv=¹G@h-77?ώ*SL r9A},@me E!_'ؐe"GM& DĽc(A,X&ؠ,B)h,ؠM/!nϯö{\aXǤ?70^p^~n1 XbŘ!f'?@ 0  ,D8\?OpIl߾}*`Jy/4׌2s=:0h2 B @) Hh! 9 v<(țM/#n>.q"M@TDRST'(i`ŋkݖ˅E9F.^ %;Q[[ 6eT)Պ͛7f)μH!0j J&#{r"b}C(4 X yWɓL0nqEEo ɀ'B C`$\+9LBJxxdŋ7dVq7z^3"3@oo/?zx뭷P\\FRNF+'lVID$O!!PBaG *Mm&(u+"׀0bH BL J_>Ck&Z-Z"- UxdD}0a|Q5\ӧcy2xŇ/++m6Ke]eΰ|Bɞ,N4D7Rs{8d@e}Q6 8_4w7 p {jEf 92P’=JY,H4LG+/62P]d$"YfI୷BooR6 7nDnn] ?f~4|TX+`BLϪ>Eɀ"ˑ+n- $B;@LdiuY"ϭmZ-ږ B^. 3]dD#GcxWF@(qUoJ],ْlcز!aۄ &84|<1Ii $ 9iϗ6ϩh| izӄԄrI `Al1߰d˺K1Zk]G53{uްd khAk@OO~{'pWH:7VɫNC> 1%.z #w [a ASI$8@p >\SɇB!B!7 JTN/Ku{ۭ1`nlLg n +ftuuرcpW Fk.|>|_ZNDMT1ٹ,Udy0s#>\Xy` WTپ03B,-_Zhnâ7NF۫WƎ;^PgddMMMD"sUW`.0@`w܁@`xx/2 .._B;mv'0(Sy3g{`-ʴ>ԕuCEr#D.SP.gg mOku&8 .MTgǁ8s-0 Ԑ0`A@E [ȗ[nO/~B /PP_,੧ٳX}nAm4ϪiC"`0C= EMi9J``NC6s \eS%O 8  "B^0/^~.2*&0|_ >C:t=r-"1T[Z-DO(6 %`=F:=' F} @ A@r8:,Ee `}'?颢.ND7mƂr=s`î] f/q\xBE L[q=HA`hhv*(Z[lQZn"绱y27^L~u!WFG+#X}y|deBjMEz so0kX{le16*=<^4蚎WZ~}}3 2c̙R7]Apm)A`ppv*ӑH۶m3ɹQNPrI!u];T*#rzkqь#h) -ʀ 7 JAQԈ%Kz:pw~-r33HU L ܂A.*. O>d(D"y)/bL' xDA<:u ,/ovM۸QNkű*n`x*paqRgeNpl,>@v0Ⱦ6 !hJhL3Ձh,S5loA.MTU)\ "Ƽy-P80Ytx ѣGq˗/~aT(ֱ\b n>(6~p=ChqfA(YϬg=^A4|GQQ396H,*sb{ĠNUc>6`(v(mQTtH&;wnAycMMMXz5v v8^@(VoQQQQt l4MC}}YeQ,3PY|'T : ;\l *CMj_>:;;6a & N u]"ۿ[7PVV {hˬsh` #A_ќa7*(J@ .TWW^(H-A_۷H,C:h•dm/;>(VXd29*-&r"Y~=>OZ@.VxS8A6ͥڗUMS WQB c̙سg::: t?Ea׮]#UD IDATb:@($---xG0k֬Q@OOOIkk+-u|`A u/Sk'@=T8P@x$ #_.Xz+Hը؈nCwww&a&D 5?ނ̅&˗/?y qc $R)466Eh4jzEk+x< DO 0A ?яP[[;j8s̔pV/aɒ%g̘a *N.]H)}-(2Ti+6bb8sADQR'kjjpW`p1yx9N =JKKGhoo?P*L'ЉQFnDVM+b݀U fϞgyfZ|X,6F誱.tf/ׯڵkm̜9)` X20֨2 Rӻ)蠱Xskb޽70k s8}4N8QAa-t(O {L-ğS*JD($se^r 8B:ă|k׮śo9n`0@0 ӃGܹsnȂ "r!QN M[ YZ\. D*\ON;#D, BиXA%11gMBDžǦKx`1dQN韢 U<244d 8|pr#GYYcmdX4&cǎ*˟Fxl֬Y&XOu>A\=wgx `X,f]39NΝ;gνJ`0Z zzzp:uh{WzHY»J{ynb#[3Za06 x3`ҥyt [ǫ&mAREHPIP;n'y"H1x|f$B97+//Ǜo 3^Dmr:K*}kkr{-J$EaO+8(fq^ G둸QQLKKKQرcV^9s܍7xIvq Zn6nܘ޽{B~Z444uuuDkk^`y)f73eAUyf4C ;ىgϢNi'ɼDGGkm=S-ZŋaJ`^Zy<LɠFsʫ<l6իDi]] O;zW0#OO:yy&-p9s @X/^+W:Z9s ***\g-Z8rH~G@ҥK|f@---&? {3{<)K e㖧@wmWB#0dK{_zW7o^ `E hiiʕ+q! mgp}a޽\4]#ym'D8pe\mZL9<OVm{СC?hk7P0dcFHeee(--EII/ ! !le"/__N8[lmmm}u*/p7ހayI,}z* Z[[!͛7ƀM܌]vVI=Acc#.?7-T#xW_}5/wy~a_. aݺuQWW';@+ nkk;g.҄KKK!cO<:&3Na+t>lkkuKKVUc'̝;˗/u !טeK/ahh *T@p-eeew`0N`Ϟ=yUW]n sU?wmmmw)M̊qA=X.vݻw>}LKKF2TL~|r ]pTVV,Z/A=f = 6l@uu577Ez í?+hRG=)v)))|w[2FOp8o+W/׳sNWǰvZKUOn08o`{YY@UW^y%_[[kעAEmimm]I 㩧rʭ5OYѨ3޽{'+`ݱ|1#0sm3g4FLZ\r%ԊhamW,\?a ? f5k9|ׄ"ihh 7ܠڎt`<@'eN.ܦW'rijj²qm۶}߼yuƍގ{]400]vaժU*++q%ޫB:gG8M6!+A`׮]rEY֭=o:yxWѦM\ 6QxI1rVɋ/8i'pLz[+h``Rzy>oqdKJJPWWCԩSw H}}Yx26{P%3?ؤw*Ɣ̙36,X`H.-T p|.7߬2^I_z"dҥJG yRb"CYY3{[3^y8x {9eu[oZzZ4Eu]@OO}]i; Ei,O<Ԅ}M8*/"oI75p޽vB*tR̙3H]g(~x MMME "~܌J4OR8rdm7@gg'ΝadCxuPYYlժUJT*ł֌'`|%>XA >mi{kk^LRˆ1rM&ēKNTбB]2cۼyse$Y{c;7}̵lmm-uPT6 A-O(J@+57.v2'cx≧Şx t!پbK#O{"x2'4o fܹs1s ̍zIQ>1w{12= <1!;w7n9rE=$GIR100yxPZqQ~xqܹ_hhh(o8xW“EO}Sعs'V<UpQa~5kք}]׽Iъ}%o!1z`pqa|s00(O #-:u 60g~\|8v]UW]3f~`л   :::__9u@!YXLUocŊp8xqa׋:K!5,Yu֙cK-mGţqIӊذa,Y2noQCs W_}5֯_oaB>Vo~y@eBpM7K/u|Gz@'c k7x8ϼ_>+5dH68p---pM7?E3ë$<e a4MGkv,#O<)6o B[q7*iصk2Bbp/pgg'Ja7|ȑ#Xd rzC]QZZ$ y{;c~`s /A& O9s&.R̙3'6iIqȈӧl2e<_yE>1 :::\yF%rUU***r~@ ϻ,DQ1\7{0駟♞j+طoEHɩI+مO)\?,Kr-W_]=)Td,3! 24>Ch{qvG0^Aoo/IPJZL{yRi+7;wȈ x@0.^A/l߾x1mMӴRjBn\__ondwpoǎÞ={lWEZ`qnm###xq+OFec01ցc3kPz#,\vR2D,Y"n.]בL&\?'[IfYsEǭ*Xxh"d4&|vR⢵adɮ9رgΜ1n [>!===/~a!]<= 0`$."˂%@4 -`npFW)א){稀)$ c" .N =_WѵV b=X7@D)5luV;w8m=uQC)e;vK/ESSeF9`q8n7s,hY\dݩ1YXIɠFȮe'F(UYusNR@^cl%4jz 4"Po"2|ccIq%%%[ p~HN6< *,(_g8yQ,:Q) 7ޗ hD;AӧOSX=(P") 5k3'H1_kOc*oxsPr\=kA' 4" 'CvFScFuZ)bZS'e-[uKqH$ ?![8 1*njY[oa͉L"+0'@WzC(JR'܇iUoz148J.@ %H$H Hqc|,q6Ka :.J;uFd"/;{l^R:('D7BAҝ|ѲF\5Fu"Z F@̃NJ1/6?7 T * [=0"z*RZQ\aCE4 Ğ={ %Aa̓AmIT?Bv϶E8pVSvojz(@ܱA4a, þEA2 ؿ؂]k7T7? ZUů>gJ}?"qe7kt) 0!#uH XW:/y8[^dDZnǖ ƌr6mUa Cp2ҤXOAz =A,BZ3V樠y/=. @ށ,v ls_^˨"~]Dp3ȁ4XeכM^8/Oy ʁ1(ad=χ/W"~F`0F2=k`ޒbgx ` )4Ձz)fҜ@_c'V"H]e}x۩TJG8XvFD;co*8#15D a;G@[Rc`ve^PlmW2m=4 d kp,-G 2% 1J3$_Q&xfq.KmN)<`e<@5l U4xWSk0ʜW{?Ze΢դb2ݐ:1P@ p#Mc# tc%|ׁjRoW2oDx|*SEcF0o%]C*~wY~o<yy1?Zؘ5I 4b+(&#CKN+A##;OO 3 Yof 'U@ֱSA6W—Ze^AXVk|jZTtƭ?>8{6*PRA0Ҍ@I#JZW](؏D8b}`kD}홷m;^ x@0?:0m1ܪM$8 <ބ比rԠHEcyE #=G|@ 23ϾȥΊ8U[:ܹɑ?BҜ̓ @d}'~Bb%tͮɭy@;g=#K/Hǐ]>+(}8)^"m A؃3PmN@)z|fa[;ۈć*eRBQ2%\`Q^nJ/X|8%L$>iIO_ 6%f`z3Ig22Q|FPLC13% XbAa^c'K$sbaw`kxG?t\w))vAćMghl @,Hhآ@ p ,S*j7(eރr!#};ޚ hPހt>fxIu%>.,Yn F6*o5\~\WfM eGMi3V?$a@볧qsҙ$Qy}Zz(8-3vzX1i7ttbG2qz@PP1ـ2 @vVf* l5;iɩ5hx yf#՟'=GhjHgzŰ4x_ʂmփ[M06V h!W. >9w4YMgAfrULcA₿DuRkbB&bg[KjPh"Ay+HŞA [/w{@0A@va '6 s{ƅFiM_RjmlQ1ba,A,V({jtT"z=%R!yҊcQk"6xsUO<ܦyO@<-H)җ p  aQ Q.J$*b[| %3nt,Q=@@E#PEC8# 2mPƧh܌ֺYWbfPY\@Y WUT8JIQQ?gBui'u EbԐ&1H@*.jS>2j( .4b@EFĹk5-]QZ}+f~ּm[Y.t89/~峒gY˂ܾ@%uvλ+b3Lg< $ذU(]*{ѥr s(~|=H' 2>nD*~q}OS3{΋f07o=3hГ$زx T*:~,.mQAB _ojHT/a+e.h#O@{RN񋀃ZyHK @b e_~O-%E_$>l|۽"\w_Y_grynH|0z> =e 2("C 6FdoǾ8X[ *('[I`T㸢ǟ]GtQ&.qiHG@RT_YN9A^M{ Yf,YG a*)J㝯!=+\~niM9,%?㦳]n;ӂ#2.c4LSu吢|΁8VP'gilV<֓VKSK@wB'%L">cH>j41bX?.xFU-%uj2{g]{ Ku̟imn9 F/4 HZR@ KM XQ^(U =P:"]/'1te̦Y:J5Nj!zX,S;p4ɴb2 2*Y%#`KYt!D3-Py`q5 J? $(*ɞ$-gsĺ(7P&&xl ij)@ C00H6m9e> u*oT@k\<wA0BE#Vߌ>0?3pL3.hFm*[mPLDxZK0=\| 碋Зj4QD3 @6s&jxo&|Ref ϵ RCe^5 y{ߦĉ f HMyM1à`,oM˷Z*foD{<0k 1]MK x'ૅ-6vJ*[+֮,r*21-w n(*@Tcj "5rJn`y CzEY0((pqy"t^l8ʷ u M*~f\)X[<HĸJ,kͬa\nTֹpSG/n D4E:(œ]0~va;A5l'/Wv4\{ę&ͣ&60n\h=hh x@0/B|BiyV߅h@2&kG pUV59;&~~NϣRVK+E(Jp LPCdёq6USCΣl|}d*sK?Sύ3x!%YoI׃)@"p@TJ)@’ EDmTmq7c@b7\7ݲdb ^ i](\Q<QU`RlDuGyf+3375_9|H(-F!Rg~ 4Q&֛0f3y=XZ)Rb72d M4?458'Xt+_l@<\Q~s*u{g$n!Cor*^S5{,p<Z_%&|,%S1&tNA^Pq2 ai/XovL\?9¡+kH PxǒW#J0Ll`ϐ 6%}lW n +E/$:Ϯah k"D 11%DX65s){&DĜhHY3ݼia[Ow 6thE璮))y!# (ReP p>JŀNV3f@0R>'3tdC:H]cR>Y`i#J)Z2xEZ" ih* -[s):A8}J< GPPN1~n*3^{Fv[7pHf dl$ξLw j#b?L%\c+MڇRB㲠0KЛ D3-Qc_x=5S!D <%O+Q|Qew#?`8:e<_"{x}e64N l"y9&]z6ӂpV@" {: O)iU2]Dm?p~  FfۆdgeOwz-Zb 085`0=  2kw}f o#nb#&P7KQD;Xl4+jrh2) Q51c;yb|%IDATԞ&EU\ѬIakc+M]T,,^+>1Fg=M7}O80gnSԣxUE!Xz֋nq "Ƕc. Tb:(/cPE"gy *<)w0bԲXI*祔8N`K e13ЉXGgK>cy)&SEkk~=:BY˦ =4]< DdAƬ`&ӧ7Uq* rZC:x:( e^tmp`90M~C bu1:z g62ވeuF V3y(ZyIGRGf؆@*vFjvc!:gYO$d#,Ӛ=QA*v dwq eS4 8T#9)o!~z?/_âXA='T^=f:PCUd(v=%G: PY4- v:j*`KFT@=?%cZ;O늎 $;nTx2^˝Rl1Ք5-LlʃA3r+}q~b?_,CWO X'62%{,ꅦ9S1 @baVP1C*HL!qI|+*}{A^㰉(jb1#-#0M* ǂZ|Td4_IHE9F>eNqH5r(>,^ydy@0B.㊺A=}r?6fB@mz= |wעt> P7 G0r4-l[,FK*}$0 RT*"Uғ6RTtɹ xP1aԚA fʟ=4DHQBz "K#0{*!~F';l9 : ˰-D`|`vAQIYH"V3rWI˿}{~\bh01ukڜaP$>#KASE!욗OwS8aԐػGuq]n|ILz,;PAe-{¼HRT@X؜wH}ʞ}PSx禕^lI 霃UHzC EPM'P6g;>!ĠŠ ?HJףS@ :6" F֙XH8kCH F JXQMZ.,V~V "+L9Ts*NRxgǠ(p6: ~7K&ŔRtZTYԳjcizBv1ڭ!$"P@Mkvux$7֒zp/({v9ᏐuX=SCܺԐK.6hfC2Ie-5%klL*vTϷTmqITN{v%fAuTSuQǠK~t 5^ K4"ER*ȘW̃5j¢r־"§\R!MT6NtQwR@:Ø*c1g@m3Rbgwi#]at;AؖAdr r,=x:qt$1ԞjPB|J)ߍga(AD>s >zdg3C`%{SX^p"a?yP]Gdf *te bׁ+k:[RSHެ~)11.CC?8@ :!|8y!b H&°&`Tʶx70%ASS1~RD*~t09 ='UwSY`݀`PnSZƺ2v$?fYE# e |>iphV^e #ΤeV)!kO{; B% oYS5I@O _b -xB4 e@>?+U8n<-i%@=nVz~\ %feTsL sV?_ /)ֈ6DEsQ:h.ށ`z#%2 :P*pJ 9rc.[Y;4ӣr@9:HnTؑ3xǿ 4_j˸}}K,LxjŶ Wx74D$(Yx"kJߦ:?#}_d%1PUeh`xu~| |0זghO!ָ嶙m@"y |BŧDQ:BubXbxAeH 'slv4 8?zϾwf~GN)Klaƾ`RpDC(>{W*Uܛi6V5iKyP" "EࣂdADZ}(AQ[m )[Z&hch~Of^>̜Yk}^IsgΜsY{}k;hI07UP6'(Ѽ;'%:Jx:0x.A\Pc) @8!h_,-nߊhONlݸo ZI#D%KC\% % lmT.}B҆P֮?g^\rUat6OReEPwϑ&{4h#tS!er_Gg[n߂OgvB2#iޕe~W6AMRe?-:ONp|Lu٢vn h\C,п0ԣ_V-L2+~[xL9\b\i+;h4 y% įچƗ߈u#>Qr>XU/up%DTQ2r[*2*lU+Ll>)~O}*|nQ;o> M*m|=u,bE>/r$qTlDb٧"UTOe+ \.2R4[e,@Y<}x,$4`[mG X`5 z۰u#0y?u,eTkMSWnFk'Z.],>y|Mϲԑ!h Q+%Eb ukAGu/|8li:hK;;]{'>rCU' >b0q^ @ZfԿe/"rTD{Nzј|G{3_@P20(X=xz 0(b`xǛOs܄cVD5'sK7&&%9yr$] $`%#ďS5` |!lj} hKHt qǯ(!>8<јGB["_>N0P$xO ,u@P p Dؿ[?!ܿ6'(5sz8g0P?ѹl!ہP7Nu5&)43r a!aIxyC iBF`m{&y߹ C}=p>SiDH;y΍o׀=XtҎ]sPHf1g;' go1Raն$ loPH2߻~x]o )EԹ]ZKG7\.rPoIz:Ӱ3&8> g=|(N=9$*|2@sʱ߻܃}{ނ}7Z8'#{#rwd&.HhCy; X>q:$i@/l]ǹNءz0 P9Uj66cPDMyHcqÎ{-q֛p; 0˙o 4xt6ޓLvN"S*fE-1<μv/={G_*ԾVy-6_ۻ[Ʈc[+;j(~lvNOYHa"yn3&b`CЩӫk?^8jcRD@  H{ygԝbVASG̥fdD9ϛV|Rnenc9%Bݓy@9@0dW}~Ae?y9>D!, Ӏ@\HjA yQAHyW.+s)[&ISWk9x8|`K"p@%y€)I( > h eQgce\kAd D)XUW-,|gYȊrk7@uĀaImng5mI$R@jJ*Ge!Tdd]`7fh"Md\`wѾK?!}u66}I6%C9R2YNeK*31p?"gc97j"@񧪆02) rtm,6fq8@6S2u x́IP܀3@\AHSI0hR5˚D@ Y` W`NX"iAaZ0RJ+mci0$(l kn6Mt\I蚬53Pkh0-ll:K+!LDq|An^?, BDbc@|I ?z@cD (WGK TYbHMDSf)7@7@hv8 hDHH !+Ծ:knc}YRa 9k n[V Ez oR %b*:?P)c;8%i@~V3ed1,l8!D0CofX!X% H}h@Id&%RMY@7Yqm,BiiriT!GKQ Y s^4Ɛy RQH n1a9nߐ-hRдUB-obWF[yB IENDB`sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/text/000077500000000000000000000000001320103431700247575ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/text/BidiMirroring.txt000066400000000000000000000601631320103431700302660ustar00rootroot00000000000000# BidiMirroring-8.0.0.txt # Date: 2015-01-20, 18:30:00 GMT [KW, LI] # # Bidi_Mirroring_Glyph Property # # This file is an informative contributory data file in the # Unicode Character Database. # # Copyright (c) 1991-2015 Unicode, Inc. # For terms of use, see http://www.unicode.org/terms_of_use.html # # This data file lists characters that have the Bidi_Mirrored=Yes property # value, for which there is another Unicode character that typically has a glyph # that is the mirror image of the original character's glyph. # # The repertoire covered by the file is Unicode 8.0.0. # # The file contains a list of lines with mappings from one code point # to another one for character-based mirroring. # Note that for "real" mirroring, a rendering engine needs to select # appropriate alternative glyphs, and that many Unicode characters do not # have a mirror-image Unicode character. # # Each mapping line contains two fields, separated by a semicolon (';'). # Each of the two fields contains a code point represented as a # variable-length hexadecimal value with 4 to 6 digits. # A comment indicates where the characters are "BEST FIT" mirroring. # # Code points for which Bidi_Mirrored=Yes, but for which no appropriate # characters exist with mirrored glyphs, are # listed as comments at the end of the file. # # Formally, the default value of the Bidi_Mirroring_Glyph property # for each code point is , unless a mapping to # some other character is specified in this data file. When a code # point has the default value for the Bidi_Mirroring_Glyph property, # that means that no other character exists whose glyph is suitable # for character-based mirroring. # # For information on bidi mirroring, see UAX #9: Unicode Bidirectional Algorithm, # at http://www.unicode.org/unicode/reports/tr9/ # # This file was originally created by Markus Scherer. # Extended for Unicode 3.2, 4.0, 4.1, 5.0, 5.1, 5.2, and 6.0 by Ken Whistler, # and for subsequent versions by Ken Whistler and Laurentiu Iancu. # # ############################################################ # # Property: Bidi_Mirroring_Glyph # # @missing: 0000..10FFFF; 0028; 0029 # LEFT PARENTHESIS 0029; 0028 # RIGHT PARENTHESIS 003C; 003E # LESS-THAN SIGN 003E; 003C # GREATER-THAN SIGN 005B; 005D # LEFT SQUARE BRACKET 005D; 005B # RIGHT SQUARE BRACKET 007B; 007D # LEFT CURLY BRACKET 007D; 007B # RIGHT CURLY BRACKET 00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK 00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK 0F3A; 0F3B # TIBETAN MARK GUG RTAGS GYON 0F3B; 0F3A # TIBETAN MARK GUG RTAGS GYAS 0F3C; 0F3D # TIBETAN MARK ANG KHANG GYON 0F3D; 0F3C # TIBETAN MARK ANG KHANG GYAS 169B; 169C # OGHAM FEATHER MARK 169C; 169B # OGHAM REVERSED FEATHER MARK 2039; 203A # SINGLE LEFT-POINTING ANGLE QUOTATION MARK 203A; 2039 # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 2045; 2046 # LEFT SQUARE BRACKET WITH QUILL 2046; 2045 # RIGHT SQUARE BRACKET WITH QUILL 207D; 207E # SUPERSCRIPT LEFT PARENTHESIS 207E; 207D # SUPERSCRIPT RIGHT PARENTHESIS 208D; 208E # SUBSCRIPT LEFT PARENTHESIS 208E; 208D # SUBSCRIPT RIGHT PARENTHESIS 2208; 220B # ELEMENT OF 2209; 220C # NOT AN ELEMENT OF 220A; 220D # SMALL ELEMENT OF 220B; 2208 # CONTAINS AS MEMBER 220C; 2209 # DOES NOT CONTAIN AS MEMBER 220D; 220A # SMALL CONTAINS AS MEMBER 2215; 29F5 # DIVISION SLASH 223C; 223D # TILDE OPERATOR 223D; 223C # REVERSED TILDE 2243; 22CD # ASYMPTOTICALLY EQUAL TO 2252; 2253 # APPROXIMATELY EQUAL TO OR THE IMAGE OF 2253; 2252 # IMAGE OF OR APPROXIMATELY EQUAL TO 2254; 2255 # COLON EQUALS 2255; 2254 # EQUALS COLON 2264; 2265 # LESS-THAN OR EQUAL TO 2265; 2264 # GREATER-THAN OR EQUAL TO 2266; 2267 # LESS-THAN OVER EQUAL TO 2267; 2266 # GREATER-THAN OVER EQUAL TO 2268; 2269 # [BEST FIT] LESS-THAN BUT NOT EQUAL TO 2269; 2268 # [BEST FIT] GREATER-THAN BUT NOT EQUAL TO 226A; 226B # MUCH LESS-THAN 226B; 226A # MUCH GREATER-THAN 226E; 226F # [BEST FIT] NOT LESS-THAN 226F; 226E # [BEST FIT] NOT GREATER-THAN 2270; 2271 # [BEST FIT] NEITHER LESS-THAN NOR EQUAL TO 2271; 2270 # [BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO 2272; 2273 # [BEST FIT] LESS-THAN OR EQUIVALENT TO 2273; 2272 # [BEST FIT] GREATER-THAN OR EQUIVALENT TO 2274; 2275 # [BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO 2275; 2274 # [BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO 2276; 2277 # LESS-THAN OR GREATER-THAN 2277; 2276 # GREATER-THAN OR LESS-THAN 2278; 2279 # [BEST FIT] NEITHER LESS-THAN NOR GREATER-THAN 2279; 2278 # [BEST FIT] NEITHER GREATER-THAN NOR LESS-THAN 227A; 227B # PRECEDES 227B; 227A # SUCCEEDS 227C; 227D # PRECEDES OR EQUAL TO 227D; 227C # SUCCEEDS OR EQUAL TO 227E; 227F # [BEST FIT] PRECEDES OR EQUIVALENT TO 227F; 227E # [BEST FIT] SUCCEEDS OR EQUIVALENT TO 2280; 2281 # [BEST FIT] DOES NOT PRECEDE 2281; 2280 # [BEST FIT] DOES NOT SUCCEED 2282; 2283 # SUBSET OF 2283; 2282 # SUPERSET OF 2284; 2285 # [BEST FIT] NOT A SUBSET OF 2285; 2284 # [BEST FIT] NOT A SUPERSET OF 2286; 2287 # SUBSET OF OR EQUAL TO 2287; 2286 # SUPERSET OF OR EQUAL TO 2288; 2289 # [BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO 2289; 2288 # [BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO 228A; 228B # [BEST FIT] SUBSET OF WITH NOT EQUAL TO 228B; 228A # [BEST FIT] SUPERSET OF WITH NOT EQUAL TO 228F; 2290 # SQUARE IMAGE OF 2290; 228F # SQUARE ORIGINAL OF 2291; 2292 # SQUARE IMAGE OF OR EQUAL TO 2292; 2291 # SQUARE ORIGINAL OF OR EQUAL TO 2298; 29B8 # CIRCLED DIVISION SLASH 22A2; 22A3 # RIGHT TACK 22A3; 22A2 # LEFT TACK 22A6; 2ADE # ASSERTION 22A8; 2AE4 # TRUE 22A9; 2AE3 # FORCES 22AB; 2AE5 # DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE 22B0; 22B1 # PRECEDES UNDER RELATION 22B1; 22B0 # SUCCEEDS UNDER RELATION 22B2; 22B3 # NORMAL SUBGROUP OF 22B3; 22B2 # CONTAINS AS NORMAL SUBGROUP 22B4; 22B5 # NORMAL SUBGROUP OF OR EQUAL TO 22B5; 22B4 # CONTAINS AS NORMAL SUBGROUP OR EQUAL TO 22B6; 22B7 # ORIGINAL OF 22B7; 22B6 # IMAGE OF 22C9; 22CA # LEFT NORMAL FACTOR SEMIDIRECT PRODUCT 22CA; 22C9 # RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT 22CB; 22CC # LEFT SEMIDIRECT PRODUCT 22CC; 22CB # RIGHT SEMIDIRECT PRODUCT 22CD; 2243 # REVERSED TILDE EQUALS 22D0; 22D1 # DOUBLE SUBSET 22D1; 22D0 # DOUBLE SUPERSET 22D6; 22D7 # LESS-THAN WITH DOT 22D7; 22D6 # GREATER-THAN WITH DOT 22D8; 22D9 # VERY MUCH LESS-THAN 22D9; 22D8 # VERY MUCH GREATER-THAN 22DA; 22DB # LESS-THAN EQUAL TO OR GREATER-THAN 22DB; 22DA # GREATER-THAN EQUAL TO OR LESS-THAN 22DC; 22DD # EQUAL TO OR LESS-THAN 22DD; 22DC # EQUAL TO OR GREATER-THAN 22DE; 22DF # EQUAL TO OR PRECEDES 22DF; 22DE # EQUAL TO OR SUCCEEDS 22E0; 22E1 # [BEST FIT] DOES NOT PRECEDE OR EQUAL 22E1; 22E0 # [BEST FIT] DOES NOT SUCCEED OR EQUAL 22E2; 22E3 # [BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO 22E3; 22E2 # [BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO 22E4; 22E5 # [BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO 22E5; 22E4 # [BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO 22E6; 22E7 # [BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO 22E7; 22E6 # [BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO 22E8; 22E9 # [BEST FIT] PRECEDES BUT NOT EQUIVALENT TO 22E9; 22E8 # [BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO 22EA; 22EB # [BEST FIT] NOT NORMAL SUBGROUP OF 22EB; 22EA # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP 22EC; 22ED # [BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO 22ED; 22EC # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL 22F0; 22F1 # UP RIGHT DIAGONAL ELLIPSIS 22F1; 22F0 # DOWN RIGHT DIAGONAL ELLIPSIS 22F2; 22FA # ELEMENT OF WITH LONG HORIZONTAL STROKE 22F3; 22FB # ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE 22F4; 22FC # SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE 22F6; 22FD # ELEMENT OF WITH OVERBAR 22F7; 22FE # SMALL ELEMENT OF WITH OVERBAR 22FA; 22F2 # CONTAINS WITH LONG HORIZONTAL STROKE 22FB; 22F3 # CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE 22FC; 22F4 # SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE 22FD; 22F6 # CONTAINS WITH OVERBAR 22FE; 22F7 # SMALL CONTAINS WITH OVERBAR 2308; 2309 # LEFT CEILING 2309; 2308 # RIGHT CEILING 230A; 230B # LEFT FLOOR 230B; 230A # RIGHT FLOOR 2329; 232A # LEFT-POINTING ANGLE BRACKET 232A; 2329 # RIGHT-POINTING ANGLE BRACKET 2768; 2769 # MEDIUM LEFT PARENTHESIS ORNAMENT 2769; 2768 # MEDIUM RIGHT PARENTHESIS ORNAMENT 276A; 276B # MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT 276B; 276A # MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT 276C; 276D # MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT 276D; 276C # MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT 276E; 276F # HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT 276F; 276E # HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT 2770; 2771 # HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT 2771; 2770 # HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT 2772; 2773 # LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT 2773; 2772 # LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT 2774; 2775 # MEDIUM LEFT CURLY BRACKET ORNAMENT 2775; 2774 # MEDIUM RIGHT CURLY BRACKET ORNAMENT 27C3; 27C4 # OPEN SUBSET 27C4; 27C3 # OPEN SUPERSET 27C5; 27C6 # LEFT S-SHAPED BAG DELIMITER 27C6; 27C5 # RIGHT S-SHAPED BAG DELIMITER 27C8; 27C9 # REVERSE SOLIDUS PRECEDING SUBSET 27C9; 27C8 # SUPERSET PRECEDING SOLIDUS 27CB; 27CD # MATHEMATICAL RISING DIAGONAL 27CD; 27CB # MATHEMATICAL FALLING DIAGONAL 27D5; 27D6 # LEFT OUTER JOIN 27D6; 27D5 # RIGHT OUTER JOIN 27DD; 27DE # LONG RIGHT TACK 27DE; 27DD # LONG LEFT TACK 27E2; 27E3 # WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK 27E3; 27E2 # WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK 27E4; 27E5 # WHITE SQUARE WITH LEFTWARDS TICK 27E5; 27E4 # WHITE SQUARE WITH RIGHTWARDS TICK 27E6; 27E7 # MATHEMATICAL LEFT WHITE SQUARE BRACKET 27E7; 27E6 # MATHEMATICAL RIGHT WHITE SQUARE BRACKET 27E8; 27E9 # MATHEMATICAL LEFT ANGLE BRACKET 27E9; 27E8 # MATHEMATICAL RIGHT ANGLE BRACKET 27EA; 27EB # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET 27EB; 27EA # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET 27EC; 27ED # MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET 27ED; 27EC # MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET 27EE; 27EF # MATHEMATICAL LEFT FLATTENED PARENTHESIS 27EF; 27EE # MATHEMATICAL RIGHT FLATTENED PARENTHESIS 2983; 2984 # LEFT WHITE CURLY BRACKET 2984; 2983 # RIGHT WHITE CURLY BRACKET 2985; 2986 # LEFT WHITE PARENTHESIS 2986; 2985 # RIGHT WHITE PARENTHESIS 2987; 2988 # Z NOTATION LEFT IMAGE BRACKET 2988; 2987 # Z NOTATION RIGHT IMAGE BRACKET 2989; 298A # Z NOTATION LEFT BINDING BRACKET 298A; 2989 # Z NOTATION RIGHT BINDING BRACKET 298B; 298C # LEFT SQUARE BRACKET WITH UNDERBAR 298C; 298B # RIGHT SQUARE BRACKET WITH UNDERBAR 298D; 2990 # LEFT SQUARE BRACKET WITH TICK IN TOP CORNER 298E; 298F # RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER 298F; 298E # LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER 2990; 298D # RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER 2991; 2992 # LEFT ANGLE BRACKET WITH DOT 2992; 2991 # RIGHT ANGLE BRACKET WITH DOT 2993; 2994 # LEFT ARC LESS-THAN BRACKET 2994; 2993 # RIGHT ARC GREATER-THAN BRACKET 2995; 2996 # DOUBLE LEFT ARC GREATER-THAN BRACKET 2996; 2995 # DOUBLE RIGHT ARC LESS-THAN BRACKET 2997; 2998 # LEFT BLACK TORTOISE SHELL BRACKET 2998; 2997 # RIGHT BLACK TORTOISE SHELL BRACKET 29B8; 2298 # CIRCLED REVERSE SOLIDUS 29C0; 29C1 # CIRCLED LESS-THAN 29C1; 29C0 # CIRCLED GREATER-THAN 29C4; 29C5 # SQUARED RISING DIAGONAL SLASH 29C5; 29C4 # SQUARED FALLING DIAGONAL SLASH 29CF; 29D0 # LEFT TRIANGLE BESIDE VERTICAL BAR 29D0; 29CF # VERTICAL BAR BESIDE RIGHT TRIANGLE 29D1; 29D2 # BOWTIE WITH LEFT HALF BLACK 29D2; 29D1 # BOWTIE WITH RIGHT HALF BLACK 29D4; 29D5 # TIMES WITH LEFT HALF BLACK 29D5; 29D4 # TIMES WITH RIGHT HALF BLACK 29D8; 29D9 # LEFT WIGGLY FENCE 29D9; 29D8 # RIGHT WIGGLY FENCE 29DA; 29DB # LEFT DOUBLE WIGGLY FENCE 29DB; 29DA # RIGHT DOUBLE WIGGLY FENCE 29F5; 2215 # REVERSE SOLIDUS OPERATOR 29F8; 29F9 # BIG SOLIDUS 29F9; 29F8 # BIG REVERSE SOLIDUS 29FC; 29FD # LEFT-POINTING CURVED ANGLE BRACKET 29FD; 29FC # RIGHT-POINTING CURVED ANGLE BRACKET 2A2B; 2A2C # MINUS SIGN WITH FALLING DOTS 2A2C; 2A2B # MINUS SIGN WITH RISING DOTS 2A2D; 2A2E # PLUS SIGN IN LEFT HALF CIRCLE 2A2E; 2A2D # PLUS SIGN IN RIGHT HALF CIRCLE 2A34; 2A35 # MULTIPLICATION SIGN IN LEFT HALF CIRCLE 2A35; 2A34 # MULTIPLICATION SIGN IN RIGHT HALF CIRCLE 2A3C; 2A3D # INTERIOR PRODUCT 2A3D; 2A3C # RIGHTHAND INTERIOR PRODUCT 2A64; 2A65 # Z NOTATION DOMAIN ANTIRESTRICTION 2A65; 2A64 # Z NOTATION RANGE ANTIRESTRICTION 2A79; 2A7A # LESS-THAN WITH CIRCLE INSIDE 2A7A; 2A79 # GREATER-THAN WITH CIRCLE INSIDE 2A7D; 2A7E # LESS-THAN OR SLANTED EQUAL TO 2A7E; 2A7D # GREATER-THAN OR SLANTED EQUAL TO 2A7F; 2A80 # LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE 2A80; 2A7F # GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE 2A81; 2A82 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE 2A82; 2A81 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE 2A83; 2A84 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT 2A84; 2A83 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT 2A8B; 2A8C # LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN 2A8C; 2A8B # GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN 2A91; 2A92 # LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL 2A92; 2A91 # GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL 2A93; 2A94 # LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL 2A94; 2A93 # GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL 2A95; 2A96 # SLANTED EQUAL TO OR LESS-THAN 2A96; 2A95 # SLANTED EQUAL TO OR GREATER-THAN 2A97; 2A98 # SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE 2A98; 2A97 # SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE 2A99; 2A9A # DOUBLE-LINE EQUAL TO OR LESS-THAN 2A9A; 2A99 # DOUBLE-LINE EQUAL TO OR GREATER-THAN 2A9B; 2A9C # DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN 2A9C; 2A9B # DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN 2AA1; 2AA2 # DOUBLE NESTED LESS-THAN 2AA2; 2AA1 # DOUBLE NESTED GREATER-THAN 2AA6; 2AA7 # LESS-THAN CLOSED BY CURVE 2AA7; 2AA6 # GREATER-THAN CLOSED BY CURVE 2AA8; 2AA9 # LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL 2AA9; 2AA8 # GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL 2AAA; 2AAB # SMALLER THAN 2AAB; 2AAA # LARGER THAN 2AAC; 2AAD # SMALLER THAN OR EQUAL TO 2AAD; 2AAC # LARGER THAN OR EQUAL TO 2AAF; 2AB0 # PRECEDES ABOVE SINGLE-LINE EQUALS SIGN 2AB0; 2AAF # SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN 2AB3; 2AB4 # PRECEDES ABOVE EQUALS SIGN 2AB4; 2AB3 # SUCCEEDS ABOVE EQUALS SIGN 2ABB; 2ABC # DOUBLE PRECEDES 2ABC; 2ABB # DOUBLE SUCCEEDS 2ABD; 2ABE # SUBSET WITH DOT 2ABE; 2ABD # SUPERSET WITH DOT 2ABF; 2AC0 # SUBSET WITH PLUS SIGN BELOW 2AC0; 2ABF # SUPERSET WITH PLUS SIGN BELOW 2AC1; 2AC2 # SUBSET WITH MULTIPLICATION SIGN BELOW 2AC2; 2AC1 # SUPERSET WITH MULTIPLICATION SIGN BELOW 2AC3; 2AC4 # SUBSET OF OR EQUAL TO WITH DOT ABOVE 2AC4; 2AC3 # SUPERSET OF OR EQUAL TO WITH DOT ABOVE 2AC5; 2AC6 # SUBSET OF ABOVE EQUALS SIGN 2AC6; 2AC5 # SUPERSET OF ABOVE EQUALS SIGN 2ACD; 2ACE # SQUARE LEFT OPEN BOX OPERATOR 2ACE; 2ACD # SQUARE RIGHT OPEN BOX OPERATOR 2ACF; 2AD0 # CLOSED SUBSET 2AD0; 2ACF # CLOSED SUPERSET 2AD1; 2AD2 # CLOSED SUBSET OR EQUAL TO 2AD2; 2AD1 # CLOSED SUPERSET OR EQUAL TO 2AD3; 2AD4 # SUBSET ABOVE SUPERSET 2AD4; 2AD3 # SUPERSET ABOVE SUBSET 2AD5; 2AD6 # SUBSET ABOVE SUBSET 2AD6; 2AD5 # SUPERSET ABOVE SUPERSET 2ADE; 22A6 # SHORT LEFT TACK 2AE3; 22A9 # DOUBLE VERTICAL BAR LEFT TURNSTILE 2AE4; 22A8 # VERTICAL BAR DOUBLE LEFT TURNSTILE 2AE5; 22AB # DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE 2AEC; 2AED # DOUBLE STROKE NOT SIGN 2AED; 2AEC # REVERSED DOUBLE STROKE NOT SIGN 2AF7; 2AF8 # TRIPLE NESTED LESS-THAN 2AF8; 2AF7 # TRIPLE NESTED GREATER-THAN 2AF9; 2AFA # DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO 2AFA; 2AF9 # DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO 2E02; 2E03 # LEFT SUBSTITUTION BRACKET 2E03; 2E02 # RIGHT SUBSTITUTION BRACKET 2E04; 2E05 # LEFT DOTTED SUBSTITUTION BRACKET 2E05; 2E04 # RIGHT DOTTED SUBSTITUTION BRACKET 2E09; 2E0A # LEFT TRANSPOSITION BRACKET 2E0A; 2E09 # RIGHT TRANSPOSITION BRACKET 2E0C; 2E0D # LEFT RAISED OMISSION BRACKET 2E0D; 2E0C # RIGHT RAISED OMISSION BRACKET 2E1C; 2E1D # LEFT LOW PARAPHRASE BRACKET 2E1D; 2E1C # RIGHT LOW PARAPHRASE BRACKET 2E20; 2E21 # LEFT VERTICAL BAR WITH QUILL 2E21; 2E20 # RIGHT VERTICAL BAR WITH QUILL 2E22; 2E23 # TOP LEFT HALF BRACKET 2E23; 2E22 # TOP RIGHT HALF BRACKET 2E24; 2E25 # BOTTOM LEFT HALF BRACKET 2E25; 2E24 # BOTTOM RIGHT HALF BRACKET 2E26; 2E27 # LEFT SIDEWAYS U BRACKET 2E27; 2E26 # RIGHT SIDEWAYS U BRACKET 2E28; 2E29 # LEFT DOUBLE PARENTHESIS 2E29; 2E28 # RIGHT DOUBLE PARENTHESIS 3008; 3009 # LEFT ANGLE BRACKET 3009; 3008 # RIGHT ANGLE BRACKET 300A; 300B # LEFT DOUBLE ANGLE BRACKET 300B; 300A # RIGHT DOUBLE ANGLE BRACKET 300C; 300D # [BEST FIT] LEFT CORNER BRACKET 300D; 300C # [BEST FIT] RIGHT CORNER BRACKET 300E; 300F # [BEST FIT] LEFT WHITE CORNER BRACKET 300F; 300E # [BEST FIT] RIGHT WHITE CORNER BRACKET 3010; 3011 # LEFT BLACK LENTICULAR BRACKET 3011; 3010 # RIGHT BLACK LENTICULAR BRACKET 3014; 3015 # LEFT TORTOISE SHELL BRACKET 3015; 3014 # RIGHT TORTOISE SHELL BRACKET 3016; 3017 # LEFT WHITE LENTICULAR BRACKET 3017; 3016 # RIGHT WHITE LENTICULAR BRACKET 3018; 3019 # LEFT WHITE TORTOISE SHELL BRACKET 3019; 3018 # RIGHT WHITE TORTOISE SHELL BRACKET 301A; 301B # LEFT WHITE SQUARE BRACKET 301B; 301A # RIGHT WHITE SQUARE BRACKET FE59; FE5A # SMALL LEFT PARENTHESIS FE5A; FE59 # SMALL RIGHT PARENTHESIS FE5B; FE5C # SMALL LEFT CURLY BRACKET FE5C; FE5B # SMALL RIGHT CURLY BRACKET FE5D; FE5E # SMALL LEFT TORTOISE SHELL BRACKET FE5E; FE5D # SMALL RIGHT TORTOISE SHELL BRACKET FE64; FE65 # SMALL LESS-THAN SIGN FE65; FE64 # SMALL GREATER-THAN SIGN FF08; FF09 # FULLWIDTH LEFT PARENTHESIS FF09; FF08 # FULLWIDTH RIGHT PARENTHESIS FF1C; FF1E # FULLWIDTH LESS-THAN SIGN FF1E; FF1C # FULLWIDTH GREATER-THAN SIGN FF3B; FF3D # FULLWIDTH LEFT SQUARE BRACKET FF3D; FF3B # FULLWIDTH RIGHT SQUARE BRACKET FF5B; FF5D # FULLWIDTH LEFT CURLY BRACKET FF5D; FF5B # FULLWIDTH RIGHT CURLY BRACKET FF5F; FF60 # FULLWIDTH LEFT WHITE PARENTHESIS FF60; FF5F # FULLWIDTH RIGHT WHITE PARENTHESIS FF62; FF63 # [BEST FIT] HALFWIDTH LEFT CORNER BRACKET FF63; FF62 # [BEST FIT] HALFWIDTH RIGHT CORNER BRACKET # The following characters have no appropriate mirroring character. # For these characters it is up to the rendering system # to provide mirrored glyphs. # 2140; DOUBLE-STRUCK N-ARY SUMMATION # 2201; COMPLEMENT # 2202; PARTIAL DIFFERENTIAL # 2203; THERE EXISTS # 2204; THERE DOES NOT EXIST # 2211; N-ARY SUMMATION # 2216; SET MINUS # 221A; SQUARE ROOT # 221B; CUBE ROOT # 221C; FOURTH ROOT # 221D; PROPORTIONAL TO # 221F; RIGHT ANGLE # 2220; ANGLE # 2221; MEASURED ANGLE # 2222; SPHERICAL ANGLE # 2224; DOES NOT DIVIDE # 2226; NOT PARALLEL TO # 222B; INTEGRAL # 222C; DOUBLE INTEGRAL # 222D; TRIPLE INTEGRAL # 222E; CONTOUR INTEGRAL # 222F; SURFACE INTEGRAL # 2230; VOLUME INTEGRAL # 2231; CLOCKWISE INTEGRAL # 2232; CLOCKWISE CONTOUR INTEGRAL # 2233; ANTICLOCKWISE CONTOUR INTEGRAL # 2239; EXCESS # 223B; HOMOTHETIC # 223E; INVERTED LAZY S # 223F; SINE WAVE # 2240; WREATH PRODUCT # 2241; NOT TILDE # 2242; MINUS TILDE # 2244; NOT ASYMPTOTICALLY EQUAL TO # 2245; APPROXIMATELY EQUAL TO # 2246; APPROXIMATELY BUT NOT ACTUALLY EQUAL TO # 2247; NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO # 2248; ALMOST EQUAL TO # 2249; NOT ALMOST EQUAL TO # 224A; ALMOST EQUAL OR EQUAL TO # 224B; TRIPLE TILDE # 224C; ALL EQUAL TO # 225F; QUESTIONED EQUAL TO # 2260; NOT EQUAL TO # 2262; NOT IDENTICAL TO # 228C; MULTISET # 22A7; MODELS # 22AA; TRIPLE VERTICAL BAR RIGHT TURNSTILE # 22AC; DOES NOT PROVE # 22AD; NOT TRUE # 22AE; DOES NOT FORCE # 22AF; NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE # 22B8; MULTIMAP # 22BE; RIGHT ANGLE WITH ARC # 22BF; RIGHT TRIANGLE # 22F5; ELEMENT OF WITH DOT ABOVE # 22F8; ELEMENT OF WITH UNDERBAR # 22F9; ELEMENT OF WITH TWO HORIZONTAL STROKES # 22FF; Z NOTATION BAG MEMBERSHIP # 2320; TOP HALF INTEGRAL # 2321; BOTTOM HALF INTEGRAL # 27C0; THREE DIMENSIONAL ANGLE # 27CC; LONG DIVISION # 27D3; LOWER RIGHT CORNER WITH DOT # 27D4; UPPER LEFT CORNER WITH DOT # 27DC; LEFT MULTIMAP # 299B; MEASURED ANGLE OPENING LEFT # 299C; RIGHT ANGLE VARIANT WITH SQUARE # 299D; MEASURED RIGHT ANGLE WITH DOT # 299E; ANGLE WITH S INSIDE # 299F; ACUTE ANGLE # 29A0; SPHERICAL ANGLE OPENING LEFT # 29A1; SPHERICAL ANGLE OPENING UP # 29A2; TURNED ANGLE # 29A3; REVERSED ANGLE # 29A4; ANGLE WITH UNDERBAR # 29A5; REVERSED ANGLE WITH UNDERBAR # 29A6; OBLIQUE ANGLE OPENING UP # 29A7; OBLIQUE ANGLE OPENING DOWN # 29A8; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT # 29A9; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT # 29AA; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT # 29AB; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT # 29AC; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP # 29AD; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP # 29AE; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN # 29AF; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN # 29C2; CIRCLE WITH SMALL CIRCLE TO THE RIGHT # 29C3; CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT # 29C9; TWO JOINED SQUARES # 29CE; RIGHT TRIANGLE ABOVE LEFT TRIANGLE # 29DC; INCOMPLETE INFINITY # 29E1; INCREASES AS # 29E3; EQUALS SIGN AND SLANTED PARALLEL # 29E4; EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE # 29E5; IDENTICAL TO AND SLANTED PARALLEL # 29E8; DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK # 29E9; DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK # 29F4; RULE-DELAYED # 29F6; SOLIDUS WITH OVERBAR # 29F7; REVERSE SOLIDUS WITH HORIZONTAL STROKE # 2A0A; MODULO TWO SUM # 2A0B; SUMMATION WITH INTEGRAL # 2A0C; QUADRUPLE INTEGRAL OPERATOR # 2A0D; FINITE PART INTEGRAL # 2A0E; INTEGRAL WITH DOUBLE STROKE # 2A0F; INTEGRAL AVERAGE WITH SLASH # 2A10; CIRCULATION FUNCTION # 2A11; ANTICLOCKWISE INTEGRATION # 2A12; LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE # 2A13; LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE # 2A14; LINE INTEGRATION NOT INCLUDING THE POLE # 2A15; INTEGRAL AROUND A POINT OPERATOR # 2A16; QUATERNION INTEGRAL OPERATOR # 2A17; INTEGRAL WITH LEFTWARDS ARROW WITH HOOK # 2A18; INTEGRAL WITH TIMES SIGN # 2A19; INTEGRAL WITH INTERSECTION # 2A1A; INTEGRAL WITH UNION # 2A1B; INTEGRAL WITH OVERBAR # 2A1C; INTEGRAL WITH UNDERBAR # 2A1E; LARGE LEFT TRIANGLE OPERATOR # 2A1F; Z NOTATION SCHEMA COMPOSITION # 2A20; Z NOTATION SCHEMA PIPING # 2A21; Z NOTATION SCHEMA PROJECTION # 2A24; PLUS SIGN WITH TILDE ABOVE # 2A26; PLUS SIGN WITH TILDE BELOW # 2A29; MINUS SIGN WITH COMMA ABOVE # 2A3E; Z NOTATION RELATIONAL COMPOSITION # 2A57; SLOPING LARGE OR # 2A58; SLOPING LARGE AND # 2A6A; TILDE OPERATOR WITH DOT ABOVE # 2A6B; TILDE OPERATOR WITH RISING DOTS # 2A6C; SIMILAR MINUS SIMILAR # 2A6D; CONGRUENT WITH DOT ABOVE # 2A6F; ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT # 2A70; APPROXIMATELY EQUAL OR EQUAL TO # 2A73; EQUALS SIGN ABOVE TILDE OPERATOR # 2A74; DOUBLE COLON EQUAL # 2A7B; LESS-THAN WITH QUESTION MARK ABOVE # 2A7C; GREATER-THAN WITH QUESTION MARK ABOVE # 2A85; LESS-THAN OR APPROXIMATE # 2A86; GREATER-THAN OR APPROXIMATE # 2A87; LESS-THAN AND SINGLE-LINE NOT EQUAL TO # 2A88; GREATER-THAN AND SINGLE-LINE NOT EQUAL TO # 2A89; LESS-THAN AND NOT APPROXIMATE # 2A8A; GREATER-THAN AND NOT APPROXIMATE # 2A8D; LESS-THAN ABOVE SIMILAR OR EQUAL # 2A8E; GREATER-THAN ABOVE SIMILAR OR EQUAL # 2A8F; LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN # 2A90; GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN # 2A9D; SIMILAR OR LESS-THAN # 2A9E; SIMILAR OR GREATER-THAN # 2A9F; SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN # 2AA0; SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN # 2AA3; DOUBLE NESTED LESS-THAN WITH UNDERBAR # 2AB1; PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO # 2AB2; SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO # 2AB5; PRECEDES ABOVE NOT EQUAL TO # 2AB6; SUCCEEDS ABOVE NOT EQUAL TO # 2AB7; PRECEDES ABOVE ALMOST EQUAL TO # 2AB8; SUCCEEDS ABOVE ALMOST EQUAL TO # 2AB9; PRECEDES ABOVE NOT ALMOST EQUAL TO # 2ABA; SUCCEEDS ABOVE NOT ALMOST EQUAL TO # 2AC7; SUBSET OF ABOVE TILDE OPERATOR # 2AC8; SUPERSET OF ABOVE TILDE OPERATOR # 2AC9; SUBSET OF ABOVE ALMOST EQUAL TO # 2ACA; SUPERSET OF ABOVE ALMOST EQUAL TO # 2ACB; SUBSET OF ABOVE NOT EQUAL TO # 2ACC; SUPERSET OF ABOVE NOT EQUAL TO # 2ADC; FORKING # 2AE2; VERTICAL BAR TRIPLE RIGHT TURNSTILE # 2AE6; LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL # 2AEE; DOES NOT DIVIDE WITH REVERSED NEGATION SLASH # 2AF3; PARALLEL WITH TILDE OPERATOR # 2AFB; TRIPLE SOLIDUS BINARY RELATION # 2AFD; DOUBLE SOLIDUS OPERATOR # 1D6DB; MATHEMATICAL BOLD PARTIAL DIFFERENTIAL # 1D715; MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL # 1D74F; MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL # 1D789; MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL # 1D7C3; MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL # EOF sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/ttf/000077500000000000000000000000001320103431700245705ustar00rootroot00000000000000sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/ttf/LiberationSans-Regular.ttf000066400000000000000000012537701320103431700316420ustar00rootroot000000000000000FFTMaSWGDEF}t14GPOSr KNGSUBOQ.OS/2˗`cmap[*.cvt JK;fpgm~a0gasp $glyf4(pmaxp  name| posthprepGI8hCW-Y_<̓G̓G j>NC z j  RT\/\33f  Px!1ASC@ Q3>`: 999Ws sIVHh !d9[99sPssgsNs/sRshsisYs`99edesTVVhV9g9 Vs9aV9aV].V  V.V-A999 sjsWsWsVsW9sVsssVssV99sS""\9ss:sqsss-sSd[k3zdA)HLPwPsS88IVVVVVVhVVVV9 9999a9a9a9a9a9GV-VsWsWsWsWsWsWBWsWsWsWsW9 999sVssVsVsVsVsVdA,sssssVsWVsWVsWhWhWhWhWVsVVsWVsWVsWVsWVsW9gsV9gsV9gsV9gsVss 999 9 999\99 Vs[s~sUssssss9asV9asV9asVaV8V]9V]9V]9V]9.9..9ssssss V-V-ASASASs @s@ s [hWz@QsRtVVY^Xs9g  9V s9aa?VaWV sVVbOl C99.[W.->/-F\]\NsZsY.$sNr9 dV +VsW99asVssssssVVsWVsWB9gsV9gsVV9asV9asV-\D dV9gsVFsVsWB9G,VsWVsWVsWVsW9i9W999asV9asVlsisV]9.9\@~!stV]_A1VsWVsW9asV9asV9asV9asVV-zSSVh s.G13V!)X VsD asVV-ssVs~s=MsVsVsWsWWH5 5K9sVsVx^sssVDnsslsVSV?WfV+++ UU999sT`(1T1\D\l9[9W9a@Ix^k.;sV/OV@V V)o)J8c''aaFg(dhWv43eeee3Cw993PBd,3bbP9..:8Y[O?^>LMK8JLD% G5Ot%ttA8SSSSSSScWu>W>9W9F2AVVhX=VA9a9VX 3Z9aVl.V-buV.W9V-VFsj`VtVFVsjsjVsVOVV)`0U3?S`sV`?Ss` .9.9{P?RD9asVrZ;<2 .c edsd VnVnanIG(DbW9aPPVsh [h[VW.UhV]99 u.7V@VUkVcC@9aVh.7vV.UUU.@i`sWx@sWZ1xx ksVUsW#V+zk-+7UsWsWs W99@ s xk r9X.y{zm` _CD9asVm  m  a,U6+ lW rhP %kx@+VsJU]dcZC1 )I(k / >+#hW.#ssV.g1&U+EU+zUs T  T 9cZWh@ kkU+zVsWVsWBVsW^sW^sWcZC1M\Dxx9asV9asV9asVi7777U+zUUV.V.@XsV[V`UM`UG F9gx^.S#RI@ 9asV D+m^`Nh)\DH^jb\D^H)==\\\95BRb?o)3D9F'3P=Fm7H?3) \ {F'3=B@Wii1a06xsV=>A/WCsVsV+UU#`DE?S1kA)U+~ O Q5Q2H1H 0n.Vcf1-4q.o.+4Y--v*o';;-Z;;;80;B^^]];H;;Z [B+] `;:Beg`b <:DssV9ss91s btMI*s` ssV9Vss91sWsVsVsWI54X=s\DW@32'.,;`BBdBdB*BWB3X[[__:7/B"8[W<.%.<bbotVsWVsVsVshWsVsVsVsVsVVsWVsWVsWVsWVsW99gsVssssls9|99VVVssssssss9asV9asV9asV9asVVsVstTV]9V]9V]9V]9V]9.9.9.9.9sssssV V      V.V.V-ASASASs9 sWVsWVsWVsWVs1VsWVsWVsWVsWVsWVsWVsWVsWVsWVsWVsWVsWVs1VsWVsWVsW9Q 99asV9asV9asV9as%9asV9asV9asVa?Va?Va?Va?Va?Vss[[[[[V-V-V-V-VVVVVVVVVV****??FFFFFFllllsjsjsjsjsjsjsjsj*sVsVsVsVsVsV--````````?S?S?S?S?S?S?S?S__VVFFsjsjsVsV``?S?SVVVVVVVVVV****??sjsjsjsjsjsjsjsj?S?S?S?S?S?S?S?S__VVVVVVVVVVVVsjsjsjsjsj"B"B**w99 e eW**``````V-V-Kz?S?S?S?S?Se\'\_/WUs9 LssNk~KKKHssQM7UUXYV`95+-e=6;6shhss: PsV.)+V9gVshE%lX`P"P=];8eV`9d3W1d8dAdd?dAd"g{mmb))s+kUFQ@;@<fB2ssVsP9zsVA1aL -g0UAJ9999ddd)Bo)3P=FmF'=Z3333BBBRb?o)9F'3P=F7H?3\ {F'3=R3PP|ZjZH-/ HjjjHHHXHHjjmzf#:WWWWWMWRWMWMWFFW5W5WOW-WH-W$W%W%W'W/%WWW6W6W0)WOWLWLWLW^LWWWWWPWLWFWLWLLW/W9W?W?W??W6W5W6W6W65WLWLWLWLWLhWLWFWLWLWLLWWWWWVWWWYWWWV\W8W7W7W8W88WGWEWEWEWEEWWW9W9W:9WWWWWLWWWWWLWLWLWLWOOW0W6W6WW)W/W'W%W%W$%WHW-WOW5W5-WFWMWMWRWMFWWWW7YVVVVVVVV````````````hX=k=kF'F'PPP=FB&\tV$~~ou~EMWY[]}  " & 0 4 : < > D ^ o u x  !!!!"!&!.!N!T!^!!!"""""""")"+"H"a"e###!%%% %%%%%$%,%4%<%l%%%%%%%%%%%%%%%&<&@&B&`&c&f&k&o,m,w.!6<>ADO# tz HPY[]_  & * 2 9 < > D ^ j t w  !!!!"!&!.!M!S![!!!"""""""")"+"H"`"d### %%% %%%%%$%,%4%<%P%%%%%%%%%%%%%%%&:&@&B&`&c&e&j&o,`,q.8>@CF 80+ zo5!;80/-*'L?0RQHEB?<5.' XUT7541.>;ڜaa7    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpk*vjJEsLMgw=@?rHl|[cnDTI>m}b:yqz@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! , `E% Fa#E#aH-, EhD-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,CRX!!!!!F#F`F# F`ab# # pE` PXaFY`h:Y-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY&QX@cTX@C`BY@cTXC`BYYYYYYCTX@ @@ @  CTX@   CRX@ @@ @Y@U@cUZX  YYYBBBBB-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,CPCT[X!# Y-,Y+-,-A! ?9U>9UB@A@;3:U839U@@ O P(F(F*F+_O_ F@FF@36FFUHU2UUHU=UU=U@F<P&(Pp@2F?Oop?а/?Э/?ЪO/o$PoF0@pЏO_oF1ts?sP&on<nF5U3U3U`P&_P&\F1[ZHZF12UU2Ul <Ll|Q@dQ@Q58F@Q%(FPIF HF5GF5FFFF2UU2UU?_/Oo?oOTS++KRKP[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYststu+++++stu+++t++ssu+++++++++++++++++s+tstusts++tus+stsststtsts^sstssss+ss+++s+tu+++++++++++++t++^s++^st++++ss^ssssss++++++^}y:wW~j`jy"3kkk{Rni`[^^eoz iq4 HjgaAhLLLLXPHld p $ T 0 ,  h4XlD p@ xh !"##&P')x)*<*p*, ,P,-./H0 01234(5\6 689\9:;< ==>@?AC`EG(GILJJJLMNOLP(QSSLTUVVPV`WWXDXYpZdZ[\[[\l\]l]^0^|^___` `P``aaabbLbbbccDdd0ddddde0efdfffg0g`ghiiLiiij$kkkllDlxllmm8n,n\nnno oToppq,q`qqrprrs s<spssst(tXtttuuHu|uuvvwwHwxwwxx0xdxxxy(yXyyyzzDzx{H||L|||}}@}d}}~~0` Hx8h$T <lHp4dDl T0`H|$PP8lt4t\XxHT4L8h|(x<P`x8@xd @(X4Dp(@XLP8p@XŒ4 (XŌ $\Ɣ$<TlǜȀPɄ0`ː(`̐0`͐0`Θ0hϠ8pР HxӬTh\HL|ذ`ٔ8lڰ۰ܰ݀0ht\ tl4\0<HX<`Td$t40H(p  h 0  d  X T$,lh0x`8,4L t !","l#0#$%&(<)8* +H,<--.4./X00(01,1223344(484455`56<6667(7l78848h8899,9`99::X::;0;;<>$>8>>?@8@@AHAAB BXBhBC C@ClCCCCD8DlDEE@EF`FGDGGHXHITIIJLJK8KL LM8MN$NODOP`PQpRRR4RLRRSPSULV V0VW@WXXY<YLYZPZZZ[l[\]^h^__`,`a a0abhc c0c@cPcdeeefXfhfghhijLjk0klLllmTmn noohopqqrrhrstuxvwpxxy\z,{{|0|}\}~d~@D8xTX h\<L$D\T8P84hdX4l\x`p|@H|dDp0”(Ĉ0ń`ƴ(`ǜ lȜDxɜHxʨ,X˄ˬ0\̔ P|ͤ4lΜ,\όϸPhЀЬ(`Ѥ4`ҌҸ 8dӐӸ@pԠ TՄհ4`֌ DL؀4h٘(TڌL|ۤ4lܠ@tݜDlޘ@lߠ(H|Dx(X(X<h8hh$X0d@tL88h8pD0<l@xLDxDt$X(\$P| PDxP4X|4l@d @xHl Dh4d,Pt(X,`8h<p(Lp  @ t    < `      8 l    4 d    , \   (dX(T0hX 8d@lXP|,XP| P8<0l,Pt |8h  h  !!8!\!!"\"#(#\#$,$\$$$%,%\%%%&(&&&''<'l'''((4(\(((((((((((((())d))**T***+H++,,,x--. .8./d/00<0h00101@4484\4|45 5P5|5606677<778l9@9:;4;H;\;p;;?H@dATBpDDF4FGIIJ`L MN8O(PQ0RRS8STUVWLWY(YYYZ<ZZ[<[\T\] ]d]^|__`aDaabb<c cd$dflghphi|ij`jkk`kkllXllm8mmn0nnoDopppqPqr,rsshstDtu(uv(vw(wx xyyzz{T||L||||} @l@l0l 8lhhH$d@P4\8T, XL4|,p(x$4DTdtd$Lp8\,D\<Tl,d$84|0xp ,x 0@Pp 0DXl,@Tdt@PHH@P˜<àXĤHŤ\Ƥ8ǜXȰDɠHʠ`˰P̨x p(ψ4|,ѐHҐ4Ӝ\԰Xմ\,ׄt<ٜ0|DۨHܘXݼh޸ h,,,T,D0L<L@@8@,@`hdhlh |,(pH$L8DT (    T  `   p 8Ph@|2@  ?/993310!!!eL5@  @[  r b T D 2 "           r d T D 4 $   g     y I = -           } o _ K ; +           } k [ M = )   7       @[ y i [ K ; -        { k ; +        ` @   ^]]]]]]]]]_qqqqqqqqqqqq_rrrrrrrrrrrrrrr^]]]]]]]]]]]]]]]]qqqqqqqqqqqqqqqqrrrrrrrrrrr^]]]]]]]]]]]]]]]]qqqqqqqqqq?/+_^]]933310#353gW#@  ?3]2993310#3#3jyE iy@h    !     O?O   /333?33399//]q]q3333222233332222993333999910!!#!##53#5!3!33!!NXnVTnTNYnXkXnX@PjNulhhlqlhhlqrR$,3@- ))##$11$%$ 4522sY)))x11w1- 1)%}@H@ @ H*sY#"pUe/2]]]2+3/+_^]+33]]9]]]]]]]]_]]+393333333333310%$7.546753.'#4.'>VFS%|*zuX.|4b\2coU%wx ,F[hI !^i C*FXxQ AT7%, t;R8I '3W@5" (. 541Y[+Y  Y [%Y?+++???+++933333310#"&54632#3%2#"&5464&#"3264&#"326Ԣ;ߟ]M[[QO[XRLZ^PP\WQp %ưH6#/9u@F4* $!0,2  :;2''QY, 70? 7QYPY?+?+/_^]]q9/999+999333310"'#"&5%.54632673274&#">&'326yKiW!0oq:?`pG.8gdV`dA{?Jy웆G AFϸ>FvZϦ+byH[g[r5NZdy7h @  ?]9310#3 X"@   ??93331073#&!,. X+"@   ??933310#6'3+43!S@2    *%   ?]]999399]]3]]3910% '7%73-ww- ZgIHHIk)dG C@&  _ Y 7?3]]3+33_^]_]933310#!5!3!X`TT "@ [[/++993310%#65#5&({^XۨjA~[Op@Y//]q+99105![Р~@ [/+93103539??33103i P# (@  sY sY?+?+993310#"!2#"32#rckj1 +@ tY?+3?33/39331035!5%3!gMW<g >@   sYtY?+9?+3993331035>54&#"'>32!g3Oys Ksu||Vt}qɹR^FN(c@9" "%)*%tYMMsY sY?3+?+39/_^]+++99333310#"&'7!2654&+532654&#"'>32$fbw 뗐srqzoŰ/7 6@sY ??39/3+399333310#!533!qh4 6??Lw^JR^@5 sYtY sYgs?3]]]+?+9/+933393910#"&'732654&#"#!!632 9쑤I~?/!uѯ.7AZh"P@+  #$uY sY    sY?+?3_^]+9/+39333310#"3 &#">324&#"326?S51s巖~~bRn[_֙Яi  *@ tY ??+39993210 #!5! زY UY$/U@.+ % 01 "("(uY"""uY-uY?+?+9/_^]+999333310#"&54675.546324!"3264&#"!26{st}#)Ŋyħy xwyu}ݍ`$T@. %& !!sYsY sY?3]+?+9/_^]+39333310#"&'732#"546324&#"32>+6(tĜNMz  ZmϱG~:'@ [[?+/+9933105353k: .@   [ [[/++?+93310%#65#553&({^XϜjA~eH<@'?0p?o/]33]2]29105 eZ;dXG?@( YO_ @YP/]]+_^]+9105!5!dXeH<@'0p??o/]33]2]291075 5eZ㚙on^T'"R@,!   #$ ?   [  _Y?+3_^]/+_^]99333310#>54&#"'6$3253',R]PHF'>NPM<%KvdD;4sDEhP?99FX;rz =n?N~@F)8G@@!18OPY JYCY     4<<%Y<0044-Y4/+3_^]?+99//_^]3++3/+9333333310#"&57##"&5463237332>54$#"32$7#"$5!24&#"32676nsclBqR't%QPNԝ)Ƒ*7^neZ_c}(ӤXXF{{̵Y^mٞKWpW[af}xҝ\R[@6   _Y P`0/]]]]]]qq?2?39/+93233999910!!#3 !&'~?6[ 1dS1EW h@:  _Y$M>_Y_Y?+?+9/_^]_]_q++993333910#!! 4&#!!264)!26AQs}rbBshy^@9  _Y@P  _Y  ]?+3/_^]?+3/_^]933310"3 #"$5!2.(WɣlB.G1%NIQ~<{e ,@ _Y_Y ]?+?+993310#!! )!26ef:~ T@2  _Yy_Y _Y ]?+?+9/_^]]]q+933103!!!!!-2< 6@ _Y_Y ]??+9/+93310!!#!ggb@;  _Y_Y _Y0@ `  ]]]?3/]q+?+9/_^]+933310!2.#"32675!5!#"$gpM$O<ӝJ[UpWx6xnTHr}K  r@  _YP@.Iy      p ` P  ]]]]]]]]q?2?39/^]]+]q+99333310!!#3!3asT|@a@0 9p`P@P@p`P@ ]]]]]]]]qqqqqqqqrrrrrrrr^]]]]]??931033 hF@*   _Y _Y@ `P@ ]]]]q?2/]+?+93310 73265!5!C~_hxrtE#? 4@    ?3?399333310!#33 R͸>/@_Y?+?9931033!R@   pdD4 t`T4gtPD$dD4tT47tD$ dK4pP@? ^]]]]]]_]]]]]qqqqqqqqrrrrrrrr^]]]]]]]]qqqqqqqqrrrrrrrrr^]]]]]]]]]]]qqqqqqqq?3?33399333310!47#/#3>73V 1'8!w%3 pe@noT/;(  P@/    p`P ]]]]]]]]q?33?3399333310!#3&53: aXHXa0@ _Y _Y ]]?+?+993310#"$5!2#"32שŦrJ< MR},- <@  _Y_Y  ]q??+9/+9933310#!#!2)! bQ@a}$?@$%&_Y"_Y _Y & &]]/+?3+?+93310327#"&'&$5!2#"32)f7<]U>rJ< #~p  CR},-h W@/    _Y_Yp ]]]?2?+9/+3933339310!!#!24&#!!26I;͗Iվ{]-@ #/. HI Y i  #@9HF#V#f# #  # _YoYK  _Y`RD?3]]]+?3_^]_]]]+99_^]]+]]+9333310! 732654.'.54$!2.#"R г?r`d53EAvgL+f%w{EV8&%J[zOē!pepoAU;++:Tr.0@ _Y{ K ; $       _ O 0  g    _ O       p _ @     o _ ?   7    o P /      o P @        ` @ ?   ^]]]]]]]]]]qqqqqqqqqrrrrrrrr^]]]]]]]]qqqqqqqqqrrrrrrr^]]]]_]]]]]]qqqq??+39310#!5!о圜)I@,  _Y p`P ]]]]]]]]q?+?3993310"$&5332653ۭĹӾ~d M>@& P 0 ` / ?3?3]]q9333310!#373TT  @  |H9* xi:JZ) h|k\K<+ m_M/?m}[M;- 8@o{mK[9+ }k]K=+_  ?333?333^]]_qq_qqqqqqqqqqqqqrrrrrrrrrrrrrrr^]]]]]]]]]]]]]]]qqqqqqqq_qqqqqqqrrrrrrrrrrrrrrrr^]]]]]]]]]]]]qqqq+q933_^]333]]33]310!#&'#3637>3.$a-&?8 "Ttdngו#s.+ @     K    4 D jT d ; $       { d 0 $       p d @ 4   9      D t $ T T d @  0 wx ?2?39]]]_]]qrrrrrr^]]]]]]]]]]]qqqqqqqqqqrrrr^]]q9333310! # 3 3 XYP} h)b-)@ H v b T F 6 $          p d @ 4 $   i        t ` P D           t d T @ 0 $      t T D $   9    p d T 4 $    @3    T 4       p ` 0 /  ]]]]_]]]]]qqqqqqqqqqrrrrrrrrrr^]]]]]]]]]]qqqqqqqqqqqqqqrrrrrrrrrrrrrrr^]]]]]]]]]]]]]_]qqqqqqqq??39^]33993393910#3 3 HH9aA J@+  _Y_Y p ` P @ ]]]]]?+3?+3393310)5!5!!ZVW)&@ YY?+?+9310!#3Wu9??33103i W&@ YY?+?+931053#5!Ws 7@o )i~FVVfN&6f6vFVHKH@LBEHDTutTATTtUXH@-;>HrpBRbr9`d_^]^]r^]++^]qr^]qr^]++^]qr^]qr^]^]?9333310 #3΢pry i@ Y/+33105!ij)@[/?/]+993310 53WsN#0@V )).21QY )QY?o  PY$PY2222p2`2P2022]qqqqqqqq?+?+3/_^]q9/+9?+93333310"&546?54&#"'!2327#"&'#'2>=pxyn .*;!DGd[EZcYF_;rRZ$.PQpip|gZSY0dQX`#]@7  $%PY !PY%?%%p%%%%%]]]qqqrr?+???+9999333310!"&'##6533>324&#"326r{32zxy"Yc 6YAXhZWNf@E  PY p`p  PY ]?+3/_^]q?3/]+9333103267#"32.#"`ri"hl ZjV"V@1$#  PYPY$p$$$$$]]]qqq?+?+99??99333310%#"!23'3#.532654&#"52z{2xyhZ6Zby6t*pWNw@F PY  PY PY p`P0qqqqqqqqq?+?+9/_^]+9/93333103267!"3 '.#"uaݺ^H- <@h  PY PY/O_?;_/@VdH@',H 0`@]q++]qr^]q??3+3?+929333210##5354632&#"3iK4-#E>Iz F\aVWK .@d! (0/ %PY+PYPY @0 0000 00P00O000/00000^]]]qqqqrrr^]]]]]qq?2^]]+?+?+99?93333310"&'73 5##"32346734.#"32>${d 3wǻs.HS~vUHWKQ;hiia68Ƅed`@; PYp]]]]]]]qqrrr?+?39?9933310>32#4.#"#3=:}*`Ujc/ro4~= =n@H SY       p         O  ]qqqqqqqrrrrrrrrrr?+??933310533 :W=@  PYSYo?oP@0 O?/=pOp]]]]]]]qqqqqqqrrrrrrrrrr^]]]]]]]]qqqqqqqqqrrrr^]]]]?+?+?933321053#"'52653xxM2>E8 Z Hh @g     ?  ? _   ? _  9 @SVH`   `   0 @  ????9^]qqr+^]qr93323993310!#33 0Ima />v@QpOp]]]]]]]qqqqqqqrrrrrrrrrr??9310334#N)~@)! !  +*%PYPY ! d+K+?++++++++++{+o+;++ +j++++++[+O+++++++++d+K++++++++++k+4+++9+++++t+[+K++++ +++++{+[+K+++++++`+O+@ 0+/++^]]]]]]]]_]]qqqqqqqqrrrrrrrrrrr^]]]]]]]]]qqqqqqqqqqqqrrrrrrrrr^]]]]]]]]]]qqqqqq?22??+99?+9933393310!4&#"#4'33>323>32#4&#"Vps:l{8qVpvxS*,9OsZbkm`/xNa@<  PY  p]]]]]]]qqrrr?2??+99933310!4.#"#4'33>329*\Y>ykv4S*,9Op]/VN H@, PYPYp`P0]qqqqqqq?+?+993310#"!24&#"326꽅!0WM$]@7 &%PY "PY&?&&p&&&&&]]]qqqrr?+???+9999333310!"'##4'33>324&#"326rV0ƽzky?{"ʼY61fd]ZVWN"@T$# PY PY@$ $$$$ $$P$$O$$$/$$$$$^]]]qqqqrrr^]]]]]qq?+?+99??99333310"!234673#7#4&#"326{66Ҋxy6We;6k[>N#@  ???3399331034'33>32&#"+pf$%$r%f 9K*d@<" +, "PY PY,,,,,`,,?,,]]]qqqqrr?3+?3+999333310#"&'732654&/.54632.#"!XbJʳnzt0^~I(+WQTT@P"(MnP~HMJK.<*%$=Ja*,E@$   PY    @PY]?+?_^]3+393332310%#"5#53733#327*Y]}5x3?$D҃UN?:_@;  PY  p]]]]]]]qqrrr?2??+3993331032653#.'##"&5:*\Y>y:Rkv4s*,9Op]: \@     ` T D          ` T D   g    T D       [ D       [ K   7    [ K ?          ` T D         ` P /   ^]]]]_]]]]]]qqqqqqqqqqqqrrrrrrrrrr^]]]]]]]]qqqqqqqqrrrrrrrr^]]]]]]]]]]]qqqqqqqq?3?39333310!#3?3ew 8#':@(uv:@    vfTF6$fTD6itfF6$iVD6r`T$8tK0$@0{dD4d?^]]_]]]]]]]]qqqqqqqqqrrrrrrrrrr^]]]]_]]]]]]]qqqqqqqqqqqrrrrrrrrrrrrrr^]]]]]]]]]]]]]qqqqqqqq?3?33^]3]93233333310!#'#37373ѽ$ &Ѳ$.Ͱ-0:!J[: T@    v D T d 6 $         v d   & F V g F V  d V D 6 $      & 6 F 7f  @6=BH9 "       t ` T @ 4   @"H   P p  ?3?393^]_]+qqqqqqqqqqr_rr+r^]]]qqqqqqqqr^]]]]]]]]]qqqqqqq9333333310! # 3 3 ! D,[W:@  PYtdRB4$tdVB4$gtdVB4$p`TD0 p`TD0 7@Z`TD `P0 P/^]]]]]]]qqqq_qqqqqqqrrrrrrrrrrrr^]]]]]]]]]]]]]]]]qqqqqqqqqqqq_qqqqrrrrrrrrrrrrrrrr^]]]]]]]]]]]]]]]]qqqqqqqqqq?+?33339333310"'532?373J2&.bSLF`CtW +5' ƭSS: b@ PYPY      t d T @ 4            t d T D 4 $        t d T D 4 $   7      d D          d D $        p P   ^]]]]]]]]]_]qqqqqqqqqqqqrrrrrrrrr^]]]]]]]]]]]]]]qqqqqqqqqqqqqqqrrrrrrrrrrrrrr?+3?+39331035!5!!S]s&ڋ"W#_@: "$%YO//O Y!Y?+?+9/_^]qr+9933310"&54&'5>546;#";inmj?[MjXYiM[?Wiussujkl^ajmN]A@t`TD4$td hpdTD4t;+oP@0 8p`P@0 p@/^]]]]]]]]]]]]qqqqqqqrrrrrrr^]]]]]]]]]]]]_qqqqqqqqqqrrrrrrrr^]]]]]]]]]]]qqqqqqq/?9103N~"W#_@: #$%YO//O ""#Y"Y?+?+9/_^]qr+99333102654675.54&+532+5^[OhYVkO[=#534632.#"!!!27PFYVē"oGrph\M7.yym9@Ks}~w)qs'B@!" ()) Y@%Y /33+22+_^]993333331047'76327'#"''7&732654&#"Ndhcrrah`PRdfermiffNsrqqrdgeRPai`urdieNPiifruvuv@O   QY QY/  @)-H   ??39/]^]33]+q2+3+333933333933223310!!!!#!5!7!5!3 3A}@[sw}/}yN]U@  t ` T D 4 $       t d    h   p d T D 4      t ; +        o P @ 0    8   p `      P @ 0         p @ /   ^]]]]]]]]]]]]qqqqqqqrrrrrrr^]]]]]]]]]]]]_qqqqqqqqqqrrrrrrrr^]]]]]]]]]]]qqqqqqq?/99//93331033  sT3@@_$4 11+.:++BAy$k$\$8$>>5>7 7z7;7 F    7>$!QYQY?2+/3+9_^]]_]]]]qq]]]]]]]99399333322323102&#"#"&'732654.'.5467.5464&'>L}uȽ`r tLMP©!ܧܨ$#yh!ED#-@Q,!''./(YH H$$Y P  Y///p/`/P/@/]]]]]]]?+3/_^]3/+39/3+++99333333310"&546?54&#"'>32327#"&'#'26=l~FQCQ #1"IQIZvTtgt|7>54&#"'>32!+kaXGJDX zd3gEJDp:>KIDk{nZ\-q'%@O"   &'#  Y?O/?O  YY%5E?3]+?+3_^]]9/_]]+9993933933310# '73254+532654&#"'>32 =9P\JGDT VZ[jt mHA?>733267#"$#5,R]QGF'>NPM<%q2KvdD;4sDEhP?99FX;rz R&$N&%+5+5R&$@ &L%+5+5R&$`@ &%+5+5R&$ ^@ &&%+5+5R&$ l@&%+55+55R&$+@&6ESs"b%+]]]]55?55o@;  _Y _Y  _Y _Y?+??+39/_^]+9/+9992223239910!!#!!!!!#! E !d<?hNy&&z : %+5&(? &  %+5+5&(@  & (  %+5+5&(w & %+5+5&( y@  & %+55+55 &,&%+5+56&,F@ &E%+5+5h&,@ & %+5+54&, @&%+55+55e T@+    _Y  _Y _Y?+?+9/_^]3+399333339103! #!#%)!!!26fj:~!`H9 &1 @ & # %+5+5a&2&ش%+5+5a&2%@ &%%+5+5a&2@ &!!%+5+5a&2 &$$0%+5+5a&2 @ &%+55+55s #@   /q933310 7   bh^^i`fJb`g_iiaG$E@#  &%""_Y_Y &]?3+?3+999933339910#"'#7&!2734'32&#"שxȮrJyɬb;ze|pKR}n[-UX)&8&%+5+5)&8@ &%%+5+5)&8@ &%+5+5)&8 @ &%+55+55-)&<@  & A %+5+5 6@ _Y _Y  ??99//++99333310#!#3!24&#!!26tۖb߀ŏ1m@A,' ', 23$+    $$PY$  PYp3O3?3]]]?+??+99_^]_]]]]]93933210#"/32654&'.5467>54&#"#4632p4E\bUa\[96:5mqO"7R0'1(VO@f:6V=d-0T2M]案gH1&;9 |Ws&DC11&225%+5+5Ws&DvT@ 1&1 14%+5+5Ws&D22&771%+5+5Ws&D11&::F%+5+5Ws{&Dj@ 11&5ʴ53%+55+55Wss&D@ 44&7ȴ71%+55+55BN'4;@^- "4;4455&&&<=5'PY 555 " $$8PY$(QY ?o  PY 0PY PY ?3/]]+?+?+3/_^]q9/+?+999/_^]+99333933399333103267! #"&546?54&#"'!263 %32>5%.#"uafOҒoy~q .cvPÅBd]fW^H-u;oP\$5eJWaYVīWNN&Fz ( %+5W&HC& %+5+5W&Hvp@ &T %+5+5W&H@ &   %+5+5W{&Hj@& %+55+55 &C&´%+5+5/&v?@ &?%+5+5i&@ &  %+5+55{&j@&%+55+55V''s@$   "()   PYP@  H$ 4 D   R    %%PY?+?99//_^]]+]3+3/99939210"54632&'57&'3%4&#"3266^m}mPZ2ӫ<;rr^WG$Bp\j&Q@ &$$0 %+5+5V&RC&%+5+5V&Rvg@ &I%+5+5V&R@ &%+5+5V&R@ & ,%+5+5V{&Rj@&%+55+55A$u J@*   YP @YYO?]++_^]+99933310535!53ި,\"}@N $# PY PY$$$$$$$$$$p$`$P$@$ $$]]]]]]]]]]]]]qqq?3+?3+9999339910#"'#7&5!2734'326%&#"XvdSs[QDCbt֊0[iɅ\X݂U1Q&XC& %+5+5&XvW@ &5 %+5+5&X&!! %+5+5{&Xj@ & %+55+55W&\v@ &6 %+5+5W!>@  "# PY PY??+?+99?9933331033>32!"'##4&#"3260rVzky?{YAXd]ʼYZW{&\j@ & %+55+55R&$hI@ &%+5+5WsS&D11&2ʴ21%+5+5R&$ y@ &%+5+5Ws&D11&4̴4:%+5+5`c&$ ϴ%+5WUsN&D:´::%+5hy&&S@ &~ %+5+5W&Fvg@ &r %+5+5hy&&@ & &  %+5+5W&F@ &  %+5+5hy&&"%@ &' %+5+5W&F,@ & %+5+5hy&& @ &&" %+5+5W&F@ &" %+5+5e&' f&%+5+5V&GK@ ++S++%+5?5eVm*s@-%+,QY/ (PY@ H"PY?,,]r?+?++99?9/_^]3+3?99333339210%#"!23'5!5!533##.532654&#"52z{2,xyhZ6Zby)6t*p&(zI & %+5+5WS&H@ & %+5+5&(  & %+5+5W&H@ &# %+5+5&(% &  %+5+5W&HD@ & %+5+5U&(P´ %+5WUN&H}###%+5&( o & %+5+5W&H&" %+5+5g&*@  &%"%%+5+5VW&J@ 00&55/%+5+5g&* @ &"'"*%+5+5VW&J@ //&228%+5+5g&*5%@ &'!%+5+5VW&J-@ /&//1%+5+5gN&*N #.#%+5VW &J877&//3%+5+5 &+@  &  %+5+5>&K@&%+5+5@#   @(H _Y_Y @ ?2?399//]q33+33+_^]+]q99]]333333333310!!##5353!533#5!fs-  s@#!"@"P"`"@"""""@HQY PY H?++?39?9/3+3+_^]]qr393333310>32#4.#"##5353!!=:}*`U,YjcWro4= &, @ &  %+5+5&@&   %+q5+5 1&,I@ &%+5+5 1S&@ &%+5+5l&, @ &%+5+5l&@ & %+5+5\U&,  %+5U}&L@ %+5]|&,'%@ &%+5+5v: @$t4$n@4$tdk4$8td+{k@0 p`@ ^]]]]]]]]]]qqq_qqqqqqrrrrrrrr^]]]]]]]]]qqqqrrrrrrrr^]]]]]]]]]]q??931033´:I&,-W&LM@o0]55]]55 &-7@ & %+5+5W/ @ [/? PY?/o?oP@0 O?/=pOp]]]]]]]qqqqqqqrrrrrrrrrr^]]]]]]]]qqqqqqqqqrrr^]]]]]]]?+?/_^]+9333393310"'52653#'##53MM2>E8xjihW Hh@nN?&.ʹ  %+5N&NN  %+5: A@%   ?  ?2?39]]39333310!#33 0Il~: //&/&^ %+5+5[>&ON@ &K%+5+5N/&/ %+5~NG&O%+5/&/@  %+5?5i&O$K)@O/p  %+5?5]]q/&/ _%+5&OBT%+5/ F@%   @    _Y?+?9/_^]]9993333310!!573%hyUU I@+   O    p  ]]]]]]??9/]99333331035737zz||DDXHGz &1@ &I %+5+5&Qv@ &a %+5+5N &1 %+5NN&Q[  %+5 &1 & %+5+5&Q&# %+5+5P&Qb$$״$$ %+5?54$=@ !!&%_Y_Y?2+???+9933310"&'732>54&#"#336$32_;ZjIU&wyHMJJsnOBheD=aqWN#;@!!%$ PYPY?+???+9933310"'52654.#"#4'33>32M2>E8*\Y>yxW Hhkv4S*,9Op]a&2I@ &%+5+5VS&R@ &%+5+5a&2 @ &&%+5+5V&R@ & %+5+5a&2 @&]$%+55+55V"&R @&g %+55+55a _@4  !"_Y _Y _Y _Y_Y"]?+?+?+?+9/+9992399310!# !2!!!!!%27&"#"CpFkiLoR4,I OLy <WV2N%,}@E! ,&-.,PY ,,, )PY  PY #PY  PY ?3/]]+?+?+99?+9/_^]+933333993103267! '!"3 6! %4&#"!26.#".uay|u~?*^H-!'S@h&5& %+5+5&Uv@ &J %+5+5Nh&5 %+5NN&U^ %+5h&5 f& %+5+58&U8& %+5+5]-&6vI@ .&.[.1%+5+59&Vv9@ +&+]+.%+5+5]&6r@ //&44.%+5+59&V@ ,,&11+%+5+5]N&6z 2(2.%+59NK&Vz //+%+5]&6 s@ ..&006%+5+59&V@ ++&--3%+5+5.N&7z^   %+5N3,&WzP@))/)_)o))?)O)_)o)))))/)_)))))))/)?)o))))) ))_)o)))))))/)?)o)))))) ))?)O)o))))))) )%+5]^]qr^]qr^].&7 !& %+5+5&WK%+55.J@*   _Y _Y   ]]]??9/3+3+3933310!!#!5!!5!&85@'`pg&6FSs%+]]]55?]55s&X@&!! %+55+55)&8 @&Q%+55+55&X@&d$ %+55+55U)&8 &g@% ) )P)`)p)))))]q+5U:&XW$$$ %+5 &:y&  %+5+5&Z&%+5+5-)&<[ & %+5+5W&\@ & %+5+5-)&< h@ & %+55+55A&=@  & J %+5+5S&]v5@  & ] %+5+5A&=|%@  & %+5+5S&]  %+5A&= 8@  &  %+5+5S&] & %+5+5 c@)`p@"%H?`  PY??+_^]]q+qqrrrrrrr93310!#4632&#">K4-#E> F\ +h@1&&,- )PYQY#PY H??+]q+9/3+3?+99?93333310353!!3>32!"&'##65#4&#"326 12zr{3Uxy9AXhZYc 6f!*\@/ ''"+,''_Y' ' &`Y_Y?+?+399//+99333339310#!#"&5463! 4)!264&#!!26MBsQADG18J=}~rbBs U@  `@#H _Y _Y_Y?+?+9/+_^]+]99333310#!!!!24&#!!26Hy%H@%  &'PY SY #PY?+??+?+99993333310!"&'##65!!3>324&#"326r{32zxy"Yc 6AXhZ 4@  _Y  _Y?+?9/+99333310#!'5%3!24&#!!26uە%8{i!x &@@ !!  '( $PY PY ??+?+99?9933333105%33>32!"&'##654&#"326 32zr{3xy!YAXhZYc 6_[f:@   _Y _Y ?+?+99//9933310%2# '6$3 ! 7ݗX7Bj<-؈* :Nx]1%NIQ~GM<{W$O@(  &%PY "PY PY ?+?+99//9+99333333103267#"32>32&#".#"`j v/4$+B7ri"hl Hy GMZje<@ ! `Y_Y?+?+39/933310#!#"&5463! )!26MBf:~DG18J=Q ?@   _Y  _Y _Y?+?+9/+993333104$3!!5!!"$73!!"Q|R%K@'  '&PY SY #PY?+?+?99//3?+993333310323&5!5!#.5## 32654&#"Rz23{ryx"ZhXA6 cY0VWN>@   !PYPY PY ?+?+?+9933339910!&/.532!4&#"32>dniZDdq}?YWX քUY :@   _Y _Y _Y?+?+9/+9331035!!5!!5!Y2-Ĝ^E@# ! _Y  `Y _Y?+?+99//+993333310%27!&$#"'6$32#"$547 'Ǜ9N30͢~y9M%>X&X@- #$$'(`Y## _Y !_Y?+?+9///+9993333339910".5467$4$32.#"!3#"3 `키 F2~bGGwUgw7Ш=vtevMW:@ _Y_Y _Y/+?+9/+933310!!#"'7325!g=R#3$hCNC@#    PY QY //]+9/3+3993333310&#"3###737>323;<@OF J^-g^a*X@.#** ,+!&_Y!_Y_Y _Y?+?+99//9++933333310.#"32675!5!#"$5! 54632&#"<ӝJ[UppM!z6B8 G>xnTHr}KWx{GM W?@"       _Y/+?3993392310"&54>73 34&'326s*OY?4""4.((.Wz-iivAy9GG:5;:$E@$ "" %& PY PY?+???+9?93333310"&54&#"#3363232653scnyzlɴE|~= ͺyy3>@ ??9310334! ,@  `Y??9/3+39333103#5333#Ħa`?H@$  _Y?3?+?99339933333910!#4632&#"3 R͸BB29~idU@+ SY QY??399//+?+9/33339339103 ##4632&#"IBB2B:/m ,@  PY??9/3+39333103#5333#ppmma`A@!         ?3?99//9993333310#&'#''7'37ʟþ)+D.I6:?Apf.kʽCvE͓9!F@$ #" _Y_Y?+?+?3399?93333310"'# 332653323#'\xc0cXwodw 2gGGCfUQUW 1@ _Y ?2?99/+993333103&53##"'5325 H1,8sHXa֐ VN3@   PY  /???+9993333104.#"#4'33>329*\Y>yVXkv4S*,9Op]aa%H@% &'`Y _Y #_Y?+?+?99//+99333333310#"$5!2>5#53#"32שŦrJLHXEXs} MR},jIj{=jw,-VN G@#  !"  PYPY?+?+?99//99333333310#"!265#534&#"326Xxp!0Aj0GaXL@@    _Y _Y/+?+9/3?3/933310%#"$54$32%3#/&#"327mwȥ:ԣ+d eU45LGG@m..VWNB@"   PY PY??+?+9/3?3933310##"!2%3"327&9QR3D+z@84W?',!0+Ar.B@"  _Y   `Y ??+399//+93333102#!##"&5463)! bMB{@DG18J=VW)F@%$ +* PY SY  'PY?+??+?+999333310!"'##4632&#"3>324&#"326rV6'%8<}0ƽzky?{"ʼY[d]ZCI@%  _Y_Y/??99//++3993339922310 !#3!2 4&#!!26g}uQվ{b*;@% ,+$ (( _Y( `Y?+3?+993993333104>7>54!"'>3 !2>7! $b>xP32^Kvf4~ZgK>Y@w!ĵMuUD()IgMB~%OK%;@!&' ##PY#  PY?+3?+993993333104>7>54#"'>32327!"&OQ\1oҷLd3:L{+[{Q##+=.V`RqN"'/C3ƧlUCW$;@ &%QY SY"QY?+?+9/+9333104632327#"&5#"&732654&#"Co^;:64EA=ThM<01<;20<ZeDK  gU1<<10<<W*,E@$  QY PY SY?+?+?3+393333310#"'5326=#"5#53733#327*CD82<6}5x3?$DBGq҃UN?3@   _Y??+39/933310##"&5463!оMBn圜QLr@@   `Y _Y?+?3399//+9933333310"$&5332653>5#53ۭĹӾysXé~dmt_j:%R@*$ $&' QY   %!PY!??+?339///+39933333331032653>5#53#.'##"&5:*\YsYXÐ>y:Rkv4sb#oej/*,9Op]W#K@& ! !%$ _Y _Y _Y?+?+?+99993399332210 47+5!32654'5!#"'њ6MM6O&Ts _` q&-@ _Y _Y?+??+9933310".53324&#"'632N[:2CNc ]-,@    _Y?+??9933102.#"#3 ><;T^%$1 rT7x-3-0H9aVGW<@  !  !SY PYPY?++?9?+99339910"'532?37>32&#"J2&.bSLF* McQlRDHa3D]W +5' XYj0]>T>G@%   `Y _Y_Y?+?+9/3+39333310!!5!!!!!5!> S l].8/:E@$   PY PYPY?+?+9/3+39333310!!5!!!!!5!/ 84_{iF[-VJ@&  `Y  _Y _Y?+3/+9/9+933333310 !5!#"$'7!2654$+SvGHFVF@% _Y`Y _Y/+?9/9++393333310#"3 # 4%5!!AGcqvS%j؋lHA3ꡜ]U$;F@$QYPY PY/+?+9/9+933333310!#"3267#".54675!KRQQ׍uؽs}\bx mNV:(R@+(#*)PYPY#(&PY/+3?9/99++39333333310#".54>7>54!5!3 7(ҊiUrddtOa4BeL\OnN+*Q:J=*w wRlH+".A0IRZ $N@( &%$$`Y_Y `Y?+3?+9/3+3933333310!>54&#"'>323!!!5>7!ZPJIys {A}d[%lPEOt}qɹ:ppt?WrY&D@$ _Y_Y `Y?+?9/9++393333310#"&'732654&+#5!!32&"#qJN«~uʜ._:G@%  PY PY PY?+3?+9/9+933333104!##5!!32#"&'732ۧ B6Ѽ" \U ]u$)B@" '$  ) +*&"#"QY'#PY?3+?39+3?9333310#"&'732654.'.=#5373[!RLfH2!j$HkFkTD-"*2CR7WQPX1?*+}[NWVM8@  PYPY??+?+999333310#33>32!"> ,pӾppdiEa`RKd@ ??9310334 @ ?2?3993310333344r:R@*   _Y _Y ??99//3+33+39333333310!3!!!!#!5!!rrrtt:̙`4 j7&''=d &+5 a&'']H &+5V&G']d --&+5&/-W &/MW&OM /&1-W&1MW&QMdR&$ c@ &%+5+5Ws&D22&4۴4:%+5+5l&, @ & %+5+5& 2G&@. %Opqr]qr+5+5a&2 &$%+5+5V&R@ & %+5+5)&8 &%+5+5&X&$ %+5+5)+&8 3@& %+555+555k&X'j?@%&& &0&@&P&`&p&&&&&& %+555+55/]q]5)+&8 4@!!& %+555+555&X'jv)@&&&& %+555+55/]5)+&8 5@$$& %+555+555&X'j)@&&&& %+555+55/]5)+&8 6@!!& !%+555+555&X'jCp)@&&&& %+555+55/]5VNA@!  PY PY PY ?+3?+9/+993333104&#"'!2# 53267ZuaX#C^H-?R+&$ 3@&%+555+555Wsk&D'j!@?<<<0<@<<<11&+55/q]5R+&$ 7@&%+55+55Wsk&D'.!@7?77707@7744&+5/q]q5&I&C%+5+5BS&X@ ==&>>?'%+5+5g &j@9%% && (' &&`Y_YO  _Y !_Y ?+?3/+99//_^]]+3+39333333310!5!5!3##"$5!2.#"32675!L[UppM$O<ӝJ\kYr}KWx6xnTHVWhK)6h@81 ** $$78' QY$ @4PY@ H .PYPY?+?+?9/++2+399333393310"&'73267!5!75##"&632346733#4.#"326${dixr3wƼs.QHT~wWKQX\5hiia6v[Ǭg&* @  &""(%+5+5VW&J00&228%+5+5?&. @ & %+5+5&N  &ʹ %+5+5aU&2lVUN&RraU&2'lI@ 0&00/%+5+5VUS&R'r@ ,&,,+%+5+5-V&y !@ &$ %+5+5DW &T@ & % %+5+5W(&@ &W %+5+5 j&'= a&']V&G]dg&*G@ $$& _ #%+5+5VW&Jvk@ 44&0d03%+5+5a@  _YP@Iy `Y ??+??39/_^]]+]q+9333331032653#"&5!#3!3 vu3̲#sTW:@   _Y _Y /??+?+999333310#3>324&#"6Կ?oÐ_V#;H*Xabbu:- &1W& %+5+5&QC & %+5+5R>(.@j " )-  0/0000@0p0000 ))Yp&+++++@ H`++ ?&&&&/3//]]3339/]32/]+]9/]+3/_^]]q993933293310#!#.54632!&'4&#"326573;.á~$1=dd 5jN89NH6 :Lp7aDsc8Z1U&D'UvcZB@&N?NONNNpNNNNFF&IIL%7Դ71%+55+5+55/]]q5&& %+5+5B&v@ <&<&<ٴ<4%+5+5N&(tj@ & %+55+55W&H@ %%&"" %+55+55$&(j& %+5+5W&H:@ %&%% %+5+5ilN&,j@ & δ %+55+55WZ&@ & %+55+55P$&,,j@ &%+5+5B&@ &%+5+5aN&2j@ &&&## %+55+55V&R@ ""&%+55+55a$&2 j@ &&&&%+5+5V&RD@ "&" "%+5+5hN&5lj@ ""&e %+55+55&U@@ & %+55+55h$&5j"&"" %+5+5l&U@ & %+5+5)N&8j@ &ϴ%+55+55i&X@ &&&##  %+55+55)$&8j@ &%+5+5&X2&&&& %+5+5]N&6 228%+59NK&V //5%+5.N&7  %+5N*,&W@!X!!/!}!!/!?!O!o!!!!!!! !!/!_!o!!!!!!! !!/!?!o!!!!!! M!!o!!!!0!!!<@!!! !0!!!!@!!E%+5]qr^]^]qr^]qr^]_^]@U"F@!  $# _Y/3/?+3/9/33993333399105$54&'5,54&#"'>32W^*%IuAs>Uq8U@Pha2I(r𧆉TlOftY\<!V.N$D@ "  &%" PY/3?+3/9/3399333339910%5>54&'5,54&#"'>32.p@)! `eefiyr2@[0X_vGGuhA<ёmeV[!b{8) &+  & %+5+5&K @ &"%+5+5W1@    _Y  /???+999333104.#"#36$32E?yoRJWT}DbRO\VVt!-9g@7.4( " ;: 7PY +PY1PY%PY?+3/+?+99?/3+99333333310%#"!23'3632#"''67532654&#"4&#"32652z{2A G=4?hZ6ZbywSq>u`pU0AP^9]w'2^@2.$(  34!+!+`Y`Y!! 1`Y '`Y?+?+99//++9999333333910#"$54675.546732654&'4&#"3 ~~彩܉ARŢ|%}aSTY^yl|_,'4P@)"  1%+65((QY"!.PY?+?339/+99993333339102654&'7#"&54675.5467"32654.E}QII{po{IIQ}Ij}Q~#qDp ɏԐ pDq#~Q~WNA<@ _Y @_Y?+3?+39333107!5!!2'654&#!AZ|GMV~MM"8 G>1 ::@  PY PY?+3?+39333107!5!!2'654&#!18jGM&psGV"1'?<R&$%@ &%+5+5Ws&DJ44&2۴24%+5+5N&(zWNN&Hz2a+&2 3 @""&  %+555+555Vk&R'j%@!!?!!!0!@!!!&+55/q]5a+&2 8@'&''3%+55+55V&R'J@/22?2O2_2o22&+5/]q5a&26%@ &%+5+5V&RE@ &%+5+5a+&2 7@&!!"%+55+55Vk&R'K!@?0@&+5/q]q5-)&<eI@  & %+5+5WS&\@ & %+5+5V>@ PY   PY ?+?9/99+393933310632#"''674&#"326>D;H{JOnb9:&> G=4?swSq>uV0AP^9VtN(4f@9  /$()(& 56$(""2PY""" "PY ,PY?3/+??+9/_^]+99333333104.#"#4'33>32632#"''674&#"3269*\Y>yD;}H{JOnyl9:&> G=4?kv4S*,9Op]nySq>u`0AP^9,%]@1  '& PY #PYQY@QY?+?+?3+9/3+3933333331053733#632#"''674&#"3265xD;H{JOnyl9:&: D<4?wSq>u`N30ATZ9W=: @  PY?+?39310#"'52653=xxM2>E8 HhS!-9c@6.4( " ;: 7PY1PY +PY%PY?+?+9??+?+?933333333310%#"!23'33>32!"'##532654&#"4&#"32622z{22zrdxyxyhZ6ZbyYAXhZïpSWL!.:_@55)/ " ;<%PY 2PY8PY ,PY?+??+?+?9?+933333333310!"'##7##"!23533>324&#"326%4&#"326rV4}50ƽzky?z*xx ̼YegU6Xdd]ZR f@7  _Y_Y `Y ?3?+9/99+3/+93933333210!!#7#33 3.'3#q¢A?fݢTq6, 1&Pkh1Sqhy%@O  !$ '&#@P @_Y$  _Y ?+3/_^]9?+3/_^]9933333310%&5!273&'3 #"'#"&VwwlBNtz=?p(Wл[I-[{b.Q~1W%NDe& W%e@9 !  '& @PY!  #PY?+3/9?+3/99333333107&323&'3267#"'#&#"I<Ɂ "7Q`tXŎ -0",Y5=VD..#hl %F˰e / ?@   _Y  _Y ??+9/3+399922231033!!!!#qy*W.L@)    _Y _Y ??9+3/+9339933210 ##!5!733#A@C(;iiGVK;R@), 33&&=<3))0PY-)  PYPY?3++9?3+999933333310#"'3267#"/&'&'732654&/.54632.#"!FE"6`8g8~ICM9uXbJʳnzt0^~I(+ !q;*RTaWX=TT@P"(MnP~HMJK.<*%$=Ja1V:;@ PY PYPY?++3?+9933331072327#"./.+5!5!>QBD2 4<`8g81NE:%DN8/P#Dq;18G, $3L8@ `Y `Y??+39/+933310!3 4&#"'6$3 4KT$ ƹuvPN<@  QY PY ??+3/9/+933310!2654&#"'>32bmףۣ{Ï&!&i@5# # ('#`Y#_Y##_Y"_Y?+?+9/+9/3+39993333333399103! #!#%4)!!!264&#!!26!ć\sQA}UGrbBs)M@&   ! _Y_Y?3?+9/33+3399333333331033!33##"$&=#26=!)uuuu)XX~~s M8@ o 0 /  ??39]]]]93333103#'#H?zTT|  @U _Y _Y@ _Y _Yy_Y_Y?+?+9/_^]]]q+3/+?+?+933333933107#!733#!!!!!!3!kLFFS:|kj5<DX&"&*@J' )  "## ,+"#PY""**PY**%%PY)PY ?3+3?+399/+9/3/+?933333933310&323!3267!"'&#"%&'D^JͿX4Bua`PL3?M| E8ppi HhO欬aW#D@" !%$ _Y_Y_Y?++?+?9999333310327#"&=# !27327&#"5873/:NrJàvs sQC[UR}M8gdVWN ,I@&! '.-PY $PY *PY?+?+?993?+9933333310%7##"!234673327#"&54&#"32656{687&02J|xy1k[6We;lQC hO@'  _Y _Y ?3?+9/3+339933339922310!!##53!24&#!!26I;͗IIվ{NE@# SYPY ??9/93+3?+993339333103#5354'33>32&#"3#zz+pf$%$<r%f ϒ-)D@$   `Y  ??39/933+3393932103###53'3!73!ؾғ2?Ha'W:"X@. $#PYPY PY?+?9/993+33/+?9933932103##"'532?#533!3?!yoCtYJ2&.bhuFF+LS +NUUo'N#0W@.) .12QY)QY!)) PY$PY?+?+3/9/9+?+93333331023267!"&54&#"56323>"7>54&֣px{m .|*;!DGd[@]cYF_N;rTXܻPQpipxkZSY0cRX`VN">@ $#  PYPY?+?+99??99333310%#"!23'53#.532654&#"52z{2xyhZ6Zby/6t*p~N">@ $#  PYPY?+?+99??99333310>32!"&'##4'34&#"32682zr{2&xyhZZby/[6t* ,F@%!'' .-$PYPY *PY?+??+?+999333310!"&'##654632&#"3>324&#"326r{3BO:E?E2zxy"Yc 6ӏLHhZ=N6@  PY PY ?+3/?3/+99333104&#"'>32#"&'7326ir٧`"jZẜ lhMN+d@8 $* ,-*'PY   PY  PY?+3/?+9/_^]9+993333310"''67&532.#"63 '2>54&#"1p4#|H%cri" e}Sq=@32cltpt`Z]pi^ʢy!^]W`\NR]C5N3~@G1#// *541PY&,SY/!! &&PY PY?+?+9////_^]q93++9933333339910#"&'732654&+532654&#"'>3273267#"&'cltpt`Z]pi^ʢ"/2%=";~U[!y!^]W`\NR]e\DC4yp7KN&T@- !!'( PY   $$PY$PY?+?+9/_^]+9993339921032654&+532654&#"32#"C~rltpt`Z]pi^y,[^]W`\NR]Co V':<@  QY PY QY??+/+?+9333310733##"'5326=#5۴K4-#E>ӃI F\{VW'5T@,( /6"7$PY ,PY2PY PY?3+?+?+99?+39933333310%!"&'73 5##"3234632&#"4.#"32>;{d 3wǻs. &729@AHS~vUH8KQ;hiia1 NFyedVWKJ^NE@#PY PY PY?+?+99//+993339210!#"32.#"3275#@Kzrig^DK"ZjCW:A@$  PY ?3?+993392310%3 3#"&54>4'326w5>l}kj}%LK'$%'!]umm(Z\SQ.43A 8P@- !4$!,9:4$/++//(PY/ PY PY ?3/+?+?+3/993310%4.'322'&#"#"&54?.#"'>32?> )  GHL8r!??#-3R}kj}P1-#?>"r8Lau)77(u*3L00/]`\o2[55^QjxxjV]055[2o\`WnFFmXo;-@  PY?+?39/9933310%#"&5332653#??;{*`Um`Rro4~4J7@ !PY PY?+?39?+933310>32#4.#"#!2&#"=:}*`UEI5B~ jc/ro4(`?W'B@$ # )( %PY  PY PY?+?+?9?+93333103>32!"'53254.#"#!2&#"C :}3U:=u*`UEI5B~J?jc!ro4( 7@  SY  PY ??9/3+3?+933310333###53qrrqqi/<V: @   SY?+?99310327# 3 97!,2-%QF (#D: 7@  PYPY?+3?+3933310%3!53#5!#Rό#D@" PY  PY ??9/+3/3/3/+339333103&#"#>3332673#"''kT7 Z`#0/h|yB/0rrxZH@%  QYQY   ??99//3+3+393333310###"&546;33!5#"3Zdf3;nYYkc{ <3iW @  PY?+?39310327# 3:977/E-sQC(MWOW@.  !  PY PYQY???9/++3?+9/933333331033!#"&'732>54&+5!璼'7aORn䁘_aS$:)H@%"( (*+) %PYPY ??+?+?339993333331032653#.'##"&'##"&533265Vps:l{8qVpv:Rxs*,9OsZbkm`RxsW:#D@# "" %$ #PYPY ??+?+?3399933331032653##"&'##"&533265Vps7n{8qVpv:RxsXkXbkm`RxsW#N1Q@, 1((-23*/PY*$PY$  PY?3??+99?+?+933333104&#"#4&#"#4'33>323>32!"'5325qVpvVps:l{8q3U2ExxxS*,9OsZbkm`WN":@""$# PY  PY ??+?9?+9933333104'33>32#4.#"!"'5325>y*\Y3U/HvS*,9Op]/kv4 WN"=@ #$PYPY?+???+9399333310327# 4.#"#4'33>32u>,@;*\Y>ys(/kv4S*,9Op]:.@ ?3?39999333310 '3#&'#<%3(!:SJ}:GbAg:VN K@)PY PY PY?+?+9/_^]+99333310#"!2267!"!. I !04XVN!k@: #"QY PY PY PY PY?+?+?+99?+9/_^]+9333333310!!!5#"!25!!4&#"326GWsvq]q!0q]SWN(H@% &&*) ##PY#PY PY?+?+?+9/9933931032654.#"326= #"&'#"5uprwiiwrpUM|ʸzM`úoo䗻`xz}%NVWR@-   PY PY PYPY??+?+??+?+9333333310&%34&'>%ihrqiejlcW2~-fD++:*@ SY ??+?9993310#/##"'53265%"?[E$%$32&#"+pf$%$32&#"327# +pf$%$r%f YQC (N@ SY??+9931034>32&#"\;%$<}mf  WN@ SY ??+993104&#"5632fnx<$%;\W f0: I@%    PYPY ?3?+9/+3933399310!#!2##!254&#Bθؕctk:?\[0: B@!  PY   PY ?+?39/3+9933333991033#!32654#!BLٷ2ls?:O[\9WK5T@-**44$$.4672,PY2)''PY'PY?3+?3+9?+933333331032654&/.54632.#"#"'327# XbJʳnzt0^~I(Эoz9*"$+WQTT@P"(MnP~HMJK.<*%$=Ja>; (WN(@  SY SY?+?+339310"'532>54632&#"O7<96=k,1BMKW,UhiuW F@%!"! PY SYPY SY?+?+?+?+3339933310734632&#"3##"'532>=#Y21BMKO7<96=޲iGF,Uh%W:N(@  SY SY?+?+339310"&54&#"5632327KKB1,W849<7W ffW,VK&Q@*"  '("  $PY SYPY?+?+9/+93399333310/#".54632!2&#"%3265&#"FTQI{I@D$k,1BMKlyvA/>IN9:6VXe=qTv+bi`86_O1A*A@"  QY QY PY?+?9/+3/+39333106323##5#534&#"Y]}5x3?$D}ԃN?W*,6@  QY PY?+?3+3933310#"5#53733#327*Y]}5x3?$DokN?Q:&R@)  ## ('"PYPY ??+?39/933+3399333333310!33##.'##"&=#5326=!:ll>ykk *\://*,9Op]{=Xkv4T6:/M@)$(- -10"%PY"( PY PY?+?+9?+993399332210"547>54&+53232654'&546;#"ET3!++R|YmCDCDnX|R++ 4TԌvH@*gW_tt]]tt_Xf*>Iv:)@  PY?+?39933310#"&533 4'3ŵpA15:;cav: *@   ??3993333103#'#4#':uv>:+@   ?2?9?3933103?3#.'#&'#0ѯ#' /+&:Mg]#.խ"&3,@   PY?+?39933102&#"#.'#>BJ2&.bHD@r +V6O:V :&@ ??39933310!3 3g,5[{K1Wr:;@  PY PY PY?+?+?+3933310327#"5!5!5!!+75%C:,8jsQC (&1N): Y@/ "!PYPYPY ?3+33?9/++39333993310%>32+'7!5!5!%"32654&eav0"78j./dFMF6{gxy^T=u&ڷQf,7'-DW :P@) PY PY PY?+3?3/+9/+393339310!#"&'732654&+5!tsv،RA:Nj wǤlVK; *j@<(#   +,PY#  %PYPY  !QY?+3?99//++9+3933933310'#".54632654&+5!5!%27&#"|_RCsA>CaISxVEzJuDEp8Ii:5=J9<@  PY  PY??+3/9/+933310!32654&#"'>32l.=ir٧ۺjZ~[:@  PYPY ??3/+9/+933310!&532.#"327ri=. Zj93@ PY PY?+?3/+?93310#"&'732654&#" ri=.j꬗Zj9WVN9@PY PY?3/+/+3/9933331032.#"3267#"Wri`Zjjhl a<@   SY SY?+?+99//99393210#"$5!2#"32%53שŦrJ< MR},-:!p@  "#0##@(#H#/## QY PYPY?+?+9/_^]+9_^]+q99333339102#!32654&+32654&#4rmCpw؀mfy:]|k:KU]eSHZRLIN&R@*!! (' PY   $$PY$PY?+?+9/_^]+999393332104&#";#"32>7#"&547.54632]jq\Z`uosms>omӻ,ZUN\`W\_`ƕ੓)"qX^'X@.'' !)(#PY PYPY PY?+?+99//9++923333310.#"3275#5!#"32>32&#""rigKzj v/4$+B7ZjCۉ^DK"Hy GM: W@   @ P ` @   @HQY/   ?3?39/]++_^]qr99333310!3#!#: :6:W) R@+ "!SY PY  PY?+?9/_^]3+3?+99333333310"&546;33#'26=#"53WOV\MAQW}~$%bYkk<98A;nz: >@        ??3939933333991073## n%2:m4i:@PY?+?99310)3!:[VW(G@$ #)* PY  PY&PY?+?+99?+?399333310"!234632&#"#7#4&#"326{66'%8<}6Ҋxy6We k[>/f@9  !QYQYPY?   PY?+3/?99//_^]++3/+9333333310!32654&#"'>32!!#5!/=.=ir٧K ۺjZn{Oc@6   !QYPY? PY??3/+99//_^]+3+39333333310%!#5!5!5&532.#"327!ôKri=.={n ZjV%(c@5 && ' )*''PY  #PY PY&&PY?+3?+?+99??+39333333310!.5##"!23'3!!32654&#" !C2z{2rVxyYt*hZ6Zbynڋ$VV1=@I((8##)8*.2?>.'PY.**PY ;PY PY5PY?++?+99??+3?9/+993333333339310%#"!23'3!# 732654&+5!#.532654&#"52z{2{܋X#oRxyhZ6Zbyn'[P͜26t*pVN&25?@G'33-99"5=@A4#4PY #:PY6PY5PY 0PY*PY?+?+99??+/+?+?3+393333333333310%#"!23'3!3>32+'7!.532654&#"%"32654&52z{2sTeav0"7,xyM/dFMF6hZ6Zbynڪ{gxy^T=ut*pɯ#Qf,7'-)d,"5_@2&. 22 .76&PY 1QY  QY 5PY?+?+?+?3+9993333333310#!"5#5373!632.#"2654&/.547#3d}5xJ\nzt0^~I(IyXbJ(3?+ƒ~HMJK.<*%$=JaFK@P"(MnPYBeNB)W%-d@7( #++# #./PY*QY@ &PY PY?+?+?3+3?+39_^]]333333310"'532>=#"5#5373354632&#"27#O7<96=XY}5x՗k,1BMK&=3W,Uh+҃0iu6 cN?,0<|@E & 5--;(=>#PY (;++8PY++  QY PY1PY?+?+?3+39/+9?3/+933333333310"'#"&5#53733#327&532.#">3 '2>54&#"w~z}5x32!"'53254.#"#'###5354632&#":lV!kv4SdIz F\a.N@)- ''/0 **PY*-PY???3+9?+99933333103332654&/.54632.#"#"&'NXbJʳnzt0^~I(ڊSRQXWQ@P"(MnP~HMJK.<*%$=Ja>G32#4&#"#432&#")oTvs{FRXf{10"/VbMD{qR}h>O udDp.@  YY?+/+?393331053#"'52653hzPS9*0%zyykbb4HFg-@  Y ???+999333104'33>32&#"ktJF%NOJq@bGt}r(.@  Y ??+?93993310#'##"'53265tLF%ONq@`It}6@   Y @Y?++?93993310327#"=#"'53265n&&'3&NH$MO9/ qPEt}d D@"  Y  Y ?+?39/3+9933333991033#!3254+ވ]f}z5x\fse.@   ?3?3393393310#&'#373?3u  y| x'~0IR9<U>M1@     Y/+?99?339933107"'5267373t1#98]+܃C/A_XtZˋh  WH @  /9/933910546733%+y_YaC}v? @  /9/993310#65#5?%+y_Y̒aC}S @  /9/933910##.=MY_y,$}F^x @    /9999331026544#*77*TeeT8117UfWWgx @  /99993310"&5463"3UddU*77*xhVWfU71184>@  Y Y??+9/+3/93331032654&#"'>32*iYWh~pb hs 3<@ Y  Y??+3/9/+9333105.54632&#"327Cq~hWYi* ĥsh eHeH!eF@ /3/9910%# #3FeF@ //39910%#3 3fHK !@  [/+9993310#'##53ih !@ [/+9993310#53373hi/9310#3qqL3XS@ Y/]+9910!5!X%/991053ǂ/9910'53ςj/9310#3qqjLChk/9910!5!h%wj/9910'53jςjj6/991053j:@  ??910#'73e ee e:c:?9910#'e e:\ @    /99993310726544#*77*TeeT8117UfWWg\ @  /99993310%"&5463"3UddU*77*\hVWfU7118 @ /339310%!53533{q{l @ /239310##5#5!{q{gl ,@   /9933933310##5#53533{q{{q{ }}l||/39910!5!pw 1@  P` Y/+_^]]q23910"&'33273)uu P@ Y/+931053 3s J@/  Y     @Y/?O/]+_^]]+993310#"&546324&#"326dddclN89NL;:Ldded8NN87RQPU9@# @ HY P`p/]q+/+993310#"&54673327?LijWD4+1-6=pfUF&-;F'*0@  Y    /   @Yyi[I9+ tbRB2"grbRD4$tdVF6"vfVB0 7@tp`PD4$tdTD4p?/^]]]]]]]_]]]qqqqqqqqqqqqqrrrrrrrrrrrrrrr^]]]]_]]]]]]]]]]]]qqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrr^]]]]]]]]]]]]]qqqqqqqqqq/2+_^]]q+3/3310".#"#>323273*TNG76 [ 0Q?,TNEd\d%-%>9fi=%-%wx $@  [/2+392331053353e @    /999933107327#"324P;~U*N#D<(4 - ,@    /3299939210''7'77XXXXXXXX;@  ?3/3933339331033#"&5474&'32ف*,UIJSJ!34ToH(L[[LT@Cc7 %d@ ??93103dz,'@@!  () Y Y?3+?3+999333310#"&'73254.'.54632&#"lbW!=xg[3znSO6APyW4luVk=9v$- 6M9imXci36(2!6T1N3p@ ??399310!#!5!pp$@ ??9/3993310!#!5!3pHp$@ ??9/3993310!#!5!3pH^p$@ ??9/3993310!#!5!3pHVp@ ?3?99310)5!3p0HbIp@ /399310)3!I_p bIp @  /9/3993310!#3!Ittswp}Xz @  /99993310!5!!5!ppvvPN 0@  /329/39933993210#65#5!#65#5%+y_Y%+y_Y̒aC}ÒaC}%@ /399910#373|Rr``r%@ /299910#'#3%r``rRV@ /9993310%5᪪r``rRV@ /999331057'5窪Rr``rW  @  /32993310#"&546324&#"326xPPxwQRv`;--<<--;QnoPPnnP-77--99 B/9910 53ϱB @     /32339910 53!53cϱGϱB @  /2233991053353lx@    /32993310".#"#>323273*TNG76 [ 0Q?,TNEd\dl%-%>9fi=%-%wx~$@ //99//9333105353| @ /299310!#! QP| @ /299310!#5!W @ /]299310!3! jWPW @ /]299310!53!jW$@ /3]2993310!3!53WPX@ /]2993103!!iYȇ:Xq 8@ @ H @ H  /]3/+2/+9310!#53!qVpp M# /10'53ς  # /1053 K  /32910#'##53KihS Y/+10!5!%#T Y/+105!#$  /2210"&'332673xi cLJciw;IG=zZ Y/+1053Zլ{Y/2+31053!53sӥø+ @ p+/H/+q99104&#"'632#5>3*HFZjQ[pE0"c;U 2x8b  /3210#"&546324&#"326xPPxwQRv`;--<<--;QnoPPnnP-77--99 /221053353K /2910#53373khi: /10#3:qqLY/3210#3#36qqqqLL /2]210 53!53$G$d  /222/10"&'332673#5xi cLJciw;IG=z$  /32102#.#"#>wibJLc i{>FI;wd /9/10546733e&({^XjA~d /9/10#65#5d&({^X̨jA~d /9/10##.=^X^{(&~@hd /9/10#65#5d&({^X̨jA~[i/]10'53NςiOj/]1053j?_!@_o/]^]9/310#5#5353oo_sqr^_!@_o/]^]9/31033##ooKrqsy /210#!5!y9> /9/10#65#5%+y_Y̒aC}W_  /q9910"&5463"3_UddU*77*WhVWfU7118LW@ /]2310!53533{q{WlMW@ /]3310##5#5!{q{glKW @ o   /]9/33310##5#53533{q{{q{}}l||9c Y/+10!5!9p֍W /]2107!"'53253U2ExW /]2107327# xE2U3(u WY/3+31053!53sӥW8W  /3q210#"&546324&#"326xPPxwQRv`;--<<--;QnoPPnnP-77--999b @    /]9/10#65#53b,(u^XWu/dZJD /29310!"'53254&+739!1%=HAk'^^bV(*dSLU  /210#"&54673327?LijWD@S1-6=pfUF&$}B*0WCo/]10#3CW^Wy/3]210#5!#!yWPc}@   /^]3322310#"&533265332653#"MD^\a..7A\A7..a\^r&5lLCJEEJDKlWE[ @/^]3]2910#'53373ehiWWA[ @   /^]]2910#'##573Aihlr$v @  /^]3210"&'332673xi cLJcirw;IG=zr$v @ /^]22102#.#"#>wibJLc i{>FI;wWgc@    /^]3]29910".#"#>323273*TNG76 [ 0Q?,TNEd\dW%-%>9fi=%-%wx; Y/+10!5!%iT Y/+105!iW9@ 0/]q9910!5!!5!9ppvvg /2299107".#"#>323273*TNG76 [ 0Q?,TNEd\d%-%>9fi=%-%wx_/310!5!_BV/310!5!VW} /3/310 'ccba//10 #X N\ @    /]q991026544#]*77*TeeT8117UfWWgWy/3]210!3!53yWPDW/3q210!!5#x_Wx纺^@  /39333/310&#"#4&#"#>32>32J7<\<7Jb ZNp9 N6N_ JJEEJJNZ[3(ZN%r% @   /32910''7'77WVWVUSUSli/3299105>54.5467i/PS,1#_s-0# 1?$ M&*15<"DN L$,25; U0/99105!%5!W ffff /10#5x  N /10#5Nx  ]@  /2/2/9910".#"#323273*GAA%/3 e,IDA$[fi%49%mze /9/10#65#5e%+y_Y̒aC}S   /339/310#553!53u| 쬬W @   /^]q210327#"=3>!,&!p y"/3210#5!#!yP W~@/^]]q99105!5! jjjjGW@ o/3]210#3#33W^^5W/]310!#5!Yuf@  /33229910&#"#>3273273#"''Q(c[ 0Q?>O=a=*d\dU;Q=azfi=0l6lzx1m7bp )@   //99//99399210".#"#>32326735353+QLE72 h 0R?,RKD62je7 '!53Y]6!'!72kլp1@ +.&! /22992/329910".#"#>3232673".#"#>3232673+QLE72 h 0R?,RKD62jeU+QLE72 h 0R?,RKD62je '!53Y]6!'!72k '!53Y]6!'!72kW //99105#'735vvOW@ /q9/3105#7#;v|W %-5=EKQYaiqy@ZNGG0488<ITTX ll$p (tt,x@D \`dhP|4557449 885667775558849*557sNsmmIt}! /91057'5窪xRr``rx\  /2210"&5463"3\UddU*77*xhVWfU7118$ @    /299//3102#.#"#>53wibJLc i{>FI;wO%B @   /32910''7'77WVWVUSUStV/22910%5r``rRtV/3291057'5窪Rr``rV @  /329/291057'5#'#3窪r``rRRr``rx]  /321026544#\*77*TeeT8117UfWWge  /104632#"&8''89&'8'89&'88AV)@   /]2993399333107''7'7'3 ]CKNB^ S3K%z)~)y%L5WE+@ # )/3//3/9910#"'#"&5463263232654&#"4&#"326EiNY33YOhiNZ22ZOh=,,;;,,==,,;;,,=NeIIeNNeHHeN36=,,=6336=,,=68g4 ?10#7!3_q@_q@SW  /3310! 332$7^JK^6svvS2 /3210! 332$7^JK^2svvS/310!5!JSU/310!5!JS /329910#"%$#"#>32327-^t-{Y͑9VUT\(-VS6  /3210#&$#"#! ^^0/ÂvvsSW//99105!5!5ϖlvcHi= ?9/1053332#4&#"#3)oTvs{FRXf{{bMD{qR}h>ppc@  Y ??+/104'33>32&#"tJF%NOJq@bGt}rWw7Y{O @  /3?3910 #33DŽ :r(v6/991053n uWx/9910#5n߈W6>Nb@A  PY p`p o  PY ?+3/_^]?3/^]q+99333104&#"'>32#"&'7326ir٧`"jZẜ lhWN&FG(@4Td%+5/_]]5>N&5(@4Td%+5/_]]5:/991053E!$%A :@   Y ?/]]33/+393331053%53!53EؖB!p%R&$9l|- @%/P0`?5]q]+5~@@w|HRb 0@p@ goHDTcfH\_HW[H@QVH$4D@CFH0$<@ 8;H@+-0H  0  HY/++_^]]]q_qqqr+rr+^]]]+]]q++++r+^]]_]+]q991053'(9(|-@ q@%/]]_]5+]5?5 '+9%|6@Pq@%/o]5+]5?5]U',9|@@ + 4 f@%/O_o]]_]5+]5?5]]&29|%@ ^@ %/]55+]5?5A'<9t|@ (HXhx8X Gg77WgwWw<'g'7GWw '7W 6vV&&Vv@6fv &F@Vfv%Eq%5Ueu %5EUe uA$DTt tbsx   @ ` p @ %]5+]5?5_^]^]q^]qr^]^]qr^]qr^]q^]qr^]q^]qr^]qr^]^]qr^]qr^]&[ 9|6@ +&$$$>@$$!!%_]q5+]5?5]A&k:@/?O/?Oom/8/_ @Ps p= &ܴ %+555+555^]qr^]qr^]r^]qr^]^]qr^]qr^]qr^]qr^]R$%/@_Y??+99310!#/8=>@!   _Y ?2?+33_^]]93333103!7%.'C#3 + ^J oj(A= +aO@+  `  _Y  _Y _Y?+?+9/_^]+_^]9933910#"$5!2#"32!!שŦrJ< KMR},-f|,?. N>@$ vo 0 /  ]]]]?2?3]9333310%#'#3N|TTz? 0 1Z ;@  _Y _Y_Y?+?+9/+99399210!!!5!5}9\a2 ?@     @ H_Y?2?++_^]q993310!!#!ax 3l E@#   _Y  _Y?+3?9+3999932231035 5!!!lmCC|.7-)<u(~@, $ )****$*D*d*****@ H# #`Y &&`Y  ??99//3+33+3+_^]_]]]]9333333310+#5#".54!3533 !#326%;#"SS~HG0884o.+;|@9 D`DT 0 @ H `Y  ??339/]3+3+_^]]]]]]q93333310!#".53;332653+SǬ88ÿS-Iǯ1UW#S@*  ! !$%% _Y _Y?+?3+33333_^]993333993310 ?3!5654&#"!53&5/[QQ[s_`qU&O4&, @&%+55+55-) @     Y H vbTF6$pd@4$it`PD tdT@0$tTD$9pdT4$@8T4p`0/]]]]_]]]]]qqqqqqqqqqrrrrrrrrrr^]]]]]]]]]]qqqqqqqqqqqqqqrrrrrrrrrrrrrrr^]]]]]]]]]]]]]_]qqqqqqqq??39^]332+393310#3 3%53!53 ӥHH9ayVe&c9!@ '&''*%+5+5Fj&g9@ '&'I'* %+5+5jX&i99@ &d %+5+5&k9@ &    %+]5+5A&w:!@ &"%+555+555VeN&U@- !!('(( !  $PY PY?+?+??9_^]993333333310%#"323673#.'3267.#"K?y~-22T;/#rxwyyywtjbg=!1WI)Y@- # +*  PY &PY PY?+??+9/+999333339910#"'##463232654>54&#"Iܷߔ@Qqyn^rXܷW+1zbpX:M@   @0`@ H  ?2/?933+]q933333103673#674z": @:]?C?[ZtV ?@  !"  PY PY?+?99+39933932104.'326'#"5475!`5ST54.'.55!5!0]6oZ:$/~ *Ss[O7={CS5#"9XD,mO8R$0$&5KeBa#jXNM@  @H PY  ???+9?+_^]]q99333104.#"#4'33>329*\Y$>yXVkv4S{l'i)p]j  U@`@#HPYPY PY?+?+9/+_^]+]99333310#"!22!"!  *#}}t : @    O ?  j   p `      ? `   7     ? /     o      ?   ^]]]]]]qqqqqqrrrrrrrr^]]]qqqqqrrrrrr^]]]]]??933103.53 +% ENAz(: A@%   ?  ?2?39]]39333310!#33 0Im: /D@' 0`   PY ?+?399_^]9333310.#"'632#&'#D++6'#C>kK)+żd; Apf.kw:L@ @H PY?+/?3?99+_^]99333310!.'##"&'##332653^54.'.VܖJzM0]6r^=$/~ -Xs[O7b! qg}kEZ4 CS5#"9XD,mO8R$0$&5KeVNRO+:J@(  !!/! PY PY?+??+33_^]933333310"&5!#6="5>3!#37fsfn4.4:Bk4%*4VMqH+P SLEW;OR@   @#H  PY PY?+??+9_^]]+]9933310#"&'##4324&# 326;n@Վ~7XDK54&'.54>32&FRZn$/~ dMOoeAt_zy?#F,mO8R0B,0qo^6DwdV:/@   PY PY?+?+39933310#")#''#"!260,9QG|Y* %yzc:L@  ? @HPY PY?+3?++_^]]]q9333310"5>3!!37#"&5Bk4D*4VMBsf SLEq:C@ `@H  PY?+?3_^]+]]q993310#"&5332654'3ɵu}F,5:;c.UvUWR"V@.   #$$ PY PY PY??3+3?+3/+_^]933333310#&4746324&#">|WP5i  * ڸDXPp@ $  @(H   H PY ?3?+?9+_^]]^]]+^]]9933922310%#.#"'6323 #IC&"5AVkPi9WJ dGW,<`@:   0`0@p PY  ??3+3?33/_^]]]qq93333310%>53#$&533.򵐸w|kzSO*z@ ""((+,,0,,,@(#H,,, ,@, )) %%PY PY ?3+3?3+39/9_^]]+]qr93393991032654&'7#"&'##"47326=3srfrqzeϾ~""~worfvϹ灊@ λ{&kj@  &ٴ  %+55+55{&wj@ &%+55+55V&R9)@ &F%+5+5&w9@ &%+5+5S&{9@ +&+7+.%+5+5s#'1^@4.("  23%PY0PY 00 00 +PY QY ?+?+99//_^]]9++99333992310>32#"&32#"32654&#"4&#"32;&*c֦l^Yy\NN)s*tK]<${o=tQrƑ6=HHO P+3]@2,**  1$$ 54 PY , PY*,,,''/QY'PY?+?+99//93+3+933333310## 47654#"'6323265&$&5463234&#"Pa:1`jO]hRFg^̪av 1]]#<16cX2]]2BI;av{9 .@     _Y?+??99933102.#"#3 > <;T^%$1 rT7x-3-0H9aVG'|2#?59 & y@ &%+55+55PW+R@-   PY PY PYPY??+?+??+?+9333333310&5%34&'>%_pzzpktujW4q .Zǽ˷+R:-[@/+ (""(/. %PY)) PY PY?+33?+9/?+993333333310!##"&'##"467#2654&'!32653Rl09Ͼ~""~63itt>01-N@+ *&! ./'PY'#SY SY ??+?99?+?+933333310 !4#"'632!327#"'5326=.547tw8"BK|z6$  oyw*2@C+578ca5"PiuI DQdBaW2@  _Y_Y/?3+?+933310$!2#"32rJ< W<R}ep,-VWN 3@  PYQY ??3+?+933310&!24&#"326ܺЅW0erP:@  _Y  _Y ??+99//+933310!&$&54$32.#"37%#X3ߓo =  C~ovIZWN,;@ & .-'# #*PY# PY ?3+?+992993333104.'.532.#"#"&'5326(PgVvi,D\9mW5]2nDvvw)6%ԗ ZjKkL3% B6u˄0{~xt9cVi1@     ??993333933310%54''%&''%%7i.7,F~-GGpj6ΒwqU3X}+Q@*$!* *!,-+"'`Y`Y@`Y?++?+?33999333331032653#!5!26=##"&'## 33265]st`ӯ桙*z{%^[tw |gYXWVc} ~X:-O@)&#, ,#./ -$PY )PY PY?+?++?33999333331032653#!5!26?##"&'##"&533265VpsaӮI8p{8qVpv:Rxs2lm`bkm`Rxsd7@ _Y_Y  ??9/+?+99392310&#"32673##"54632HUww^IݶTx346$~S'&E@& PY  SY ??+?9/_^]9+933310!#"&5!2&#"3265393|3U:=u*`U)[P(|ro4~4X8@ ! _Y_Y?+?9/9+99333310!2>54&#"#3763 #!.Z1UWF$7j"MВ0*P:y> '2]@3, 1$ $ 34 1"/"PYPY// PY (PY?2+?+99//++9?993392210267#".#"#46323 4'# 54>"327&p4#|H%crɅC_H7=mTOWVO "bSq=@7>54&#"'6$!232654'7! $n+NlOl?05c`r?ǻ5CMsT=-($6Q?nqep!ƳNz[K%&8VEzYh6zun,E@#&.-&PY,,)PY?+3/?+3/99933310%! $54>7>54!"'6$3 3267+NlOl?/ 5c`r?ڽ+?۾MsT=-($6Q?il!öNz[K%&8VEy("%V@0 %#$$ # '&%  _Y $ $_Y _Y?2+?+33?+399339102'&#"!5.#"'>327>!L@f"FA)1|;6|2)AE#f@L_t(CB%t>eln3f&.*.&f3nle]qma HA#O@- !$% !PYPY PY?3+?+33?+399331032'&#"!5.#"'>3276cmL8h!B$<@!! %&SY SY ??+?99?+9333310 !4#"'632!327#"&547tw8"BK|z6$  oyw8"BK|z5"PiuIBbWDN,@@!  '.-PY  *PY $PY?+?+9?9/+999933104>32#"'32#.#".4.#"326b6wz`B$O{E%?}paQ없H$VY =lUWNFW=MaP9:G@%  PY    PYPY?+?+9/_^]+99923310)"3!!"!!)9w# 'P9:E@% PY  PY PY ?+?+9/_^]+9333107! !5!4&#!5!2#!P #wUWhy& 9@    ?3?9/993?9333310!##3 3VE=l(UW:5@ ?????999333310!##!67!#?4. Gk}rs W;O&Q@)   '(PY @ $PY?+/9/3/?+9933333310#"&'#!!#5#534324&# 326;n@?zzՎ~7XDK32!"&'7326=4&#"#!5!uT^Eo!!24&#!!26 `13_:%5C:::*~or A)ByP@*  _Y _Y  _Y?+??39/+3/+933333310#!!#3!3!24&#!!26¿>VGsTy./e@<  ` _Y  _Y ?2?+39/3+3_^]]qr993333310!4&#"#!5!!$3 q ?zm2 圜;&@ &% %+5+5&T&%+5+57:&^J@ & %+5+5h G@(        _Y/?+3?3/_^]]q933310!3!3!""hhR$ _@  `@%#H _Y 0  _Y_Y?+?+9/_^]+_^]+]99333310#!!!!24&#!!26!~oHy%/FhEL@*   /  _Y _Y/3?+33?+_^]93333310%3#!#36!! 2Nm%CԺ1%Z?h8a_#Ae(G#@Q #""# #%$/%% %0%@%% !`Y/ #?33?339/]q3+39933_^]^]_]9333333993339910"&'#&'3332>73###R%oU0o`XKKZbk0Um' U#ϧk(k)o1f# {Cp'@B" %%()"  _Y? @ H_Y_Y 0p H?2/+]q+?+3/+9/_^]+9993333339910"$'7!2654&+532654&#"'6$32mVwGG~3FMvw{k{u=ǩ  8@  /  ?22/?3/3]99333310333#47ުdrzZ:&J@ &%+5+5P@)     `Y/ ?2?39/]+39_^]9933333991032>73###KZbk0Um')o1f# {7@_Y _Y?+?+?_^]93310! #"'532>!#^13_:%5C:::*r A)B0 +a2S@   P ` p ]q]3hy&.77=@    _Y?+3?3/993393310"&'732>73 3L>.Q^H3EFfUj}& ;'_z8v(j@< # )**`***?** $ $`Y &&`Y  ??99//3+33+3_^]]]q9333333310+#5#".54!3533 !#326%;#"((~ W o.+;h A@!     _Y/?+3/?3/_^]99333310%#!3!3ŴP@ @H _Y/  ?3/?9/]3+3+_^]q9933310#"&533273#Gھ"R{l1 U@1      p @    _Y?+3/?33_^]]_]]]]]93331033!3!3h/W@0  0@`/   _Y/?+3/3/?33_^]]]93333310%#!3!3!3/-ӺҺ. E@$   _Y _Y _Y?+?+9/+_^]99333310!2#!!5!4&#!!26B5s3-Ҿym R@  @ H_Y_Y?+3/?3/9/++_^]99333310#!3!24&#!!263~oy B@% o@P _Y_Y?+?9/+_^]]9933310#!3!24&#!! ~o@yiyy@J  _Y_ _Y@P  _Y?+3/_^]?3/^]+9/_^]q+93333310"'6$3 # 7327!5!&$ɘ-G.BlVE rv<tNa@8  ! !_Y _Y  0   _Y?+??9/_^]^]+?+_^]93333310# !#3!! #"32ٿ*W6\f;s$E,+`  Y@0     _Y?Oo _Y ?2/?+9/_^]+3993333393103.54$3!#!3!!"`I;_֝IWsNDx? "W@ $#$$$@#H PY   PY?+/_^]39/9++_^]993333104&#"3262#"4>%>.b~]d+ʿûBN|L56b}:!x@  "#0##@/#H#/## QYo PYPY?+?+9/_^]q+9_^]+q99333339102#!32654&+32654&#4rmCpw؀mfy:]|k:KU]eSHZRL:@PY??+99310!#:I:h:F@$ PY PY  /3?+33?+9333333310! !#!#36!3B%Z@FңwKn.mh}nIWNHS:#@I ""##$%%%p%?%o% %%%%P%`%%/%% @) H!PYO_O #?33?339/^]q3+399+33_^]]]]]qqqqq9333333993339910"&'#&'3332>3###SAa#]ؼBH;.04Bü]#aA PEj]$&3\; #1^N$@R  ""%&&  PY    P`pPYPY`p ?2/]]q+?+3/_^]9/]q+9_^]993333339910"&'732654!52654&#"'!2ʧ-5`qc]Yq .KΑgy},^[ȉTXJVJF~e  m: F@+   p   ?33?33]qr993333103#467#< Ŭ:e$:&@ & %+5+5:d@9  /  PY?O?  ?2/?3/9/^]q+3_^]3333933991032>3###04Bü]#aA:&3\; # :M@3 pp 0`p PY PY??+?+_^]qr93310!! #"'5326!h;@JmV2,+7C7#,LϜJ nE:F@&  ? ?333?333]]933333310!##!67!#?4. G,:k}rs: g@   @ P ` @   @!HPY? ?2?39/^]qr++_^]qr99333310!3#!#B紴:6:VNR:C@+  `   PY?3?+_^]]qr993310#!#Ǵ/:I:WMSWNF#:;@" ?    QY?+3?_^]]]]9310!!#!#d:IW:\VW>%2@@g3:,, & BA ##6PY#0PY)PY >PYBBBtBdBTB4BBBBdBTB@B0B BBB]]]]_]]]]]]qqqqqqq?+??+?+??+9933333310#"'##47##"!23'33>32326754&#"4&#"326>O.omp-.rmvzwylnkx^j7zwn"WSYmChZ6Zb\hY\ɿ:[hf: :@    PY ?2/?+3_^]99333310!33#!Bݴ:II:z:8@   PY?2?9/]3+399333103273##"&5.nN~:n/8: W@!  d 0     @ H PY?+3?33+_^]]_]]]93331033!3!3:IIhf:c@;   `@P?    PY?+33?33/_^]]]_]]]9333331033!3!33#:III-: i@ @'#H QY   O   PY  QY?+?+9/_^]q++_^]^]993333102#!!5!32654&+ gʂxtp6\c^W2: R@0  QY   O   QY?+3?39/_^]q+93333102#!332654&+3&gʂxt<p:6\c^W :: j@H /Oo @#'H QY   O   QY?+?9/_^]q++_^]q99333102#!332654&+8U܂xtp:6\c^W7N@B  PY  o     PY `p @""Ho @ H PY?+3/+_^]?3/+^]+9/_^]q+9333331073267!5!.#"'>32#"&7d c ߫; lhNO@+   PY PY    PY?+??9/_^]+?+93333310#"'##33!24&#"326 ޴!׽x~|:6: S@*  QY QY ?3?+9/_^]+399333339310 #&463!##";Xxk|63\]^]W&HC&ʹ %+5+5W{&Hj@& %+55+55 W){@A )) * ++@+P+`+@+++0+@+)PY QY%PY H??++9/3+3?+?9_^]qr]3933333103#5353!!3>32#"'52654.#",:}xxM2>F6*`U= jcљ Hhro4&v@ &U %+5+5WN@\  PYo  PY /   @ H  `p @ "HPY?+3/+_^]?3/+]+9/_^]q+_^]93333310"32&#"!!!2674 ҋc d!ħgm 9KV=L%{&j@ &%+55+55W=M :#}@ %$ %%@5#H%% #QYO PYPYQY?+?+?+9/_^]q+_^]]+q933333102#!! #"'5326!32654&+M>e;@JmV2,+7C7#,xtpLϜJ nE6\c^W,:z@  `@1#HQY PYO  QY ?2?+?9/_^]q+3/+_^]+]93333331032#!!#3!32654&+fNʂxt:6:6E\c^W  o@<!"@"P"`"@""""0"@"QY  PY H?++3?3?9/3+3_^]]qr393333310>32#4.#"##5353!!=:}*`U,YjcWro4= &v?@ &Q %+5+5&C& %+5+5W&\@ &  %+5+5h: L@.   @ P ` @  0 @ P  PY/+3?3/_^]qr93331033!3!#:Ihr F4Z@. '-!!65$$*_Y($1_Y_Y _Y?+3?+?+?3+9/99939333210332#"'>3 # '!"$5!2&#"3267J#tMϓBllBMtO2qLV;,h|GANQ~AG|hXR:8@     ?3?339939333310# #33654'2o S\0:y]x:nnKzM@)  _Y`Y_Y??+99//3+3+93333310!53!!!2#!!4&#!! ut@דҾyK@(  QYQYPY?2+3??+9/+93333310!3!!!2#!!3254&+-*:t:o娟^f@!@K!!  "#@P _Y _Y   _Y/??9/]3+3?3/+?+3/_^]933333393310333! .#"!!3 # #a+2Гz$c1N<~$Mñ]5xN!@Z !!#" p`p PY PY o      QY/??9/]3+3?3/_^]+?+3/_^]q999933323310333>32&#"!!!267#"'#֫ ҋc d:+ħgm R @@     _Y  ?33?9/9+393333310##3###!&'T?6мY 3'YEW: @@    PY   ?33?9/9+393333310!#####3 tpBpp:n$R@*    `Y ?222??9/93+339333333331033!3#####!!&'n)6м 3('YEW:Q@)    PY ?222?39/93+339333333331033!3###### !tp:Bppqny)Y@-     `Y_Y ?33?+9/33+3933333333310#54.'##5%! ![Ƥ[  GCx/ʹ\r\ |}z:_@0  QYPY?33?+99//3+3933333333310!#54.'#"#5467!!;|>ȔMOFDQEFFkƯi~!k@7  ! #"`Y!_Y ?222??+9/33+3393333333333331033!!#54.'##57! !GE[Ƥ[Cx|/ʹ\r\*|v:!$m@8 "#!! &%  PY$#PY ?222??+9/33+3393333333333331033!!#54.'#"#5467!!BȔM;|>8<O:kƯFFDQEFF+Q~`WpT@M)&6KA@ &,,&@KUVC=_YC CCH)`Y2OOH#AHH8_YH#@### `Y#?+3/332/2/+39/93+93/_^]+9333333933104&+532654&#"'>737>32&#"32>32#4#"#"&54>7>GG~37[H6S-B X0Rsm;:kc]-^mJVl~CTlW?#vw{k{u=SC8M> [\;$ 3)l!dM"zWn>%>Y WM@LDC1 - *9*1CONF@PYF FFK5- PYK 'CKK;PYK#'@'''QY'?+3/332/2/+39/9+933/_^]+9333933233104>7>54!52654&#"'6737>32&#"32>32#4#"#"& Hk9c]Yq $Լ[H6S-B [pgyRpO*:\RN-[jJEWlD{Nc<-L=ȉTXJVJF,YC8M>fe  m_P.$t$}hM#C<@  `Y ??339/3+393333310!".'33332653+cr\30PmP8¿S6k-GD̯;UDW:<@    PY  ??3+3?3393333310%>53#.'33w\(#TWwrkQRia.P@+,!0/*_Y$_Y$ *$*$ _Y _Y?+?+99//_^]++99333310#"$5!22$7#".#"">32327&שŦrJ}=Q>}LOq7vcMR}4+5+Is("$%!9VN (N@*" )*PY$ PY $$PYPY?+?+99//_^]++99333310#"!2"3267#".%27.#"632xE= I64WNJ&?= >?2OKO!0#ð !)!!!(! V&@ _Y ??9?+9310!#3?632&#"(,;-LK0TMa37 iN+7)E3GN&@  SY ??9?+9310!#37632&#"ew 8JBrQAT.8:@( [?4L VL&%mh@  &>%+55+55&&@ &Q %+55+55aW (O@*"!! $%% )*# $$!PY _Y _Y?+?+?+?39993333310#"32#"32"'532?3 3J2&.bg2tVwWS|-+ +5vUWN 'M@)! $ ()"## PYQYQY?+?+?+?3999333310# 324&#"326"'532?3 3d|˻crsifnwfFJ2&.bq(~P2 +5v6t0J@&% ("." 21""_Y+(.( (_Y ?33+3?3399+39333310%>32#"&'$>32654'#"&'6X'T43U(YݾV56UX87YZ89Z3+55+~-87-30<<01/1>>1+-I@$#,&   /.  PY) ,& &PY ?33+3?399+3993993321047>32#"&'&7>32>54&'#"&'+R43RR33QU65S{ts{T55S-77,,75+$00<:.ն.:9-7l 1EP{@A2+LFOO< RQE223 # '!"$5!2.#"326732#""&'.#"#>323%#65#5HNR@jˡj@SMKCg5AA4hCferFv ߤAo\ %jSNW#mmLR}# VEEV?, 58?.mp*@91b*<4:bGWw%0D~@F1,/'../;FE1;//+000;++;0>>7QY>> >PY #PY ?3?+?+?3/_^]+9///_^]9/39933933221044#"'#"4>3"3267326#65#5"&'.#"#>323p㻃pٕX1-Z %jSNferFv ߤAo\"ĖSS:-+<rb*<4:bG58?.mp*@91r F& Ph??&@@5!%+5+5& l@  &!! %+5+5hUl4@  _Y _Y?+?3/+933310$!2.#"327n@.G1ٕ(,Uy7T<{ PWL1@ PY PY??+?3/+93310&532.#"327ri=.W" Zj ?@     //993993999333310%%#%7%73)Nd))\M)a~^_`5]^^_{` ?3210#!#"&5463!54632`I5C.2BI5[C.2Bq2<8<@42<8<@L  /333/310#.#"+53267632LY55!B&thfi4X*aFcgJ@&H .}Ib /9/10##.=bYNw#̺^k3rLIb /9/10#65#5b%wNY̒Jm:k^;.  /32210"&'.#"#>323.ferFv ߤAo\>58?.mp*@91% '1;EOh@5F#K>C HH%M2 7 449(-**/KCM79-//-97MCKA</3/39////////33333333333333333333104#"#324#"#324#"#324#"#324#"#324#"#324#"#32%4#"#32rrgrrgrrg-rrgrrgrrgrrgrrgĕ敕&k '1;EO%@OME@C0'%  /3/3/3/33??33/310#65#5546733#"&'5353%32&##&'''.'77'67'77>7EdBKdBKjzD])XQzD])XQ=V0/ G95nEV0/ F95o V1S1Gl:5nV0T1Gn95m;zD])XPzD])XPdBK4dBKW0T1Fm85nV0T1Gm95n>V0/ G:5nV1. G96mf:\@1  _Y p ??399]29?+993333333333103333##47  33273X dr,zZ@[@-  @ SY ??+?99?299933333333331033##467#% 33273< ٬y :Ceg(77:v@N@(  _Y`Y_Y??+99//3+3+993333333103533#!2#!#4&#!! +@דҾyN@(  QYQYQY??+99//3+3+99333333310533#!2#!#532654&+ww ?z.xtl\c^W\@0      _Y_Y??+99//3+399933922910#!#!2)!27'76(wbQVtks~MslflGʟn}`-|ao6W!M-[@1*,$$ /.  PY),++''PY?3+39???+999933333910'#"'##4'33>324&#"327'76^bd^WyV0ƽzky?X8ve_#"jZe6Y61fd]Z*[hb#@ _Y/??+9933103!# %@ PY??+/993310!#!3!B٣8:/ =@     `Y _Y?+?9/3+39933933103!!!!##8\&\@m: ?@  PY  QY ??+9/3+3999222310!3###53˴zz:τW O@)"!_Y_Y _Y?+9?+9/9+93333310#"$'732&#"#!!63 <-α:qj_& !ZM% %1[W: P@*"!PYPY PY?+?9/?+9/9+93333310%#"&'732654.#"#!!632tӋ.yfUbtfaSts|\{ TKpV c:t#uhN'|@@" &&' ' ')("%`Y! !_Y! '?3?33/?+9///3+3339333399333339910"&'#&'3332>733####R%oU0o`XKKZbk0p1Um' U#ϧk(k)o1f# {hS:'|@@!#  #)(!  QYQY  ''"QY'% ?3?33/?+99//3+3+33933333399339910##"&'#&'3332>33##uAAa#]ؼBH;.04Bü]#N% # PEj]$&3\;3CWp5o@:(%%+0 +763`Y3.@(`Y".. `Y .""_Y"?+3/?33/+9/+9+9333333399103254'.'7!2654&+532654&#"'6$32#"'7YVQwGG~3FtsS#PAMvw{k{u=ǩ |R_n"1W^N3r@;%" ."")) 45%PYPY1QY1@, PY ?3/+3+?+3/9/+993333333991032654'.'732654!52654&#"'!2#"'WN@;,5`qc]Yq .KΑgy!wxgT"=?OG|~,^[ȉTXJVJF~e  m}kVkq!hQ@( `Y _Y?2/?+99//+?99333333991032>733####KZbk0m.Um')o1f# {h:T@+ QYQY QY?2/?+9/++?33333933991032>33####04Bü]#N%A:&3\;3 #\@.   `Y   ?3?39///333+339933339933310##3273>3##:1@+s!v0s{'.f#k:\@.    PY    ?3?39///333+339333339933310##32733##%(iݼ]#ai#:& IvU@+ `Y`Y ?3?399//3+333+9933333399103533#2>73####KZbk0Um'ד)o1f# { [@. QY QY ??9/3+3?39/3+33339333339910353!!2>3#### 004Bü]#aA93\; #)N@(  `Y  _Y?+??39/3+39333339910!2>73###!)KZbk0Um'?)o1f# {(O:G@$  QY  PY?+??39/3+99333339910!2>3###!(04Bü]#aA:&3\; #hC@#  _Y _Y ??3/?+9/+93333310!#3!33##asThW:F@$     QYQY??3/?9/++39933333310!#3!373##)y:6G =@  _Y _Y ?3??+9/+9333310!!#3!!!a@sT : =@    QY PY?+?39/+?9333310!!!#!#BӴ:6ʋQ:W!S@+ !! #" _Y _Y_Y?3+9?+9/9+933333310!!#!63 #"$'732&#"ax <-αJ[ZM% $31W:"V@- !!$#PY!""PY" ! PY ?+?39/?+9/9+933333310632#"&'732654.#"#!#ts|tӋ.yfUbtf":#uޗ{ TKpV cN:>+7Z@3 //5$$'2 892' ,_Y  _Y _Y)_Y?+?+?+3/+99333310%#"'# 4632&#"327&5432327"654&kȀ眄WOFkZRQhII@ 5j{2DGvy0D07./46/.5#7%%-Nu޲'xvti44hWy'v@E%$$  )(% %%!!_Y!`Y @P  _Y ?+3/_^]3/+9?+3/_^]93333310"3 #"'53254'&$5!2.(KtsSYV֝lB.G1%N|R_n"#PAHQ~<{WWN(@R# *) p`p &&QY&!!QY! PY ??3/+?+3/?+9/_^]q]9333331032654'&32.#"3267#"'WN@;ri`{"wxgT"=?OG Zjhl wNkq!.h 4@   _Y _Y?+?+3933103##!5!Ќ圜#h: 5@    QY QY?+3?+93310!5!!3##{dt_ "@   ??39933310#373<&--'HH9gfAW: @   ??399333103?3e 8:]W8P(5W_6@   _Y ?3?9/33+393333310!373!!#!>F&--'FffgfA_HW:6@  PY ?2+3?39?933333103!3?3!!#!d>e 8:]>´:P(5Ɗ.hBO@*      _Y ??3/?+9993333333910 # 3 3 3##P}6h)bh:O@*      QY ??3/?+9993333333910 # 3 3 3## *d.D,[U1h9>@!    _Y _Y?+3??+3933310!!!33#!!1&h^;?@!    QY PY?+3??+39333310!!!33#!!&Lݴ;Ih:<@  _Y _Y?3?+9/+9333310#"&5332733##Gھ"R{l1E&:@ PY PY?+?39/+9333310###".5332733nJ?Y8ft-4ouɅaD$6nV@-  `Y  _Y  ?3?9///3+33/+933333310"&533673##W۾z{xj{³Rxl1 ") z:V@-  QY  PY  ?3?9///3+33/+933333310"&533673##6pBvwApyn 0"/-@  _Y  ?3?9/+9933310!4&#"#3$3  ?zm2 ;K ")^@1& ' *+_Y&  #`Y _Y ?+3/?+99//_^]3+39333333103267# #"&5473;!2"!& Λ9RCvz'f!4_ ryFU1:;;2p.X$?  N$V@,$  &%  PY$ !PY PY ?+3/?+99//3+39333333103267!"#"5473;>3 '.#"ua 4zݺ^H-=>;2p %,m@9)) *## -._Y)  &`Y @  _Y ?+3/3?+99//_^]3+393333333103267#$#"&5473;!2"!& Λ9IÆCvz'f!4_ ryF$S:;;2p.X$?  N&k@7&   '(PY& #PY @  QY ?+3/3?+99//3+39333333333103267#$#"5473;>3 '.#"uXۆr4zݺ^H-&(=>;2p|,G+&];@ $$&&&,%+5+5S&Y@ $$&&&,%+5+5W"S@) $# `Y _Y?+9?39/+3993333333910%#"$'73254$!##32>73<.~ǿKZbkp45Ѽߝ]Ox)o1f2W:#V@+!%$!QY PY?+?9/?39/+3993333333910%#"&'732654.+#32>3rҎ.yfn|04BüT&\z TK] :&3\;|$fR:@ _Y_Y _Y?+?+?+933310! #"'532>!3##^18`~:%5C:::*Z A)B, ::@ QY PY SY?+?+?+9333310#! #"'5326!3Y~;@JmV2,+7C7#,Ϊ7LϜJ nEg(W F@#   _Y   _Y ?+9?39/+993333331032!#3!3!"$'p.Լ9h\NsTlW:M@&   PY QY   ??39/+?+9/99333333103265!#3!3#"&'Ilk~xų"VJ:6f=@   _Y   _Y?+??39/+993333310#!#3!33pfsT,:C@"   QY  SY ?+??39/+9933333310!33##!#B紻¬:6g(7:h@@! _Y @_Y ?3?+9/+9333310%3#"&533273##eGھ\"R{l1hzh:<@   PY  QY ?+?39/+93333103273##3#"&5.n}lN~:n/hJ8f"G@%!#$"  @_Y?+?3?9?933333310%3##4?#./#3>73ļ (' Dw%3 p}"k@pT/;(:G@% @SY ?3?9??+933333310!##!67!3##?4. ¬zG,:k}g(7rs>OR&$ z@ &%+5+5Ws&D11&4Դ4:%+5+5R&$ n@&%+55+55Ws{&Dj@ 88&6д64%+55+55BN&(  & %+5+5W&H@ &# %+5+5^QWN^&Q @ ''&%%#%+55+55W{&j@ !!& %+55+55G& m@ ++&))'%+55+55S{&jk@++&))'%+55+55Cp& )@//&--+%%+55+551^{&j@,,&**("%+55+55MT@+  `Y_Y_Y?2/+?+39/+3933393310"$'7!2654&+5!5!wVw?7M𡇅q ܲuDW :T&I@ &%+5+5S&@ &  %+5+5& @ &%+55+55{&j@ & %+55+55a&2 @""&  %+55+55V{&Rj@&%+55+55a?@ _Y _Y _Y?+?+9/+99333310#"$5!227!"!&$שŦrJ~_τz.WKG@& _Y?+?9?99333333910#"'532654. # 3 3 K;I:=5CUP}LtX 4@ _Y  _Y ??+9/+99333104>3!3!"$7)! XsܖH{}m%,VG[9 &N@(! !!('$`Y$ _Y `Y?+?9/9+3/?+993933931032>=!"#"$54$3!332653#"&|UL~q後QnbW⻾T7śEoV{)U@,$ $+*  'PY !PY PY?+?+?+9/99?9333393103# '#"!23'33265%32654&#"Ƶ_:Ȏ{2q~~pMxyzib6Zby韟j`")Q@* $''*+`Y%%_Y"`Y?+?+3/9/3/+99333310"&54&+532654&#"'6$323 3dݾGG~3F{|~tw{k{u=ǩ :EUN&Q@* !$ $('PY"" PYPY?+?+3/9/3/+99333310 .#52654&#"'!23253O.c]Yq .KΑgz dKgUTXJVJF~e ca{t=`h#S@+ #! $%`Y!##_Y# _Y?+3/?+9/+9933333104.+532654&#"'6$323##JGG~3Fujz5w{k{u=ǩ UhNU@,   ! PY PYQY?+?+3/9/+993333310%3##4춮&#"'!2_wc]Yq .KΑg%jdTXJVJF~e A"C@#  #$ _Y _Y `Y?+?+?+9/933310"&5! #"'532>!32653^18`~:%5C:::*xwTZ A)B6E : C@# !" QY PY PY?+?+?+9/933310"&5! #"'5326!326=3;@JmV2,+7C7#,~y&LϜJ nEzÞG@$   _Y  `Y?+??39/+3/93333310"&5!#3!332653swsT6E:C@# QY  PY?+??399//+93333310!3326=3#"&=!#B~x:6zÞ\:g*^N$.t9@  _Y `Y?+?+39/93310"&5!5!!32653twXҞE#:<@  PYQY?+3?+9/933310!!326=3#"&5!#du~~x뿾:&R&Z@-#$$('`Y _Y ##!_Y?+3/?3/+9/+9993333339910"$5467$54$32.#";#"3 [槛G4{GGuYCФ=r{k{wvMIvN%^@/ "# #'&PY PY "" PY?+3/?3/+9/+99933333399310"&54675.5463 .#"3"327ݿՖ|kѥK. pZ]coe5-l  cFJVJWUfaTf,}W;@!_Y_Y _Y_Y?+++?+93310 #"'532>!#"'5325=18`~:%5C:::*;@,B{Z A)BX W:5@   QYPYPY?+?+?+93310!"'53265! #"'5326!=@,D7:;@JmV2,+7C7#,΁CQ2LϜJ nEa}4VWNT ::ZD!@ @P`/]]29/3310#54&#"#546753f&03'dCFfHC/6..6/1XZ ^^ [W+N #(@ @ H!>?3+]q223104632#"&74632#"&4632#"&+. ".." .. ".." .r. "--" .o -- ".." -- "..".." 00mN >?10#7'7'7vVVVVVVVVVV^N "@ @ H>?3+]q2104632#"&4632#"&`. "0/# .0 0/!".m"--" ..".0 0.`N (@ @ H>?3+]q2104632#"&4632#"&3'." 00 ".. !/." .om"--" .. 0/!"...nN/]2103"&5DtfR\ijhX >?10'7P+! @    >?]99//3310"&5463232654'7#"&'.%9mRA/Q5(7 %9mQ>2S4,19&%@H&xAR5J9/&%@H)uAR6I>*N>?]21044oqhN\hmB/10#'73B)@ @ /3]29/10#52654632#"&ev. !/." .Rji\ 0/!"..NH>?]210463"fsNme_NHNH@ >?2]22310463"!463"fsIfsNme_me_; )+7!@//))55 P##/]333]223104632#"''7&732654&#"'#"&546324&#"326Z~Y[~|];-XRXy7'(:<&$:XRX-;\}~[Y~y:$&<:(&8RY~}ZY~XTX/<(89''98(?]29/310#"'#3326=WW"ee !0&2Xf fL -72D!@@P` /3]]9/3310#5.=3326=3EFfCFd'30&ffZZ \\ Z[1150051H/]210!535#j^/10#'73j/]210#5265ftRjj[H@ /2]22310#5265!#5265grgrRjkZjkZb @/]1057'%㍪3P3L\N;)@ /  P/]3]210'#"&546324&#"326=XTV28YXWw:&':8)(8R=.XTX|[Z}~Y(89'(88D^N >?10#'73NHN>?3]10!535jHP){) / /3]2104632#"&732654&#"~YY~|[[|w8((8:&&:RY~~YY~~Y(88((88P @ @ /3]q2104632#"&4632#"&P/!".0 00 0/!".b!/0 ..".0 0.- #/;'@39!@!!!- '/33333]q223104632#"&74632#"&4632#"&%4632#"&4632#"&. ".." .. ".." .r- #--# -I. ".." .0 0/!".` .. ".." .. "..".." 00".." ..".0 0.@@ /3]q29/3105!74632#"&4632#"&G8-# // #-- #--# -mm@".." .. 00 "..!@@ /3]q29/33105#5!#74632#"&4632#"&nGj-# // #-- #--# -rmmr".." .. 00 "..PB  /3104632#"&P/!".0 0!/0 ..=D  /333104632#"&74632#"&0 0/!".. !/." .".0 0." 0/!"..= #@!@!!! /333]q2104632#"&74632#"&4632#"&."".0 0. ".0 .u0 0/!".b".0 0/!".."".0 .. 00\ /]310!!H}j\/105#5!#5oHkujjuPF  ??3104632#"&P0 0/!". 00 00PF #@ @!!! /3]29/3104632#"&4632#"&4632#"&. ".." .0 0/!".0 0/!".`".." ..I 00 00H".0 0.qP  /3104632#"&P0 00 0#--# --7/310537nyL/33310!!<L\+ /]310!!H+j`//9103` wPFPF91@  CYCY:?++9333105353jP  /3104632#"&P0 00 0f 00 "--jP  /3104632#"&P/!".0 0!/0 ..6@  A @Y9 @Y:?+?+3339104>;#"!!CIN!yY:5 /10##57#5!m͠kBK@       :9?2?39933333323339999103673##>7}Ö 7.+5aiD94qTM2!#.Hl24>9E ~J}Ɖ//}Nvvif9=??93103#Hy6@  A @Y 9@Y:?+?+333910)5!4.+532yy!NJD;Y˷3A@  @Y ;@Y9?+3?+333933310467#5! ! 32654&#;'Cy3X5?w5X)B 3@    <9?3?399333393107 7363)HzCi3Xd1X=@   @Y = @Y9?+?9/+3333910 #4&#!3!VRRqL@ @Y A @Y 9@Y:?+?+9/+333393104&#!3!! )5!265R ]R 6@  9=??39/33333393310#4.3>?3,f٬c \h5 YwP}, G@  9@Y:?+3?39333333993310)5!3>?3P}MF 7f]?!!ی`%P{H?@  A ;=@Y9?+??39/33339310#!25654&#!f3ub)x^OІf' &  :@Y9?+?33910!#4&#!5! yѶ3P@BY9AY:?+?339/+933993310332>7337>73dt1M?@r{IlV Q#/d ?y=A@@Y: @Y9?+3?3+33339310#5! #4&#!+532>5𸕹C FP"!1ѸY3lmd&d& Fd&  H@ 6?933310#3`{-@ 6?329933333310#3#3`׉{{: ?@!   PY /   ?3?99//]9+9993310!#!#3 vutF5:3:^@4   PYPY PY    PY?+??99//_^]+++39339933310)!#!!!!!!#Kb? @^L:KBL'4;@R-" ;(;5&&;=<$8PY$'5PY(QY '"( ('('(   PY 0PY PY ?+3/?+?+3/_^]99//]99++?+933333399333105!"'! >323267#"'# 57>54&#"3267< uaXAfOҒoy~q cv#ÀLd]fV Cq^H-u;oP\?/bRWaYWī):&^@0#  (' PY PYPY?+?+9/_^]33+33993333333333103!23##!#!!2654&#32654&#uK!-2u)yfnqyffyb؞dF]3RRYSURRVWNF:(@  PY PY?+?+993310)! 4&+326n(ĸ-:ʵ: P@)    PY   PY PY?+?+9/_^]3+399392223103! )#%4&+3#326n(dĸ񻷷pIʵŠ: G@%   PY PY PY?+?+9/_^]+99923310)!!!!!$d:1^N$b@2  ""&%&  PY PYPY?2/+?+3/9/+9_^]993333339910"&'732654!52654&#"'!2ʧ-5`qc]Yq .KΑgy},^[ȉTXJVJF~e  mn=: @ ??933310#5#=欬 :0:.@  PY PY?2/+?+9939210 73265#5!{4_@O^'dZhrc:: <@       PY?+?99//99333310)5737!wwweep:A@% ? ??3?39]]9333310!##!67!#?4. G,:k}rs: H@,   p   ?3?399]qr993333103#467#< Ŭ:e$:VNR=N>1 &@SY SY/+/+993310 ! 4$"32654&n꽅Ak*@  SY /333/+93310".54$! '>54&whl Zj"`ri/^"D@'   $# SY SY/2+/3+99333310% $547'563 ''27"654&sbt֊0[iɅ\X݂U1Q2vdSs[QDMCW3N%,m@:& ,!,-.&PY  )PYPY #PY  PY ?+3/?+?+?+9/99+933399333104&#"'! 6!2# '! 53265!"3267[uaXz|u~#֤C^H-籱?'Cd:%E@!  &'@  #QY ?+?39/99993333339910#"&54675.5332#"326MyСxyf\!lrtnmq:ҿC VN &@   PY ?+29933104&#"#!2`0V &@   PY ?+299331032653#"!: 4@  PY  PY??+9/+9933310!#!2#!!2654&#Bβظivuj:ze`^c: I@$  QYQY ?3?+9/+399333339910 #&463!##";Xxk|63\]^]: D@"   QY  QY?+?39/3+993333399103!"&573#";P;|kxpƖ36}]^]\#:;@" ?    QY?+3?_^]]]]9310!!#!#d:I:wDX*@  /2/993333310!2>54&#!5!27#!Dkv4S*,9Op]/ *\Y>yEX"H@" !"$#"  /2/99//93333333310!2>54&#!5!27#!3#3#\kv4S*,9Op]/鸸 *\Y>y>-?Y)<@ ! %!*+) !  /2/9/3993333339910!2654&#!5!27#!5!2654&#!?xS*,9OsZbkm`/x|Vps:l{8qVpv:Y:ZS:]1^:R@)  QYPYPY?2/+?+39/+399333339910"&'732654&+5!5!ʧ-5`y06},dZkb)A+L'd@5 $$()'QYQY '!'' !!PY! PY ?+3?+399//99++9333333310#"327#"&5467532654&#"'>32;xgbT5˗š;xgbT5˗šPQ@Vt|PQ@Vt)M+D@$( !!-,(  $$PY$PY?2+??+399933910"&'7326?'.546323267#"&/-Wu8X!Z-"G]J:ù:J]G"-Z!X9sXWz:779y7?s'4"#[CCZ#"4's?7G[UUZH:@QY??+99310!#:I::N:C@+   `   QY?3?+_^]]qr993310#!#Ǵ/:I::8~6:@@!  PY/   ??339/]3+393333310!".53332653#|6lwc̛4yj :R@7 pp 0`p QY PY PY??++?+_^]qr93310!! #"'5326!h;@JmV2,+7C7#,LϜJ nE5@  Y?  ?3?39/]+33339910!#3 !&mXo/ V{!!e"\AR 6k@;  Y Y    Y Y?+??+399//_^]]++93333993310!#!!!!!#!=&UZ/6!trtug75 P@) Y YY?+?+9/_^]+9993333310#!! 4&+324+326{[VUUosha7rkO{fOBXa"X@,    #$ Y YY?+?+9/33+3399333333399103! 3##!#4+3264&+32$^[VFL{^rj6ha-jBxDm˧XOB2n (@ YY?+?+993310#!!24&+326nt؊< ɠz궿1' E@$  Y Y Y?+?+9/_^]+99333310!!!!!1%itrt  E@$  Y  Y Y?+?+9/_^]+993333105!!5!!5! i%tRr6t0C@# YY Y?+?+99//+9333310432.#"32675!5!#"04)jX5H s{)TO̻73wN\.A 3@   Y ?3?39/+99333310!#3!3 6^c@ ??9310#3V4@   Y Y?+?+9/93310"73265#5!70 R>CND TZb`GtH1[ >@     ?3?39399333339910 #33 väg]P-@Y?+?993103!-t45@     ?33?9?933331047#&'#3?3_!d  z$gGMDiRj!a.A .@   ?3?39999333310 #3&53 6lT\.A ,@    ?2?3999933331033#47.u;ZH+ (@  Y Y?+?+993310#"54324&#"326 ̻4%J@$#  &'Y  Y ?+/39/+999933333399102653#"&5467.53"32654&SbSGVa`WHR^YcseqqcrĀply{rvzcxx- 4@ YY??+9/+9933310#!#!24+!2⡍-r Q@)      Y Y?3?+9/_^]+399333339910!#!24#!!26Ǧ{l3_pf☆mUí_*F$@ Y??+39310#!5!Xntt'F%@   Y?+?3993310"&5332653ԍ|ɯxd&@   ?3?39?910#&'#3?33%ȓvPbpquiVM;#/k@:- ((01 *Y/ YY$Y?+3/+?+3/_^]9/+9933333310"&546?54&#"'6!2327#"&'#'2>=poMQQL (3,F;/y@Bi=}bAyjx~*_N:? ΄}99O LMVJ_@m<>OQ?C#/g@%) -01)Y )@ H))Y@H Y$Y?+?+3/+?+9/+9+93333331023267!"&54&#"56323>"7>54&poMQNL (3,F;*}ADi;}bAyjx~*_N5D ΄}H99O LMSM_An:>OQ?C;"=@ $#  YY?3+?+99?99333310#"&5!23473#&532654&#"/#oTTp#uuQ]h^^g\SeJ?AE3F*e8*Q-(4;@.5( #;4;-;(<=(5Y((% )Y #!)@ H))!@"H!!Y!!%%8Y%0Y Y ?+3/?+?+3/+3/+9/+99+9/+9333339933105.#"'>32>323267!"'# 57>54&#"32674g^P`k%oG7crrLRRO CQgD?Fj9 a]Vc B3 _UcRyjx~*_N6C bbRN=EAl324&#"326To#vz#oTQ]h^^g\Skq?E'"8eylJ?ʝ;"?@#$  YY?3+?+99?993333310#"&5!235'3#&532654&#"/#oTTo#zuQ]h^^g\SeJ?@E?323632#4&#" 8NN\zs&eLTaCpey8ON[kXzsFX`#NCFKkXwvF]8@   YY?+??9?+933310#"'53254&#"#'33>32)*-PBWWg{s,mS{n t h5lWzsFX`#PA; (@  YY?+?+993310!"&5!24&#"326J[jl`^fn_htƞH6@  Y Y ?+3/?3/+99333104&#"'>32#"&'732!Wb~ou{ XBk nv̿˃nLI;h &@   Y ?+29933104&#"#!2L[jl`Jh;h '@   @ Y/+2993310!"&533265^fn_htɖZ$=@  %&Y "Y?+???+9999333310!"'##/33>324&#"326<zv"lVTZk[^g\SkqG$*I?ˢ 9@  Y @Y?+?3+39333310#"5#53733#327<@UZ$R"+-]]7, [1@    Y?+3?39993331032653#'##"&5BWWgzt*nT{nnUzsLD+ f6@  Y  Y /3+3/+993333310!2654&#!5!7#!+nUzsZDMCBWWgzt*nT{n]-&J@%  %% ('& Y"Y?+3/+3?33999333331032653#/##"&'##"&5332658NN\zs+bJTa hWqdy8ON[kXzs`#S>FKCNkXwv @   ??3933910#3>304C!'C@#  )( Y $ Y?+?9/9993?+999310"&'732?'&546323267#".'3S+<4>7 EhY%4b@"4=;+R3';4DI27#2RA)Wndgwwf-aE{W.$R2#,]`+`(X@. " )*Y  %Y Y?+??+9/_^]93+99333339910#"'##463232654>54&#"ꥒ{]{es&qMtefp;6bnex ɾ`(˯e6@  Y ?3?+?93233910#'.#"'6323#g,t/."/9H9`-Fd6\By|ex'gxu`jsxxs{ba(W@- " )*Y  %Y Y/+//+9/_^]3+993333339910%#"'##463232654>54&#"륒{]zfr$v9beuoWLTIbZB먚u@'o]eodbVGMkx`r$@  /3/933993103>3#67 " .1 /r$fA*d<`#I@' %$ Y!Y @ HY/+/99//+9++9933333107!2!"&'32#.#".4&#"326<JPh%bo7B[jl`_en_s51^8Ue:aM"L@&   $# Y YY//+3/+/+33933333310#.546746324&#">MtSXdfikp:7bne `˰eMN#*l@8 *$##+,*PY** 'PY  PY PY ?+?+?399?+9/+9/933333393103267!"'#"&533265363 '.#"횔ua~3˵-e`s|ݺ^H-PIRmt4}Vj-9l@7%44    . :;#  '++1PY+ 7PY?+???+9999//9933933333333310!"&'##65&#"#>35332673#'3>324&#"326r{3'kT7Z`72zxy"Yc 60/h|i/0rrtAXhZV,8f@4 33& !!&-&:9($!! 6PY0PY?+?+99?99//9933?933333333310%#"!23'5&#"#>35332673#'#.532654&#"52z{2'kT7Z`7xyhZ6Zby0/h|i/0rrF6t*pZ+m@7*##- )  ,- ) )QY) %PY QY ??+?+?+99//993393333333333331032673#'#&#"#>3#5354632&#"3iZ`7'kT7K4-#E>m/0rrb0/h|Oz F\aN/9B@M3++&"*9//=: *DC+6PY.00<<: :),,#2%:22:%@PY????+9////3333333333?+?999333333339333310!$'##46754'33>323>32653#&%54&#"%54&#"ЏEmZ:l{8q?b_-VpvVpt: RXܽ*,9OsZbkm`$|wH 4w9 x 7yxN'3h@5-#'( '54& , ( ,##, 1PY????+9////33333399933333333310!.'##46754'33>3265354.#"9A{:,-za>yNoaT_?*\YM (.- ;&]ٽ*,9Op]$}wp=? kv4WM.;]@0/6$ =<(,,2PY,# 9PY?+?99//9933??+999333333310!"'#32673#"'#5&#"#>34'33>324&#"326rV Z`#'kT30ƽzky?{"ʼ/0rrv0/h|ǧ61fd]ZN*L@&** $,+* ' $$*SY??+?99//99339933333103&#"#>34'33>32&#"32673#"''kT/+pf$%$32&#"32673#"'#&#"#>3\;%$<|nZ`#'kT/f /0rr0/h|K7Q@)(64! 89640 0(%PY" PY?3+?3+933333993333333310#"&'732654&/.#"#67&54632.#"32673!TgU+Kʳnzt+MlAK/a!+WQTT>L%(i)5J~HMJK)7((3,<K,,`@0 ,'' ,.- #    QY@QY*PY?+?+?+99//9933993333333310%#"5'"#>3#53733#32673#"&'327*Y]&'kT)}5x3Z`:3?$D0/h|5/0rrN?1:![@0"#PY PY?+3?99//9933+393333910"'!!5&#"#>32!5!326733{38'oU5~8J*Z'w 0/h| =00rrW K,K@&& .- )PY #PYPY?2+?3/+9/99+99333333102.# 3632#"&'##6532654&#"R{diǻs. HS~vUHKKQŮiafS6;:edb ?@!  Y@ H  ?3/?3/9/++99333310!#3!31wwXxC(3MW/: ,Q@*** $ .-!PY  'PY PY ?+3?+9/9+933333331074>3.546?!5!#"."32654&M~ރ )+9%-=]k]=玏{횣8y <&)<,$6CUtexw履*3@H%%& ,& 3&54 2//*PY/%'!PY QY@ QY?+?+??+?9??+9993333333333923107&5#53733#33632#4.#"#327#"'# }5x]9E*`UC$DY]I0.}-;҃u~^T/ro4&D: 2@    PY ??9/3+393333310333###mrrmi/:WiM%,l@7*"")#.-#@))   PY &PY ??+?+9/_^]9933/22?99333333333310354'33>323#!"'###267!"!.l0 NMVlA}|  {i61fd]YQB:U@+    PY PY?3?+9/_^]33+3399333333331033!33##"&=#26=!geegwwi//{Ÿ{XX :#,k@8 '' **#!!.-)##PY  $PYPY?3+3?+9/_^]33+339933393322223103>7!5!!.'5!!3##"=#26=! ][iNwMwNg\_LJ;iQf&zy&eR$ W$0S@,%++ 21""(PY".PY PY?+?+???+999933333210!"'532=#"&'##6533>324&#"326^;I2E}?H{32zxy"wYc 6YAXhZVWB ,W@/''!.-PYPY *PY$PY?+?+99??+?+933333310%#"!23'33!"'532=#.532654&#"52z{2Y;I2E}VxyhZ6Zbywt*pW<!P@,  #"QYPYQYPY PY?+?+?+?+?+933323103!"'532=##5354632&#"3iT6E/?}_K4-#E>wz F\aVWK.<w@@)(/ 6 )>=%(PY ((3PY9PY,PY,, PY5 &  ?3]]+3/+?+?+9/99+?9333333331032=!!"&'73 5##"3234673!!"'4.#"32>+-(v;{d 3wǻs.).HS~vUH f8KQ;hiia6MedWS@,  PY   PY?+???9339?+99333339102=##33 3!"'5}'IZq;I2wma /2W-@   PY  PY?+??+9331032=#33!"'+5dW-@ w WN5c@6- +"55 &"76- PY-$)PY$1PY PY  ?3??+99?+?+?+93333333310!4&#"#4'33>323>323!"'532=#4&#"Vps:l{8qb;I2E}kVpvxS*,9OsZbkm`wxWHN&N@)$   ('"PY" PYPY?+???+9?+993333333102=#4.#"#4'33>323!"'5"}f*\Y>yZ;I2wkv4S*,9Op]WM%2W@.&-  43##)PY#0PY PY?+?+???+99939933333210!"'5326=#"'##4'33>324&#"326|;I2E=@67V0ƽzky?{"ËCQp Y61fd]ZWNI@& ! PY SYPY?+??+9?+99339333102=#4'33>32&#"3!"'5o}^+pf$%$r%f g9WK4Q@)%$$ +65+ !!(PY%! PY PY?+?3+?3+99993933232310!"'532=#"&'732654&/.54632.#";I2E}l!XbJʳnzt0^~I(3WQTT@P"(MnP~HMJK.<*%$=Ja>W%N@+  '&PYPY # #PY# PY ?+?+9///++3933331032>5!2&#"!!"'532=!#"'Y5!6;=pxyn .]!v(-.) EZcYF_;rRZ$. (y |gZSY0dQX`VVN"._@2 ##!! )0/ &PYPYSY,PY !PY ?+?+++?+99?99333333310327# 5#'5##"!23467334&#"326-v(-.)Q6{6Dxyx (w;k[6We;jVW".R@+)""#0/ PY PY  ,PY &PY?+?+99?+?+9933333310%##"!23'5!2&#"327# 32654&#"92z{2AF:;yz@*?=xyhZ6Zby((WWN$[@. $$&%$PY$$!PYPY PY?+?+?+9/+9/993333333103267327# 5#"3 '.#"uz@*?=hݺ^H-(D IWZN/s@;"--$ ($ $10+&PY+PY PY ."" PY?+3/9?3/+9/+9?+993333339933310"&54675.5463 .#"3"327327# 5ݿՖ|kѥK. pZ]coe5z@*?=cl  cFJVJWUfaTf,(<5WbN/j@7&##* *10&PY /- -- PY- PY PY?+?+3/?+3/99/+993333339910327# 732654춮&#"'!2#"'z@*?=5eoc]Yq .KҎj{c{(k,eUafTXJVJFc  m<XWN%`@1%% '&PY  "PYPY PY ?+3/?+?+9/3+39933333333104&#"'!23327# # 53267\uaX z@*?=#C^H-D(;?W|?@! SYSY PY?+?+??+933333310 5#3332753_[z@*?LW(:aɬ=WN#F@% ""%$ PY PY  PY?+3/?+3/9?+93331032654&#"'>32#"'327# `ir٧fz@*?=NlhjZG(WG!?@ #""PY PY PY ?+?+9/+33993310%327#"&=32>54632&#"<|/;@*?=<927k,1BMKέ B:'NgiW:$K@'  # #&% PY  PYPY ???+?+?+99933333310326533327# 5#.'##"&5:*\YYzA$?8M>y:Rkv4sa(,9Op]DW :%s@>   $$'&PY  QY@ H ""PY"  PY?+3?+99//_^]++3+3/99933333910732654&+5!5!#"'327# RvUsy׊vD>@*?=̃ugUgel2IK(AW!A@! #" YY?+???+999933333310>32!"&'##4'34&#"326"oTTo"{uvP]h^^fZUjJ?p@EU!]1J@.@  Y Y ?+3?3+9933103267#"&54632&#"BV |wr~bVlILmľxl 3'S@."&)($Y& Y Y`?]+?3+9/9+93333310"''7&54632&#"632'2>54#"|lM#TJCr~aW?y[8M)Tpc/874Aufxl _E=^9^0K32'Z@/   ")(    Y%Y?+?9/9+33/99933393310"&54632&'57&'374&#"326y^@JV̔?f4?Аp{(]mn_bbodݵ*P]PB5;0]OAia}}}'Q%x@D#!! '&#Y@H@ H@HY Y@?3/]+?+3/+_^]9/+++9993333339910#"&'732654&+532654&#"'>32Q| y KMMNA=?MCEuz oxRXduwz E?=DdA76E jyra}0b.E@#  YY Y ??+9/+3/+993333310##5354632'"3{ggX\96/*a\Uh\a1AD\,D@"  YY Y ?/+9/+3/+99333331033##"'526=#5{ggX\96/*\h\a1A\;m`9@  YO _ o  ?3?9/]9+99333310#"&5332653#475)oTwr{GQXf{{MD}tO}hp. @@!   Y@ H??9/+3+3?39333333310#5333#53dSSyQQyyjX9Xyyd@ ??93103dyd@ ??93103dy. 6@   Y@ H??9/+3+393333310#5333#dSSyQQjX9X%T@,  ! Y Y  Y?+?9/_^]3+3?+339333333107"&546;33#'26=#"53m|ckz]]yz;>}4,7zaYYZGbjdMJ))'.dyyWd $@  YY?++?39310327#"53'%$","{9/ r q 4@   Y @ Y?++?933331032=#33#"' "VDz<(#6hTWX@Y?+?99310!3!Sx5n[*-Q@,-$$(./&+Y& Y  Y?3?99?+?+?+9333333104&#"#4&#"#'33>323632#"'53258NN\y8NN\ys&eLSaCpd*2%,QlXwvFlX{rFX`#NCFK s i[&#M@' "" %$ #YY ??+3/+?339939333331032653##"&'##"&533265}9MN\yx&eKSa iWpdy9MN\mVzsL=EKBN~mVwv:@  !Y Y ??+?9?+9933333104'33>32#4&#"#"'5325bt,mSzo{BVWg(5"/PB`#PApT{r r i_F:@   YY?+??9?+993333310327#"54&#"#'33>32P+**BUXgzs,mR{ni t >pT{rFX`#PA_.@  ?3?39999333310'3#&'#=tr s.X@s,: K@* Y@H@ HY Y?+?+9/+++99333310!"&5!2267!"!.Jf^s_dc`[is {}\{}w7S@+   Y YY??3+3?+3/+?9333333310%.5%34&'>%D pHLKIoID!d$÷ $N/1S@*, !'0!032.)Y.$&$$$Y$Y?3/+?+3/999+99393322103254.'.54632&#"#"'327#"5bW!=xg\2znSO6APyW4sOS $+"=9v$- 6O8hmXci36(2!6T>lu*per2@  Y Y YY?++?++9333107"'532>54632&#" 1*0 $)efI#+62 q:L7y vKY"I@%  Y Y Y?+?+?3+3?99333333310#"'5326=#"5#53733#327,0%$#* UY$R",( q (9O\\8, !\@0   #" Y @ H Y ??+?39/+933+3339933333333310!33##'##"&=#5326=!WzIIt,nQ{oHHXgBG^Q?~W^GZs>nU81I@$ *!!&//23' Y*$ Y?+?399+3993333333310"&54>54&+53232654.546;#"$*$8TWC5bF?/FAE.qr.EAF/?Fb5CW>[%@   Y?+?3993310"&533253{{ݎ%W)@  Y?+?39933310#"&53324&'3{LW)%!+}ٓ)gh;]LH @    ??39339103#'# 04C< 5@  YY?+3?+3993333105!5!!<Y1>`9b`b.J@'  Y  Y YY?++?+3?+39933333310327#"=!5!5!!&!1$Y1>90q [`9ba%mT@+  !YYY ?3+33?9/++3993993332210>32+'7#5!5!%"32654&$FBRZ^h^%Z0>{BQu40&LyoXHTV:C+Ra8b`ǁ &.M@'  Y Y Y?+3?3/+9/+99333339910!#"&'732654&+5!NYyer{p8 "~‰ une< G@%Y Y Y?+?+9/_^]+99333310#"!2267!"!.=aYXbaXsUAkɺP) @  /2/2/99105375353,Ҿ_6`B%!}) @  /3299//1053%5353_!F%Nf,)/910"54>54.54>54#"'632Z!)!!)!$*$1+/ <   ] /32310#"#"&5332>;]"8``g@mB94Z]iC#Z%-%m9G(0(,Y/+10!5!7,:h:@ Y//]+10!%7!:@:Ԕl,@ Y//]+10'!5!WԀ:@ Y//]+10!'%!:@WTlb@ /332310'%77==Amzzmb@ /22322310''%7v=,=ԀmzzmoWl  /339104'33>3&#"T71$=5W>J0!ZG7t}! /910%5!r``rRV @  /93/391057'5#373窪Rr``rRr``rWR&$@ %+55WWsN&D97Դ71%+55&%%""& ˴ "%+5+5&E ''&+5&%  "%+5&E  % %' %+5&%!!"%+5&E7&&' %+5hNy'&&z& 4]+5Wb'z&Fvg22&4]+5e&'%&%+5+5V&G ##&+5e&' H%+5V&G $$&%+5e&'%+5V&G) %%&%+59e&'%+5V9&G'',%+5We&'%+5VW&G**$%+5+&( @ & %+55+55W&H'C!@0&+5/]q5+&( & %+55+55W&H'vn!@0 &+5/]q5W&(  %+5WWN&HI ! ! %+5W&(! %+5WWN&H6##/ %+5NP&('jz@  &%+5+5WN&H'z*@ &# %+5+5&)%@  & %  %+5+5<&I%@ &K%+5+5g&*I@  &!!"%+5+5VWS&J@ 00&1 12%+5+5 &+%@ &   %+5+5&K~%@ &6%+5+5 &+ k  %+5&K %+5 &+ @ & %+55+55&Kj"j@ &'%+55+55N &+zlN&Kzr &+ %+5r&K> $%+5W&, %+5|WI&L %+5M+&, 4"@& %+555+555{&&jv#@/o&+55/]q5?&.@ &   %+5+5&N(& q  %+5+5?&. A  %+5&N  Ǵ  %+5?&.´ %+5&N&ߴ %+5/&/    %+5>&O z%+5/&/' 'I  &+5&O' {N &+5/&/i %+5&O %+5W/&/d %+5W&&O %+5&0@  &O%+5+5#&Pv@ //&+}+. %+5+5&0c%@ &%+5+5#&Pc@ --&++- %+5+5&0  %+5#N&P  ++- %+5 &1%& %+5+5&QT@ & %+5+5 &1 q %+5N&Q  %+5 &1 %+5N&Q: %+5W &1 %+5WN&Q:"" %+5a+&2 #@#&##/%+55+55V&R' *@444P444& ,%+55+5/]]5a+&2  @+&++7%+555+555V&R'  ]@I4/4?4_44/4?4O4_4o44444/4?4_4o4444 44&332 ,% ,%+5+55+5/]]qr55a+&2 @ ""&##$%+55+55V&R'C.@ 0&%+55+5/]q5a+&2 $&$$#%+55+55V&R'v_.@ 0&%+55+5/]q5&3&%+5+5W&Svm@ **&&8&) %+5+5&3%&%+5+5W&Ss@ ((&&&( %+5+5h&5%& %+5+5&U@ & %+5+5h&5 fǴ %+5N&U  %+5h&5'I f@ & %+5+5tS&U' A@ & %+5+5h&5 %+5TN&Ugߴ %+5]&6%@ 11&//1%+5+59&V@ ..&,,.%+5+5]&6 D //1%+59K&V  ,,.%+5]&6 @77&0j06%+55+559&V @44&-7-3%+55+55]+&6 @ 33&55;%+55+559&V @00&228%+55+55]&6'% C@ ..&//1%+5+59&V' @ ..&,,.%+5+5.&7{%@  & %+5+5*&W% &%+5+5.&7   %+5*,&W  8%+5.&7p %+5`,&WN )%+5.W&7q %+5W,&WS )%+5W)&8%+55W:&X:@    %+55W)&8 )%+5W:&X9 $$0 %+5W)&8%+5W:&X&"" %+5)+&8 @&(%+55+55&X'c *@555P555&$$0 %+55+5/]]5)+&8 !@ &%+555+555u&X'jE@2$/$?$O$o$$?$$$$$$&##"% %+5+55+5/]]q55 M&9 a@ &%+5+5&Y&  %+5+5 M&9 @ %+5:&Y   %+5 &:A& %+5+5&ZCH&%+5+5 &:@ &K %+5+5&Zv&@ &]%+5+5 &: @& %+55+55{&Zj@&%+55+55 &:%@ & %+5+5&Z@ &%+5+5 &: _  %+5:&Z | %+5.+&;%@ &   %+5+5&[ &  %+5+5.+&; o@& %+55+55{&[j@ & %+55+55-)&<%@  &  %+5+5W&\@ & %+5+5A&=A@ &  %+5+5S&]&  %+5+5A&=   %+5S:&]   %+5A&=r   %+5S:&]  %+5&K<%+5 8&Wj @ &%+55+55s&Z@ &%+55+55Ws&\@&  %+55+55Ws&D>&>>;%+5+5&AP%&%+5+5C%Q@*#""" &'##`Y##  _Y  _Y?+??+9/9+3993333910#"'732654&+5.#"#! CwݎkE52VcP%df '{_7A.^(I%R&$ 4%+5WsN&D 2̴24%+5R+&$@  &%+5+5Ws&D4@@&77;%+5+5R+&$ @&f%+55+55Ws&&D E@==&>D>3%+55+55R+&$ @ &%+55+551s&&D E@ ??&5y5:%+55+55R+&$ @((&)=)%+55+55Wsu&D 1@II&JJ:%+55+55R+&$ @11&%%+55+55Ws&D 1@ 99&CʴC4%+55+55R&$'` C@ &%+5+5Ws&D' @ 66&772%+5+5R+&$ @&"%+55+55Ws&D 1@ 11&4̴4:%+55+55R+&$ @&"%+55+55Ws&D 1@ 11&4̴4:%+55+55R+&$ @&%+55+55Ws+&D 1@ 11&4̴4:%+55+55R+&$ @&%%2%+55+55Ws&D 1@ 11&4̴4:%+55+55R&$' v 4$@&% !%+5+5+5Ws&D' $@11&44:%>̴>@%+5+5+5&( \  %+5WN&H    %+5+&(& %+5+5W&HR@ ))& $ %+5+5&( @ &! %+5+5W&H@ #&##/ %+5+5+&( @&f %+55+55WV&&H E@&&&'q' %+55+55+&( @ & %+55+551&&H E@ ((&# %+55+55+&( @##&$)$ %+55+55Wu&H ;@22&3O3# %+55+55+&( @ ,,&  %+55+55W&H 1"&,, %+55+55&('w \$@& % %+5+5+5W&H'  &+5Q+&,:^@N& % 0@9@P`p@P @P`p]qr^]^]+5+5  & 2D@6& %Opqr]qr+5+5|&, %+5=&L z  %+5a&2 %+5VN&R %+5a+&28@ **&! !%%+5+5V &RG&&&!%+5+5a+&2 @''&(d(%+55+55VJ&&R 9@##&$c$%+55+55a+&2 @ ''&"%+55+55%&&R 9@ %%& %+55+55a+&2 @22&3;3$%+55+55Vu&R 1@//&0C0 %+55+55a+&2 @;;&##/%+55+55V&R 1@ &))%+55+55a&2' $@ &""%&&'%+5+5+5V&R' $@&%""#%+5+5+5a&b9++&'δ'*%+5+5V&cve&&&"մ"%%+5+5a&b++&(c(+%+5+5V&cC&&&#;#&%+5+5a+&b855&,,0%+5+5V &cG00&''+%+5+5a&b /&//;%+5+5V&c*&**6%+5+5a&b '')%+5VN&c ""$%+5)&8 n%+5:&X ٴ %+5)+&8##&%+5+5 &XG**&!!% %+5+5&q$$& #%+5+5&rv++&'մ'*%%+5+5&qZ$$&! !$%+5+5&rC++&((+%%+5+5+&q..&%]%)%+5+5 &rG55&,f,0%%+5+5&q (&(i(4%+5+5&r/&/o/;%%+5+5&q j D "%+5:&r 'F')%%+5-)&<" & %+5+5W&\Ci& %+5+5-)&< B  %+5W:&\ -)+&<@ &%+5+5W &\@ &&&! %+5+5-)&< h@ & %+5+5W&\@  &  , %+5+5Ve0&cd,&,״,(%+5+5Ve0&cd--&1´1,%+5+5Ve0&cd2&2˴2(%+55+55Ve0&cd@ 33&77(%+55+55Ve0&cd@2&2 2)%+55+55Ve0&cd@33&7 7)%+55+55Ve,&cF@ EE&0ʴ0=%+55+55Ve"&c<@ FF&0ʴ0=%+55+55R&$?5R&$̓?5*~'$, ?55*~'$, ?55*~'$, ?55*~'$, ?55?~'$,P 4?55?~'$,P 4?55Fj0&gd@ ,&,,( %+5+5Fj0&gd@ --&1 1, %+5+5Fj0&gdd2&2ش2( %+55+55Fj0&gdd@ 33&7ش7( %+55+55Fj0&gd@2&22) %+55+55Fj0&gd@33&77) %+55+55'( ?5'( ?5V'(X ?55V'(X ?55V'(X  ?55V'(X  ?55jX0&id@ & %+5+5jX0&id@  &$$ %+5+5jX0&id%&%% %+55+55jX0&id@ &&&** %+55+55jX0&i6d@%&%[% %+55+55jX0&i d@&&&*/* %+55+55jX"&i<@88&# #0 %+55+55jX"&i<@99&##0 %+55+55'+ ?5'+ ?5x'+X ?55*x'+X ?55x'+X  ?55x'+X  ?55x'+X /?55x'+X /?550&kd&  %+5+50&k̝d& %+5+50&kd&ʴ  %+55+550&kd@ &ʴ  %+55+55Q0&kd@&  %+55+55Q0&kd@&  %+55+55YJ&kd@ ((&  %+55+55YJ&kd@ ))&  %+55+55D',?5D',?5',X  ?55',X  ?55',X ?55',X ?55',X '?55',X '?55V0&Rd&%+5+5V0&Rd&!!%+5+5V0&Rd"&"۴"%+55+55V0&Rd@ ##&'۴'%+55+55V0&Rd@"&"/"%+55+55V0&Rd@##&'/'%+55+55;&2d?5;&2d%?5'2 +?55'2 +?55g'2 +?55g'2 +?550&wd&%+5+50&wd& %+5+50&wd!&!Ӵ!%+55+550&wd@ ""&&Ӵ&%+55+550&w,d@!&!;!%+55+550&w,d@""&&;&%+55+55"&w<@ 44&,%+55+55"&w<@ 55&,%+55+55U'<,?5'<X ?55'< ?55'<X "?55S0&{d0&00,%+5+5S0&{d11&550%+5+5S0&{d6&66,%+55+55S0&{d@ 77&;;,%+55+55S0&{d@6&6&6-%+55+55S0&{d@77&;&;-%+55+55SJ&{d@ II&44A%+55+55SJ&{d@ JJ&44A%+55+55&[d.?5&[d.?5'[ 4?55'[ 4?553'[ 4?553'[ 4?553'[ G?553'[ G?55Ve&c,,&)),%+5+5Ve&c9,,&((+%+5+5Fj&g1,,&)), %+5+5Fj&g9@ ,,&("(+ %+5+5jX&i&д %+5+5jX&i9#@ &N %+5+5&kE&  %+5+5&k9@ &  %+5+5V&R&%+5+5V&R9 @ &(%+5+5&w&%+5+5&w9@ &%+5+5S&{o00&--0%+5+5S&{9@ 00&,K,/%+5+5VWe0&c'd,&,״,(%+5+5VWe0&c'd--&1´1,%+5+5VWe0&c'd2&2˴2(%+55+55VWe0&c'd@ 33&77(%+55+55VWe0&c'd@2&2 2)%+55+55VWe0&c'd@33&7 7)%+55+55VWe"&c'<@ EE&0ʴ0=%+55+55VWe"&c'<@ FF&0ʴ0=%+55+55WR&$&?5WR&$&̝?5*W~'$,& ?55*W~'$,& ?55*W~'$,& ?55*W~'$,& ?55?W~'$,&P 4?55?W~'$,&P 4?55jW0&i'd@ & %+5+5jW0&i'd@  &$$ %+5+5jW0&i'd%&%% %+55+55jW0&i'd@ &&&** %+55+55jW0&i'6d@%&%[% %+55+55jW0&i'6d@&&&*/* %+55+55jWJ&i'd@88&# #0 %+55+55jWJ&i'd@99&##0 %+55+55W'+' ?5W'+' ?5Wx'+X&< ?55Wx'+X&< ?55Wx'+X&<  ?55Wx'+X&<  ?55Wx'+X&< %?55Wx'+X&< %?55SW0&{'d 0&00,%+5+5SW0&{'d 0&00,%+5+5SW0&{'d 6&66,%+55+55SW0&{'d @ 77&;;,%+55+55SW0&{'d @6&6&6-%+55+55SW0&{'d @77&;&;-%+55+55SW"&{'< @ II&44A%+55+55SW"&{'< @ JJ&44A%+55+55W&[d'`.?5W&[d'`.?5W'[& 4?55W'[& 4?55W3'[& 4?55W3'[& 4?55W3'[& G?55W3'[& G?55Ve&c''&**0%+5+5VeS&c((&)Ĵ)*%+5+5VWe&c&~,,&)),%+5+5VWeN&c22-%+5VWe&c'9,,&((+%+5+5Ve&c0&0Ĵ0<%+5+5VWe&c'0&0Ĵ0<%+5+5R&$ v@ &%+5+5R&$hI@ &%+5+5R&$i?5R&$9?5WR&$ .%+5 @  /9/933910#>5#5--.WMWn53L#W @   ?999310327#"=3!,&!p  @  /9/933910#>5#5--.WMWn53L#$@  /2299333310".#"#>323273*TNG76 [ 0Q?,TNEd\d%-%>9fi=%-%wx:@ ! /322/329999399332105353".#"#>323273&LFA30S ]N(LF@[S *Hĸ "("84r"("l_^6jW&i'& %+5+5jWN&ijW&i'9#& %+5+5jX&i@ #&##/ %+5+5jW&i'@ #&##/ %+5+5"*'(,Wr%+5?5B*'(,9u?5"L'+,W?5BL'+,9u?5W &+ * %+5*n2@    /3/2/9/99333910#53!#>5#5nr--.W MWn53L#*x2@    /3/2/9/993393103#5#>5#5rT--.W  MWn53L#{#N@'!##$%% $! @ H ##/]2/3/33/+33/93333933910".#"#>3232673#>5#5*TNG76 [ /R?,UPG62\c--.W$49ac9%58n,MWn53L#0&k & ش  %+5+5S&k & ״  %+5+5&k&j&F@3/Oo?@P`p& %+555+55/]]q_]5&k&j9F@3/Oo?@P`p& %+555+55/]]q_]5wD&k&Ӵ %+5+5,&k!@ &ܴ& %+555+555dP&,j& %+5+5 1&,N@ &%+5+5 ',,U?5W',,9?5*n0@   /3/2/9/9939310#53!##.=nrW.-- #L36nVM*x0@   /3/2/9/99393103#5##.=rXW.--  #L36nVM{#)@ @ H ##/]2/3/33/+33/10".#"#>3232673##.=*TNG76 [ /R?,UPG62\cW.--$49ac9%58n,#L36nVM&w&ݴ%+5+5S&w&%+5+5&w'j\F@3!!/!O!o!!?!!!@!P!`!p!!!&%+555+55/]]q_]5&w'j9F@3!!/!O!o!!?!!!@!P!`!p!!!&%+555+55/]]q_]5W;0&sd$&$$ %+5+5W;0&sd%%&))$ %+5+5&w&+%+5+5&w@&''2%+555+555-)&< r & %+5+5-)&<lI@  &  %+5+5K'< ?5z'<9 ?5'3?5 *@   /339/333333310#53#53#53uк   *@   /339/333333310#53%3#5#53u<кY /991053E %!SW&{'o SWO&{ 6+61%+5SW&{'9 @ +&+7+.%+5+5S&{4&44@%+5+5SW&{' 4&44@%+5+5\&2,?5;'92d?5\&[,?5/'9b[d?5WW&[ /+/* %+5D/991053 "@  /9/933910##.=W.--̥#L36nVM* /2/10#*T "@   /?933910#'7'77'*T7777Y7887 @  /?9/3310'7##!'77RJ77}m}8L( @   /?9/33]10##'7!(R77J}78}rLrL1@"Y?O/?o@&+H/+]q+99105!rÉL1@"Y?O/?o@&+H/+]q+99105!ÉL1@"Y?O/?o@&+H/+]q+33105!ÉN&BB1@% ?O_o/]]q5/]q5H #@  [[?++933310546733%+y_YaC}H #@ [[?++933310#65#53H&({^Xi@|H "@ [[/++933310%#65#53H&({^X3jA~~G #@ [ Y ?++993310##.=AX^{(&|@gK_ J@&     [  []]?3+3/+39933333310546733!546733$*z^X%+y_Y_C~ÒaC}K_ J@&    [  []]?3+3/+39933333310#65#53#65#53_"-y^X&({^X]I~Ñi@~K_ J@%   [[]]/3+3/+3/9333333310%#65#53#65#53_#,y^X&({^X3_G~ÐjA~HF&v 7@  Y YYY/?++++9333333310#53%s`arxsa@<   Y[ Y [Y[Y[?++++/++++93333333331053%%%%#5hihixxGQ| @ 0  /]]]9910#"&54632|vqptqss .@    [/33+33933310!53!53!53( //310#!*SmOM) /3/105!#MSO/?9/310###!T @  /?39/3310###!!!TuO @   ?3/9/3310!!5!###uTON7'3?K@.%%[(( F=7=[@7 [ 7 ML4CY:4[:IY:+Y"["1Y" Y [YyMiM]MIM9M+MMMMMMM{MfMIM6MM MiMMMMMyMkMIM;MM MMMMMMMyMkM[M9M+MMMMMMMyMkM9M&M M8MMMMMM@RYMFM)MMMMMMMM{MdMKM?MMMMMMMMpM_M@MMM^]]]]]]]_]]]qqqqqqq_qqqqqrrrrrrrrrr^]]]]]]]]]]qqqqqqqqqqqqrrrrrrrrrrr^]]]]]]]]]]]qqqqqqq???+++?+++?+++993+33+3939933+310!#3%2#"&5464&#"3262#"&5464&#"3262#"&5464&#"3260.COTFIOKI.COTFIOKI.COTFIOKI xwy|׷xwy|귲xwy|UzY@ ?9933103U@ĞzUz&V `]5z'O&dzXQ%@ [/o/]+9310%53 PQm?sYR%@ [/o/]+9310%#5 53RQot?G&@ //]55]55T/3310!5!+u`b??3310!#3q~ H@&   Y Y YY?+?+99//++9333333310353535353[Y@   ?33/9/3103#5!#3###TOON @  ?3/9/3310!!!##:QTN/?9310#!*T&n/?9/310##*TG "@   /?39333310#'77'*TT~~~ݵqqp @    ?3/9/3103#5!3###TON3 N@.Y_o ??39/]q33+3993333310#5!533!'jo=岲o-q(_h5(|t@C  YDTd5  Y Y?+?+9/99_^]_]]+993333333_^]_]10#"&'732654&#"#!!>32|w S@GVQK[CF@;АLFSLKHvuQr lIbwvdIl meb@    Y @# H K[@H@H/?/_]]]++]3/+]2+399333104&#"#'33>326FHUw#cJnaZFd^oA:lyO=g ~c6ji;irxs1N6ijgl@>   `Y`Y O   `Y`Y?+?3+99//_^]3+3+933333310&#"!!!!!!&432-_U|$ĸՊhy"(.x@<# " /0"@%,._Y)  @ +_Y ?33+23/99?+333/9299333993333332107&5%7373&'$+#7&'&'0=L0!"UH*9G?W SF*}EV@Uqa8B{tvY32&#"$#"$5!2.+pf$%$=#535#5354632.#"!!!!!27PFYVē"oGrphh\M7.yym9@Ks}w)8#@.c@5. ++,##"", /0'PY@- PY+# ?33??+9?+9333333333331054&#"#4'33>323632#4&#"#5}Vps:l{ߞ{*Vp-$oxS*,9OsZbk/x ;#&)@K)(' %$$! & *+%_Y  # _Y'&)(((?3?39///99333+333333+333993333393332223333231033!33#3##!##535#!5!!'##3'󬎎?\oo`"owqq++՛~)g 'N@&CBB( <""/..H<< 44((POH@H4HH4?++2PY/+%PY_Y?FPY9CCC/C C??!PY?@ H_Y@P0P PPPPPPPPPpP`PPPPPPPP0P PPP;PPPPPPPpP`PPP@PPPoP0P PPP^]]]]]]]qqqqqrrrrrr^]]]]]]]]]qqqqqqqqqrrrrr??+99//_^]+]3/3+33/3_^]_]++?+?3+99++99393333333993333310+#!2!#3 #"&5#53733#327%#"&'73254.'.54632.#"+iq@{LQi]ho5n)7=跪cX$Gmh8 VFWrl<L{zŃbNIbxnx=:i* ;W>pwit04U&7<] &5VQ|#'+.14@A.2165+'#_Y40, _Y($ 21.  ?3?3399//9993333+33333333+3333993333339103#3!#!#!53'#533!3!337!37!!'###3 mBBmslpok>@!>CGG/]**֛qq. ]@  !A9: @Y9@Y:?+?+??9/9/33339933102#54.#!#3!2>53#Y1rpToq0[jG5GRh~)-g@-$$,-,./' @ H !@ H*P--?/]3+2299//]+99333399333333310# 46323'5#53533##&5%32654&#"!!#lV╌Vi#xx~\\xPbo]\obQ27I?{?E6\gg\Pd8a]V+@i '')$$  ,-"QY) QY&    @'/H   @_ sYsY?3+?3+9/_^]qr3^]+q2+3+39933333333339310%267#"#73'7#73632.#"!!!!dxۮ(x( ۯyj(c(l~d[NV[d>(&õ?R@)    _Y ?3?39/933+3399333333339103333!!####j=D#-TTsq.M@(     _Y   ??9+3933333333310%%#5%55%!5!GGGG]@ǜ)is:FU_@S[*VM#133'7S;AS '#V`a'G3GaYMV#  XaY>`Y  03@-3**3^aY O`YD`Y?+?+?+?3/399//+9+9+393333333310#"'#>7>32".'#"&546327756%7674&#"32632654&#"32lV8&2 q$~4Dcn>&JX]dQ&V ;[~B2&O0LNyUHL\XGI],H=4F2wvg@I+=9-p6M=9&ĕQ?7ENSQFXnt2$A\)YXLs߸aqt^atxJyy_*{*)11-W!-M@*"+(/.++%`Y `Y/+?3?+99339393210#"'532654' #546324&#">HPSE>@z&~>30@5)7L\N@~"<73B[D쐀r5FC8N~A %*@f'##  &%  ,+%`Y$   @&0%@%P%0 @ P /   %  % *_Y ??+9///_^]]]]33]2233]22+9933333333933333333103##!##535#535!23#'!!627!!&#!Z}3bQ1wY@NtR8ub~{,,bddcg,#%i@8 "'&""_Y_Y@ _Y #_Y?+?3/+?39/9++3933333333310%53.'>75#5!#5$gQ6|K5pIdݯ|Hv 6jl NDf| BnW"p@;    #$_Y _Y  ??99//933+3333+33?9933333393310!3!!3##!##537)!3&/پ}[檥á~ƨ\]WE !&ddvDSne5@C.55 4/*#$$* 76##,',/_Y  _Y4' ' `Y' `Y ?+/+99//3+39/+339/933339233310!3267!"$547#53>?!5!654&#"'>323#!YDY?u'E zo( (pG(Dy~%/)?\1+Ap~xm!--/G*"h,y ^@0  ! _Y @_Y@_Y?+3/3+?33/+39333333310$%53.'$#F |+R|\?>y\Bu}fov7<",! Aa`_: 6@  PYPY??+9/+99339103!5!!5!K$:Ƌ?P{&{'u?5"&t'u#?5PN&{'3@!/o  @ ` p /A)?555]]]5]555=N''u"3@!/o@`p/\?555]]]5]555]N''(3@!/o@`p/T?555]]]5]555N''f+@/o/B?555]]]5]555;Nb@A  PY p`p o  PY ?+3/_^]?3/^]q+99333104&#"'>32#"&'7326ir٧`"jZẜ lhd^D $@     /3299339910#&'5673!9>HH>9)CI$ICV $@  /299/9333105673&'#CI$ICV9>HH>9#d^D $@     /3299339910&'3#67!59>HH>9#CI$ICV "@    /299933310%67#&'53+CI$ICV9>HH>9d^D<@     /2333993399339910#&'5673!&'3#679>HH>99>HH>9)CI$ICCI$IC6@   /299/299933333105673&'67#&'5CI$ICCI$IC9>HH>99>HH>9H>@   /299/2999333333310!!5673&'67#&'5 CI$ICCI$IChPX9>HH>99>HH>98*Y@3$,+PY QY /o 'PY/,]?+/_^]q9/3++3993333910 #"&54632374&#"7632.#"326Hք~T 5{.$qpmEQWZRN_˴>cTu(#>Xss @| _Y H  /p ?:`@_pP ]]]]]]]]qqrrrrrrr^]]]qqqqqqqrrr?2^]+?+333993933103!7%.' #3 + ^J ojN"@ _Y/2?+993310!#!No3N0 E@$   _Y _Y ]/+9?99+993333105 5!! !{BHNm0,je`H@ Y?+99105!e``b~#@Y@&H/+]+9310533bT@l Y   p P 0  o 0   O /  9       p   o O / ]]]]]qqqqqrrr^]]]]qqqqrrrrr/3?9/^]+9933933210##5!3njuNW]!-R@1"( /.%%++"?@HP/]3]+q29]2]29939210#"#"&54632>32%"32654&.#"326]˄DXɀAaFw83wMSmm33wNTihWFxNӰ傓S~qkop~`@ //993103!!^j8^@   ?2/9933104>32#4&#"tтwgĠN@ PY @   PYtfTD6&I9) gv) {k[K;) {k[I9) 7{m_@0 p@'`Pp`/^]]]]]]]]]qqqqq_qqqqqqqrrrrrr_rrrrrrrrrr^]]]]]]]]]]]]]]]]qqqqqqqqqqqqqrrrrrrrr^]]]]]]qqqqqqqq/+/_^]+933310"'53254632&#"$O7<9W,1BSYNwrB8P,,@ *-.'Y#T"@  //999310#4632#"'&'&#"~?K3% &!$ V?0(4 *''#i@  //9993103#"&546323265";N2$! =/'5)%3%Y?+33105! %iH[??+3103#ؑHK&[Y?+?3+310!!#(in&[Y?+?3+3105!# (%%H$[Y??+3+3103!!Hn%H&[Y?+?3+3105!3 %H-[ Y??+?3+33103!!#iHnnH,@ [Y?+??3+33105!3# %K/ @ [Y?+3?3+33105!!# i%n%H/ @ [Y?+3?3+33105!3! %nH ;@   @ [   Y?3+3??3+3333105!3!!# i%nnqj.@  YY?+?+3333105!5! AّH([[?2?3+3+3103#3#ّhHK Kj < @ [ YY?+?+?33+3310!!!!#(iijב" 8 [ [  Y ?3?+33+3+310!###בnn#j D  [ [  Y Y?+?3?+33+3+310!!#!!#htjo"j :  @ [ Y Y?+?+?33+33105!5!5!# i(qב) : [ @ [ Y?+3?33+3+3105!### ܑב%nj D  [ [ Y Y?+?3?+33+3+310#!5#!5!ґttj)F)ޑqH 9 [  YY??+?+33+33103!!!!iH"ב%H 5 [ [ Y?3?3+3+3+3103!!33A$Hn#nqH D [ [   YY?2?+?+33+3+3103!!3!!ّK$hH"qH :  @ [ Y Y?+?+?33+33105!5!5!3 iqב)%H : [ @ [ Y?+3?33+3+310!5!333$ב%nqH D [ [  Y Y?+?3?+33+3+310!5!3!3!5!$KqFH A @ [  YY??+?+?33+333103!!!!#iiH"ב"H ? [ @ [  Y?+?3?33+33+3103!!#3#AHnn KH P   [[ Y @ Y  ?3?3?+?+33+3+33310#3!!#3!!jt " "H ?  @ [ Y Y?+?+??33+333105!5!5!3# iqבKH = [ @ [ Y?+?3?33+33+3105!3#3# 㑑h%K#KH O  [[  Y Y ?3?+?3?+33+3+333103#3!5!#!5!A㑑tHK )ޑj B   [ @ Y Y?+3??+33+333105!!#5! iiq"h @  [ @ [  Y?+33?33+3+33105!!### ב%nnj S @ [[ Y Y ?3?3+3?+33+333+310#!5!3!!#!5jtttAޑ"בqH B  [ @ Y Y?+3??+33+333105!3!5! Aّ"%H @  [ @ [  Y?+33?33+3+33105!333! ב%nnqH R   [[ Y  Y ?3?3+3?+33+3+333103!!3!5!5!AH"oHZ@  @ [ Y Y ?3+3?3+3??33+3333333105!5!5!3!!!!# iiiqב"ב"HV@    [@ [    Y ?3?33+33?33+333+3333103!!###!5!33AבHnnnnH m  @ [  @ [@ Y Y ?3?3+3?3?3+333+33333+333103!!#!5!3!!#3!5!AבttH"ޑ" mH/?3310!!Umm?/3310!!UH??3310!!U H??3910!!* H??3910!!* *g #'+/37;?CGKOSW[_cgkosw{3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#%3#%3#3#3#3#3#3#3#ghhhhgghhhhhhgg`hhbhh hhahhahhhhhhgghhahhahhhhhhgghhhhhhgg`hhbhhhhhhhhhhhhhhhhhhgghhhhhhhhhhhh"bbbbba```````````c```````````c``````aaaaab^^^^^baaaaa``````bbbbb#`````b``aa`T #'+/37;?CGKOSW[_cgkosw{ #'+/37;?CGKO3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#%3#73#73#73#%3#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#3#3#3#3#3#3#3#3#3#3#3#ghhhhhhhhhhhhgggggghhhhggZhhhhhhhhhhhhgggggghhhhggZhhhhhhhhhhhhgggghhhhgggghhhhhhhhhhhhggggggghhhhggghhhhhhhhhhhhggggggghhhhgggggggghhhhggZhhhhhhhhhhhhhhgggghhgggghhgggghhgggghhgggggghh"bbbbbbbbbbba```````````````````````c```````````````````````c````````````aaaaaaaaaaab^^^^^^^^^^^baaaaaaaaaaa````````````bbbbbbbbbbb#```````````ba```c```c``ab^ba``b#`CIMQUY]aeimquy}  !%)-159=AEIMQ!35#35#35#35#35#353353353353353353353#3#3#3#3#3#335335335335#3'#3'#3'#335335335335#373533533535!355#%355##5##5##5#353353353355##5##5##5#35335335335#3'#3'#3'#3#3'#3'#3'#335335#3'#335335#3735355#5##5#353355##5#35335#3'#3#3'#3+jjjjjjjjjjjkjkjkkkkkjkjkkkkkkkkkkkkkkjkjkkkkkkkkjjjjkjkjkkkjjkjkkkkk?kkkkkkkkjkjkkjkjkkkkkkkkjkjkkjkjkkkkkkkkjjjjkkkkkkkkkjkjjjjjkjkkjjkjVkkjkjkkjkjjkjkkjkjjjjjkkkkk"a"a#`!b!b!`````````````b```^`j````````bbbbbbba``````````````````````````aaaaaaaab^^^^^^^^aaaaaaaa`````````bbbbbbb"bbbbbbb````bbba`````````````aaaab^^^^aaaa`````bbb"bbb{uZT//9910!!{!T!@  //993310!!!7L17}1mi{@ P/]/9910!!imi{*@ P/]/q993310!!!iLPbh?9910!!hL@ ?910! XVRZ?9910 7L@ ?910 LRZ?9910Z79e 1@  /3?39933933310!# 3 #R7Rb`15>.)@  ! /9933104>32#".732>54.#"xyzyy{zxVbcbdbbabdzyyyyxxzba`bbbbbV_R'/7?GOW_gow#"5432'#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432%#"5432#"5432'#"5432488448847575$4884766667557R75576666488448844866$48846666\666675576666666,557775666775Z557w557639D557448775666775557848*557# @  /]99102#"54>jnrotjlw)@ //9933103!32>54.#")vwvvvwvu}Avvvuvvw)#*@ %$ //993333103!4>32#".'32>54.#")QbaabbaabMvwvvvwvu}Aabbaabbavvvuvvwscu (@@   //]]993310#".546324&#"326caDpDegGJLfFFfbJM_}kArEgJGgFffFGeiy $0:p@? :6+%%<;("."51h1Y1G11138"_"o"8@ H"8"8  /]99//+^]3]]]]332993993333210"'4! 4'"2#"&54632#"&54632327#"'ybbZڗ20[. -- ., // ,LL>bbHfj24V .. -- .. --# #-d@9$ (./!)-h-Y-K-=--+&_oO&_&&& /99//]^]3]]]]3329999333310"'4! 4&#"326%4&#"326327'#"'bb. -- ., // ,bb>LLHfjz -- .. -- ..ܺ#Fs;*7~@C5  **.&"## 98+** !"1%*"*""*'o/]33/]339///999933/393333333333310373#'#5&''7&'#5367'767"327654&BBc<-VK1$J>5Byd+N P(nqÉ`c;'--qt>}`+*I-d}>_1M =ƈaaP&'\@3$  )(  /   @@%(H/+/]99//]q2329333333310.54632!!#!5!"327654.LkDrvSVjJ#8\9{RW;=9]B LzK~VSzm FF5_9Vy>=T:_4Q,8>@!0,'6!'!9:,*3$$$-  **/^]]]9999339210&'&54763327632".'#"&54632"32654&+!D;X)$  %$ TttzGSY|~WYz|++   =#"&5467> `FWcO|WZYYc`~G`^ %%a\YRw<48@4((56$+/     //99//]92393310)7>5'#".54672&'&5463267632#".'Fw_59YMF]>d& qtET'iDIt8v_=1mr#6xL/xvL~Ms3A(%&ykVb(NLtt1Ri~x5fZy@    //399310./.54632>32b[ZK6eW&"XaXoV{dBkrxwubVB @   //9/3933310&&'6H zDS%Ut}FiGfY;.@  //9/939333103#654&'#"&54632Lm^/9r@k9:}N,.dyxx {6.Mt \@ "! H@ H  /3/]/3339/++3339333310#"&54632#"&5462%%56{I99zN0*ډs9:|O--&DNqT6/Qovu5-Lxgu28h@7  !     ?/9////////////3993993333333333210##57573%377```L`Y=>YYYYG\YY Y /w@G  `Y`Y@H@ /O_  _Y?+?9/_^]3]+2+3+3933333333103#535#533!!!!!||䅔`@7  PYPY@H / ??9/]3]+2+3+39333333333103#535#5333#3#nnnnnnnn "ޅ@G@%   @ H _Y?+?9/+233/393333103'"#>3332673#"'!#'kT&Z`!0/h|Bv/0rr*L@(   _Y`Y _Y?+?99//3+3+9333333103!2#!##%)!!! Qb\@rMمWh P@) "!_Y_Y _Y ?+?+9/+39339922310!!327#"&5!24&#!!26I/4<2F>;͗IJQLվ{PXs)06;@P7#..43 )1=<3*'*PY40:0QY#'::)@' QY 9  PY_ /  ?3/]q3+3?+?39/93+3+39933333333933310&54673&#"'!23327#"&'#+>=4'7P0Myn .dQr*;!DGd[E aU@{l M[XBꨴRZ$,^.PQpip|gl_QYg/wn@I(zXT`@2   PYQY @QY?+?+?+9?99333333333310#537333327#"'#"}5xIJ3?$DY]h6GXЃp0N?80HhG@$  _Y  _Y?+??39/+9933333310!#!#3!33#sThcH@$    QY PY?+?+93??9933333310>323##4.#"#3=:}u*`Ujcro4~= hAR@(      _Y?+??399339933333339910 3###33>͸>hQ@(      QY????+99339933333993310#33 3##„Iga/ma /Ah :@   _Y  _Y?+3?+3933310#!5!5!!OZhV1h: ;@  PYPY ?+3?+399333331035!5!!#18j&a5:@  _Y _Y??+?+?999933310!5# !273%27&#"vrJàvs G[UR}M8gdN0@ SY ??9?3/+39933310!#3?>32.#"ew 8:*zh`j);$*2:@(5Љ}d<':D $7@%& _Y?2+???9?393102&#"#.'#363767>bqBI2<- 5a-&?8 9#kc9E52ongו#CzN"8@ #$ #SY ????9?3+333910!#'#37367>32.#"ѭ4/Ѳ "#.C\TJz5j?+,6#J:'!ĊBXh.FUd-6>@-E#R@+  %$QY !PY ??+?9/93+3933933310 #'67&54632?654&#"lj3F)&l\qcnU$:"i"/:#$rHfxcY4155-U;M"+'a'@ _Y??9/+993310!#3!as):'@ QY??9/+993310!!#B:6:UR9@   PY PY?+3?3?+9333310! 4746324&#">|WP5* ڸ AAj$@ //99//9933105%5%j))J@/9/10533U{s/2/9/10 '53WmdVm_/39/10!5!%53$_ya_ /310!53!}hv @  /9/39310!#!53h[c Uhv @  /9/39310 #5!3!h]U"@ //9/993310#353Si8٘$@ //39/933310#3#58i4, @  //9933310#3#58i4,i8  /32210##5##!8}}Oc8c /23310)335338h}}cWt@ /^]9103#'⪕W㾾~:@  /3/39333105353k^dj@  /3/39933105!5!dH֔dY?9310#535Q3ndP?9310#535Q3evV@, SY PY PY?3?3+3?+?+?9933333333310##5354632&#"3533iK4-#E>ӆIz F\ai:wH@$  PY PY?3?3+3?+?99333333310##5354632&#"33iK4-#E>ӇIz F\aI4&f&  J  ??3]210#"&533265JZNM[㑬M]`J<d& '  #)BB)7oP F  '=Z'h,@  AY7?+3999310!5!3!'3T3F&- $%+53F&3F&'P*@_****{*T**$%+5]]]]5]53F&'@_{T]]]]5]5B&B&B&9`#0##]]5R& ]5?-&LO$/$$]]5)7& ]5o& ]5f& ]5y& ]5& $$]5Ff&  @/]]]]]5'&    ]5P&  ]5F& @ ]5]57&@ 0"""d""]5]]5Hy& ]53& ""]5X&5q&- &H@ /$]]5]5{H&Td0]]5'& ]53&@_{T]]]]5]5=& ]5fF& R+&P+& q+&BPJ@ A : 996??3?/?93333393310#33673#VÖ 32`F[q> ܺw).2}5  /31032673#"&'aEmzNX*4Cfq5" !%)/AOW_jv@ qe*`kU]]FMJPBX?::<3XPFk*e + x"..+''+&&#++w( ..+nhhG5#53H,(u-1XWk/0V. %@  Y/+2323310 33273T @|E ,@ YP`?]q+993310#65#53E,(u^XWu/dZK 7@"   Y_oP  ?   /]q]q+99331046733#,(u^X%Wu/dZj&@/?_/]]9910%53H&@/?_/]]9910573H 4@  /?_/]3]99993310#'##573ih 0@ /?_/]]9993310#'53373hi-Z$@ Y/3+399331053!53ӥI@*  Y    /   @Y/2+_^]]q+3333310".#"#>323273*TNG76 [ 0Q?,TNEd\d%-%>9fi=%-%wx  -@  /?_/]2]29105733573  &@  /]2323310"&'3326734um[[kuo5<=4rjCjCjCHvHvHv+ /99104&#"'632#5>3*HFZjQ[pE0"c;U 2x+ /99104&#"'632#5>3*HFZjQ[pE0"c;U 2xX + /99104&#"'632#5>b3*HFZjQ[pE0"c;U 2xwu/1053ɬHvHvjCjC++u ?u |u u Zu m!u ^8u u~u u z.u kfu WRu #u :u +<&@ ??931033&++ %@  @ H o/r2232/+]31053!53%!5!%++ U@  k@!H0@Pp/]]]q+q_qr/r3/99//3103#553!53>+ ָ3+ [@   k  @!H0@Pp/]]]q+q_qr2//r399//39103#'5353!53ћ+ a޸++ S@  k  @!H0@Pp/]]]q+q_qr/r399//310#%5353!53h~ }+'@_@ HP/q22/+]q310537!5!W%?+7@$ 0@ o@H/+]3q/339/q3310!5!"'&#"#>3232673%3TU$--[ +L?7SS"0+\ *K&%#(RH(%&&%QJ' '@ ??39/?993310!#!5!3e*\* '@ ??39/?993310!#!5!3e*$E '@ ??39/?993310!#!5!3o ZZ#@ ??39/?99310!#!5!3o DM%@ ??9/3?399310!# 7 3f\>Ha4R '@ ??9/3?993310!#!7!3bG4oaRM'@ ??9/3?993310!#73_J^rM '@ ??9/3?993310!#5 73fS܈kh/iF$@ ??3/9/99310!# 73pZzfjFp'@ ??9/3?993310!#73p^ZHd(5%@ ??9/3?399310!# 7 3RkGHyN!5 1@ ??9/933?993310!# 7 3Rka.K- O &@ ??39/3993310!#!7!3xtdH^- &@ ??39/3993310!# 73`vcFFH @??39/99310!#73wp=Dg-p&@ ??39/3993310!#73pEsHVH!$%@ ??9/3?399310!# 7 3=|GHY2#% 0@ ??39/933993310!# 7 534~X4(5f% 0@ ??39/933993310!# 7 3C{sVH2}q' &@ ??39/3993310!#!7!3cy Q2X/@  ?3?399310!# 73HԈG7%p&@ ??39/3993310!#573p={H\2#"@ ?3?39999310!# 7 36}MH)Q0) *@ ?3?399//993310!# 7 530~YAuO/l6 *@ ?3?399//993310!# 7 3ON,qS-m6 *@ ?3?399//993310!# 7%3O~t8S-{0@ ??39310)733+NS.)p @??39/99310!#73pA>S.)O"@ ??39/9310!#!'!ataoL 0@ ??3399//393310!# ' 3[H5fy4L .@ ??3399//393310!# ' 3UH)hy?L ,@ ??3399//93310!#5' 3SHhy-^(@ ??3399//9310!#' 3Q6fy=Lp @??39/99310!#'3p^iy"@ ??39/399310!#!5!3pT; .@ ??99//33993310!#!5!3a.izT *@ ??99//3993310!#!5!35$&@ ??99//399310!#!5!3?P(@ ??399//39310!# 7 3h`axbVL 6@ ??99//93393310!# 7 3e`;H`GF (@ ??99//3993310!#!7!3fb*]v\^L(@ ??99//3993310!#73^'c{L"@ ??99//99310!# 73e[؈scSc[Lp(@ ??99//3993310!#73pd]?de[/(@ ??399//39310!# 7 3Gu7TaF?9 6@ ??99//93393310!# 7 53Rv8HqGG? 4@ ??99//93393310!# 7 3Nw]sX=U8H? *@ ??99//393310!#!7!3mwo V=2? @ ?3?9/99310!# 73\w߈5:=?p&@ ??99//993310!#73pWw2-B=+6&@ ?3?9/999310!# 7 3O}4H) 0q5 .@ ?3?9/99393310!# 7 53N}<Au 1[l6 .@ ?3?9/99393310!# 7 3ON,q -+m6 .@ ?33?9/9393310!# 7%3O~t8 -6"@ ?3?9/9310)7330O~ -Q5p @??9/999310!#73pM4 -zL"@ ??39/9310!#!'!owKzL 0@ ??3399//393310!# ' 3oH Kz4L 0@ ??3399//393310!# ' 3oH)S@Kz?L .@ ??3399//393310!#5 ' 3oHu^Kz-L&@ ??3399//99310!# ' 3o6RjKz=hp @??39/99310!#'3po~32326733#53TU$--[ +L?7SS"0+\ *K&%#(RH(%&&%QJ'h ?+v @ H@6H @P``p@P/]]]qqqqr3+qq2/39/+39910#5##5"'&#"#>3232673wwX3TU$--[ +L?7SS"0+\ *K+&%#(RH(%&&%QJ'YX @ /]99//10#5353 0K+   @ H/+39910#53#'53373D'hi++ 3@#  P`p@Pp/]q3/9/9310#5!#5!5!+=%+: 2@    PY ??9/3+393333310333###mrrmi/Ve*&c'^6@&0/0O00000000(&(('%+555+5/]]q55Ve*&c'^6@&;/;O;;;;;;;;(&(('%+555+5/]]q55Ve*&c'^6@&;/;O;;;;;;;;(&(('%+555+5/]]q55Ve*&c'^6@&;/;O;;;;;;;;(&(('%+555+5/]]q55Ve*&c'^0@ /E?EOEEEEE''&**2%+555+5/]]55Ve*&c'^0@ /7?7O7777788&;;C%+555+5/]]55Ve*&c'^0@ /7?7O7777788&;;C%+555+5/]]55Ve*&c'^0@ /7?7O7777788&;;C%+555+5/]]55*&k&^8@'/O & %+555+5/]]q55$*&k&^8@'/O & %+555+5/]]q55*&k&^8@'/O & %+555+5/]]q553*&k&^8@'/O & %+555+5/]]q55 *&k'^0@ /?O&&%+555+5/]]55*&k'^0@ /?O&&%+555+5/]]55 *&k'^0@ /?O&&%+555+5/]]55*&k'^0@ /?O&&%+555+5/]]55*&w'^8@'*/*O********& %+555+5/]]q55*&w'^8@'&/&O&&&&&&&&((&))* %+555+5/]]q55*&w'^8@'&/&O&&&&&&&&((&))* %+555+5/]]q55*&w'^8@'&/&O&&&&&&&&((&))* %+555+5/]]q55*&w'^0@ /&?&O&&&&&''&**2%+555+5/]]55*&w'^0@ /&?&O&&&&&''&**2%+555+5/]]55*&w'^0@ /&?&O&&&&&''&**2%+555+5/]]55*&w'^0@ /&?&O&&&&&''&**2%+555+5/]]55+&k&j' X@+;k{ +;[;{/o/?_?_o  _   0 @  @ P ` p & %+5555+qrr55/]qr_]5/]q]qr5+&k&j' @+;k{ +;[;{/o/?_?_o  _   0 @  @ P ` p & %+5555+qrr55/]qr_]5/]q]qr5 +&k&j' X@o""""?""""?"O"""""""/o?/?_ 0@ @P`p &% %+55+55+qrr55/]qr5/]q]qr5 +&k&j' @o""""?""""?"O"""""""/o?/?_ 0@ @P`p &% %+55+55+qrr55/]qr5/]q]qr5+&w''j y@+$;$k${$ $$+$;$[$$$;${$$$$$/o/?_?_o _ 0@ @P`p &%+5555+qrr55/]qr_]5/]q]qr5+&w''j @+$;$k${$ $$+$;$[$$$;${$$$$$/o/?_?_o _ 0@ @P`p &%+5555+qrr55/]qr_]5/]q]qr5+&w'j' @{o....?....?.O.......!/!o!!!!!!?!!!!/!?!!!!_ 0@ @P`p &%+5555+qrr55/]qr5/]q]qr5+&w'j'  @{o....?....?.O.......!/!o!!!!!!?!!!!/!?!!!!_ 0@ @P`p &%+5555+qrr55/]qr5/]q]qr5h+&L ;$@   & Y %+5+]]5W]"9@  $# _Y_Y?+?9?+9333310"&'732654&#"#336$32_;ZjjİwyHWJJsnsheD=atW ;@ _Y?+?39999233310#"'532>=#3&53 }zRA4(-1  AS[aXX`]%I@$ ##'&_Y _Y?2/+??+3/99933333310"$&'33254&#"#336$32 ]İwyHyJhe=Mwj2j '@ Y ) Y/?/]q+/_^]+10#"&546324&#"322LSVMNR¾X}c+@//]qr/]q9107573}Yxy}$jA@%  Y)Y/]qr+3/_^]+3/9910%5>7>54&#"'>32!$zd\EF=:S yTQzU}CF|F@S,6865XkpdEv6QE$nj0j#_@:!Y@ H/Y) YP`/?/]q3/]+/_^]+3/_^]9/++910#"&'732654+732654#"'>320HR?3WM yKgzm^p@;jk/1eb[np^!}Ac A@)Y @ H//]qr/]q9/+933+310#5!5333tfq$ T*q5j1[d@ Y@2 HY?_  YP`/?/]q3/]+/_^]q+9/++3/910# '732654&#"#!!>3211O>LWIP?Q ur#T?z};5SGCR(u#i)j"GY@" H Y )  Y/?O/]q+/_^]3/+9/+9+10#"&54632&#">324&#"326)7^N[`HvFQCHIFENow˙L{}"1{KCJDENN|#\ /@  Y/  O  /]q/]q+310#47!5!#}EFح#oj0j"-E@( !&!&Y!@ H!!Y)+Y/?/]q+/_^]+9/++9910#"&54675.546324#"324&#"3260`PKO~QLQaEEQKIPNJBiopgHf ^>VljY=^ bii69:;@7@i*j$F@*Y @ H Y  / ?    "Y/r+/_^]q3/+9/+9+104632#"&'73265#"&732654&#"؜eN+N[`HvLKCHIFENdwSM)'x}"1}R@LFEJJ+@ @  /329/]103#5#'##538`tih+ C+@ @  /329/]10#'53#'##53`~ihy +#@ @/329/]]99104#"'632##52#'##53T;3(:CC6=Kih#@`,8N?+ /@   @ H  /3]22/33+q2210"'&#"#>3232673#573#3TU$--[ +L?7SS"0+\ *KЛi&%#(RH(%&&%QJ'pC 9+D@( P` @ P `  @  H /+]qr29/r9]103#5"&'332673#݂YkjTThk+ tD78Cx9+D@( P` @ P `  @  H /+]qr29/r9]10#'53"&'332673\ kjTThk tD78Cx9+ X@&@H @P` HH H/+++qr29+9/99910"&'3326734#"'632+52kjTThkc;3(:CJI#KtD78Cx#@`047?+ $K@  P`p@*/Ho@/2/]]+]qr339/33/3310"&'332737".#"#>3232673fUU#DA=Q[ *K@$F@;0+\ *Lj]SSdc#'##53YL=5FYBI[*5ih,Pp0E &^F !5@@H@ H@ @ H /322/+33++22103#'##5"'&#"#>3232673fhi3TU$--[ +L?7SS"0+\ *Kh&%#(RH(%&&%QJ'M /222/10"&'33273573uuIM  /222/10"&'33273%53uuIM+ @  /222/29910"&'332734#"'632#5>uuR]HFZjQ[pE07c;U (nM #)@@ H@ /2]22/33+2210"&'33273"'&#"#>3232673uu 3TU$--[ +L?7SS"0+\ *K|&%#(RH(%&&%QJ'6+/9910'53E6%!6+/9910573E6!%k& &k& '& J'& +P&P&P&F B&9`#0##]]5&i #/;?3@-3'99? !!?>''>>>/3/3/3/2/93/99014632#"&74632#"&4632#"&%4632#"&4632#"&3&. ".." .. ".." .r- #--# -2. ".." .. 0/!",n` .. ".." .. "..".." 00".." ..".0 0..o!@ /3/33/2/99015!%4632#"&4632#"&3G"-# // #-- #--# -nmm@".." .. 00 "...o\d#4@ #"@ "H"/3/+933/2/99015#5!#%4632#"&4632#"&3nGj-# // #-- #//# -nrmmr".." .. 00 "...oV04.'32> #".54>75$&`1ELIf<#IoMQpEr/Z3O1@{sr}BI`.UsVQr[L`76_`:anHDspa!:wTSV H$7<;DGNVW]h$$7h$9h$:$?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ 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 { | } ~                          uni00A0uni00AD overscoremu1middotAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5uni01E6uni01E7uni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236 j.dotlessuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Funi0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295uni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BAuni02BBuni02BCuni02BDuni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DEuni02DFuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F3uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0342uni0343uni0344uni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034Funi0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362uni0363uni0364uni0365uni0366uni0367uni0368uni0369uni036Auni036Buni036Cuni036Duni036Euni036Funi0374uni0375uni037Auni037Buni037Cuni037Duni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammaEpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdanuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0uni03D1uni03D2uni03D3uni03D4uni03D5uni03D6uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03E2uni03E3uni03E4uni03E5uni03E6uni03E7uni03E8uni03E9uni03EAuni03EBuni03ECuni03EDuni03EEuni03EFuni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0487uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8 afii10846uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni051Auni051Buni051Cuni051Duni0591uni0592uni0593uni0594uni0595uni0596uni0597uni0598uni0599uni059Auni059Buni059Cuni059Duni059Euni059Funi05A0uni05A1uni05A2uni05A3uni05A4uni05A5uni05A6uni05A7uni05A8uni05A9uni05AAuni05ABuni05ACuni05ADuni05AEuni05AFsheva hatafsegol hatafpatah hatafqamatshiriqtseresegolpatahqamatsholamuni05BAqubutsdageshmetegmaqafrafepaseqshindotsindotsofpasuq upper_dotlowerdotuni05C6 qamatsqatanalefbetgimeldalethevavzayinhettetyodfinalkafkaflamedfinalmemmemfinalnunnunsamekhayinfinalpepe finaltsaditsadiqofreshshintavvavvavvavyodyodyodgeresh gershayimuni1D00uni1D01uni1D02uni1D03uni1D04uni1D05uni1D06uni1D07uni1D08uni1D09uni1D0Auni1D0Buni1D0Cuni1D0Duni1D0Euni1D0Funi1D10uni1D11uni1D12uni1D13uni1D14uni1D15uni1D16uni1D17uni1D18uni1D19uni1D1Auni1D1Buni1D1Cuni1D1Duni1D1Euni1D1Funi1D20uni1D21uni1D22uni1D23uni1D24uni1D25uni1D26uni1D27uni1D28uni1D29uni1D2Auni1D2Buni1D2Cuni1D2Duni1D2Euni1D2Funi1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Duni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D5Cuni1D5Duni1D5Euni1D5Funi1D60uni1D61uni1D62uni1D63uni1D64uni1D65uni1D66uni1D67uni1D68uni1D69uni1D6Auni1D6Buni1D6Cuni1D6Duni1D6Euni1D6Funi1D70uni1D71uni1D72uni1D73uni1D74uni1D75uni1D76uni1D77uni1D78uni1D79uni1D7Auni1D7Buni1D7Cuni1D7Duni1D7Euni1D7Funi1D80uni1D81uni1D82uni1D83uni1D84uni1D85uni1D86uni1D87uni1D88uni1D89uni1D8Auni1D8Buni1D8Cuni1D8Duni1D8Euni1D8Funi1D90uni1D91uni1D92uni1D93uni1D94uni1D95uni1D96uni1D97uni1D98uni1D99uni1D9Auni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB8uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1DC0uni1DC1uni1DC2uni1DC3uni1DC4uni1DC5uni1DC6uni1DC7uni1DC8uni1DC9uni1DCAuni1DFEuni1DFFuni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1E9E Adotbelow adotbelow Ahookabove ahookaboveAcircumflexacuteacircumflexacuteAcircumflexgraveacircumflexgraveAcircumflexhookaboveacircumflexhookaboveAcircumflextildeacircumflextildeAcircumflexdotbelowacircumflexdotbelow Abreveacute abreveacute Abrevegrave abrevegraveAbrevehookaboveabrevehookabove Abrevetilde abrevetildeAbrevedotbelowabrevedotbelow Edotbelow edotbelow Ehookabove ehookaboveEtildeetildeEcircumflexacuteecircumflexacuteEcircumflexgraveecircumflexgraveEcircumflexhookaboveecircumflexhookaboveEcircumflextildeecircumflextildeEcircumflexdotbelowecircumflexdotbelow Ihookabove ihookabove Idotbelow idotbelow Odotbelow odotbelow Ohookabove ohookaboveOcircumflexacuteocircumflexacuteOcircumflexgraveocircumflexgraveOcircumflexhookaboveocircumflexhookaboveOcircumflextildeocircumflextildeOcircumflexdotbelowocircumflexdotbelow Ohornacute ohornacute Ohorngrave ohorngraveOhornhookaboveohornhookabove Ohorntilde ohorntilde Ohorndotbelow ohorndotbelow Udotbelow udotbelow Uhookabove uhookabove Uhornacute uhornacute Uhorngrave uhorngraveUhornhookaboveuhornhookabove Uhorntilde uhorntilde Uhorndotbelow uhorndotbelowYgraveygrave Ydotbelow ydotbelow Yhookabove yhookaboveYtildeytildeuni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200Buni200Cuni200Duni200Euni200Funi2012uni2015uni2016 underscoredbl quotereverseduni201Funi202Auni202Buni202Cuni202Duni202Euni202Fminuteseconduni2034 exclamdbl radicalexuni205Euni206Auni206Buni206Cuni206Duni206Euni206F foursuperior fivesuperior sevensuperior eightsuperior nsuperioruni2090uni2091uni2092uni2093uni2094uni20A0uni20A1uni20A2lirauni20A5uni20A6pesetauni20A8uni20A9sheqeldongEurouni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20F0uni2105uni2113uni2116uni2117Ohm estimateduni214Duni214Eonethird twothirds oneeighth threeeighths fiveeighths seveneighthsuni2184 arrowleftarrowup arrowright arrowdown arrowboth arrowupdn arrowupdnbseuni2206uni2215 orthogonal intersection equivalencehouse revlogicalnot integraltp integralbtuni2500uni2502uni250Cuni2510uni2514uni2518uni251Cuni2524uni252Cuni2534uni253Cuni2550uni2551uni2552uni2553uni2554uni2555uni2556uni2557uni2558uni2559uni255Auni255Buni255Cuni255Duni255Euni255Funi2560uni2561uni2562uni2563uni2564uni2565uni2566uni2567uni2568uni2569uni256Auni256Buni256Cupblockdnblockblocklfblockrtblockltshadeshadedkshade filledboxuni25A1uni25AAuni25AB filledrecttriaguptriagrttriagdntriaglfcircleuni25CCuni25CF invbullet invcircle openbullet smileface invsmilefacesunfemalemalespadeclubheartdiamond musicalnotemusicalnotedbluni266Funi2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C6Duni2C71uni2C72uni2C73uni2C74uni2C75uni2C76uni2C77uni2E17uniA717uniA718uniA719uniA71AuniA71BuniA71CuniA71DuniA71EuniA71FuniA720uniA721uniA788uniA789uniA78AuniA78BuniA78CuniFB01uniFB02uniFB1DuniFB1E yodyod_patahalternativeayinalefwide daletwidehewidekafwide lamedwide finalmemwidereshwidetavwide alt_plussign shinshindot shinsindotshindageshshindotshindageshsindot alefpatah alefqamats alefmapiq betdagesh gimeldagesh daletdageshhedagesh vavdagesh zayindagesh tetdagesh yoddageshfinalkafdagesh kafdagesh lameddagesh memdagesh nundagesh samekhdagesh finalpedageshpedagesh tsadidagesh qofdagesh reshdagesh shindagesh tavdageshvavholambetrafekafrafeperafe aleflameduniFE20uniFE21uniFE22uniFE23uniFFFC commaaccent breve.cyrcaroncommaaccentcommaaccentrotategrave.ucacute.uc circumflex.uccaron.uc dieresis.uctilde.uchungarumlaut.ucbreve.uc grave.alt1 grave.alt2 grave.alt3 acute.alt1 acute.alt2 acute.alt3hookabove.alt1hookabove.alt2hookabove.alt3 tilde.alt1 tilde.alt2 breve.alt1circumflex.alt1 dotbelow.alt1 acute.alt4 acute.alt5 grave.alt4 grave.alt5hookabove.alt4hookabove.alt5 tilde.alt3 tilde.alt4 tilde.alt5 tilde.alt6 tilde.alt7 tilde.alt8 dotbelow.alt2 dotbelow.alt3 dotbelow.alt4 dotbelow.alt5 dotbelow.alt6 tilde.alt9 dotbelow.alt7 dotbelow.alt8 dotbelow.alt9dotbelow.alt10dotbelow.alt11dotbelow.alt12dotbelow.alt13dotbelow.alt14dotbelow.alt15 tilde.alt10 tilde.alt11 tilde.alt12 tilde.alt13 dotlessi.alt1uni03080304.capuni03080301.capuni0308030C.capuni03080300.capuni03070304.capuni03030304.capuni02E502E502E6uni02E502E502E7uni02E502E502E8uni02E502E502E9uni02E502E602E5uni02E502E602E6uni02E502E602E7uni02E502E602E8uni02E502E602E9 uni02E502E6uni02E502E702E5uni02E502E702E6uni02E502E702E7uni02E502E702E8uni02E502E702E9 uni02E502E7uni02E502E802E5uni02E502E802E6uni02E502E802E7uni02E502E802E8uni02E502E802E9 uni02E502E8uni02E502E902E5uni02E502E902E6uni02E502E902E7uni02E502E902E8uni02E502E902E9 uni02E502E9uni02E602E502E5uni02E602E502E6uni02E602E502E7uni02E602E502E8uni02E602E502E9 uni02E602E5uni02E602E602E5uni02E602E602E7uni02E602E602E8uni02E602E602E9uni02E602E702E5uni02E602E702E6uni02E602E702E7uni02E602E702E8uni02E602E702E9 uni02E602E7uni02E602E802E5uni02E602E802E6uni02E602E802E7uni02E602E802E8uni02E602E802E9 uni02E602E8uni02E602E902E5uni02E602E902E6uni02E602E902E7uni02E602E902E8uni02E602E902E9 uni02E602E9uni02E702E502E5uni02E702E502E6uni02E702E502E7uni02E702E502E8uni02E702E502E9 uni02E702E5uni02E702E602E5uni02E702E602E6uni02E702E602E7uni02E702E602E8uni02E702E602E9 uni02E702E6uni02E702E702E5uni02E702E702E6uni02E702E702E8uni02E702E702E9uni02E702E802E5uni02E702E802E6uni02E702E802E7uni02E702E802E8uni02E702E802E9 uni02E702E8uni02E702E902E5uni02E702E902E6uni02E702E902E7uni02E702E902E8uni02E702E902E9 uni02E702E9uni02E802E502E5uni02E802E502E6uni02E802E502E7uni02E802E502E8uni02E802E502E9 uni02E802E5uni02E802E602E5uni02E802E602E6uni02E802E602E7uni02E802E602E8uni02E802E602E9 uni02E802E6uni02E802E702E5uni02E802E702E6uni02E802E702E7uni02E802E702E8uni02E802E702E9 uni02E802E7uni02E802E802E5uni02E802E802E6uni02E802E802E7uni02E802E802E9uni02E802E902E5uni02E802E902E6uni02E802E902E7uni02E802E902E8uni02E802E902E9 uni02E802E9uni02E902E502E5uni02E902E502E6uni02E902E502E7uni02E902E502E8uni02E902E502E9 uni02E902E5uni02E902E602E5uni02E902E602E6uni02E902E602E7uni02E902E602E8uni02E902E602E9 uni02E902E6uni02E902E702E5uni02E902E702E6uni02E902E702E7uni02E902E702E8uni02E902E702E9 uni02E902E7uni02E902E802E5uni02E902E802E6uni02E902E802E7uni02E902E802E8uni02E902E802E9 uni02E902E8uni02E902E902E5uni02E902E902E6uni02E902E902E7uni02E902E902E8cyrillic_otmarkuni03040300.capuni03040301.capuni03030301.capuni03030308.capuni03010307.capuni030C0307.capuni03040308.cap bari.dotlessuni03B1030403130300uni03B1030403130301uni03B1030403140300uni03B1030403140301uni03B1030603130300uni03B1030603130301uni03B1030603140300uni03B1030603140301uni03B9030403130300uni03B9030403130301uni03B9030403140300uni03B9030403140301uni03B9030603130300uni03B9030603130301uni03B9030603140300uni03B9030603140301uni03C5030403130300uni03C5030403130301uni03C5030403140300uni03C5030403140301uni03C5030603130300uni03C5030603130301uni03C5030603140300uni03C5030603140301uni03B9030803040300uni03B9030803040301uni03B9030803060300uni03B9030803060301uni03C5030803040300uni03C5030803040301uni03C5030803060300uni03C5030803060301 dottediacuteEng.alt1Eng.alt2Eng.alt3zero.altone.alttwo.alt three.altfour.altfive.altsix.alt seven.alt eight.altnine.altcircumflexacutecircumflexgravecircumflexhookcircumflextilde breveacute brevegrave brevehook brevetildecircumflexacute.lccircumflexgrave.lccircumflexhook.lccircumflextilde.lc breveacute.lc brevegrave.lc brevehook.lc brevetilde.lc uni1FEF.short tonos.shortlamedholamdagesh lamedholamfinalkafqamats finalkafshevaaleflamedhatafsegolaleflamedsegolaleflamedtserealternativelamed alefdagesh uni05B105BD uni05B205BD uni05B305BDS_BEx          bcyrlgrek2hebr>latnLMKD SRB ccmpccmp dlig(locl.&.604D@8 8BLV`jt~ (2<FP           (06< ", j"4FX:BJRZbjrz S R Q P O M L K J I G F E D C A @ ? > = < ; : 9 N T B H:BJRZbjrz o n m l k i h g f e c b a ` _ ^ ] \ [ Y X W V U p d Z j:BJRZbjrz             ~ } { z y x w u t s r q   v |:BJRZbjrz                            :BJRZbjrz                            J",6@   bcyrlgrek2hebr>latnLMKD SRB kernkernmark  6>FNV^fn|&.6>     jx|8  #j&&&'8''(F( () )<)l)))*,*\***++,+Z+~;;>pB0ElI,I4IP1lll~.6llL.llll....llll..lll    ))     ))          ,             ))     -              1          n L $*06<BHNTZ`flrx~ &,28>DJPV\<<<<<<<~<<<<<<<<PPd<<<~<<<<<<<<<<~<<<<< (  H" X &,28>DJPV\bhntz x  XX&|vll  L28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntzFFhFhFXFXFFFFFFFXFXF F F@F@FXFXXFXXFXFF0F0FFFFFFXFXFFFXFXFXFXFFFFFFFFFFFhFhFXFXFFFFFFXFX F @F@FXFXXFXF0F0FFF00XFXXFXFF (  HFLRX^djpv|FFFFFFFFFFFFFn. L28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz00>>,,hhXX00\l4XX>04hh00>0,,ThhXXXX\l400XXTT (  H   jpv|-22<2  !<  !J`    J`      J`     !   J`     ! 4<4J4<  !4J  !Jl      Jb    Jl        !Jb      !n L $*06<BHNTZ`flrx~ &,28>DJPV\FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFdFFFFLFLFFF (  HFFFn L $*06<BHNTZ`flrx~ &,28>DJPV\8FFFFFhFFFFhF F FpFFFhFFFFFFFFF\FFLF F F FF F8FFF F FF\FLFFFFF8F8F8FFFFFhFFFhF F F FFFFFFFF\FFLFFF FF F F F F (  H FFn L $*06<BHNTZ`flrx~ &,28>DJPV\$HD88\ X88$888888$$$HD$ X8$H (  H ( >       $*    $*    $*    $*    $*    $*    "    $*$*$*$*$*$*$*$*$*$*$*$*$*"("(pi 2@^p4r|&Tv.8,:dR>TZ`6  . < b 0 H j  @ R  , > l .p(FLZ $7<;DGNVW]h 7h9h:DJPV\`88ppp8LL8\Ppppp`pp\PP```pppLL8\PLpppp (  H n L $*06<BHNTZ`flrx~ &,28>DJPV\&0,ThXXXlhhhXh000,ThXXl0XTT (  H   flrx~888888888n L $*06<BHNTZ`flrx~ &,28>DJPV\FFhFFXFFFFFFFFFF FF@FFXFXFXFFF0FFFFFFFXFFF0FFFFFFFF FF FFFFFhFFXFFFFFFFF F@FFXFXFF0FFFFFhF,FFFFFF (  H:@FLRX^djpv|FF,FFF,FhF,FFFFFFnn L $*06<BHNTZ`flrx~ &,28>DJPV\0h,|hXX,XX0h,|hXXXh0X (  H   flrx~h@,,x|,|@p 8=@̑sambox-1.1.19/src/main/resources/org/sejda/sambox/resources/version.properties000066400000000000000000000014601320103431700275770ustar00rootroot00000000000000# 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. sambox.version=${project.version}